| 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 14 matching lines...) Expand all Loading... |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "core/animation/KeyframeEffect.h" | 31 #include "core/animation/KeyframeEffect.h" |
| 32 | 32 |
| 33 #include "bindings/core/v8/Dictionary.h" | 33 #include "bindings/core/v8/Dictionary.h" |
| 34 #include "bindings/core/v8/ExceptionState.h" | 34 #include "bindings/core/v8/ExceptionState.h" |
| 35 #include "core/animation/Animation.h" | |
| 36 #include "core/animation/AnimationEffectTiming.h" | 35 #include "core/animation/AnimationEffectTiming.h" |
| 37 #include "core/animation/AnimationTimeline.h" | 36 #include "core/animation/EffectInput.h" |
| 38 #include "core/animation/CompositorAnimations.h" | |
| 39 #include "core/animation/ElementAnimations.h" | |
| 40 #include "core/animation/Interpolation.h" | |
| 41 #include "core/animation/KeyframeEffectModel.h" | |
| 42 #include "core/animation/KeyframeEffectOptions.h" | 37 #include "core/animation/KeyframeEffectOptions.h" |
| 43 #include "core/animation/KeyframeEffectReadOnly.h" | 38 #include "core/animation/KeyframeEffectReadOnly.h" |
| 44 #include "core/animation/PropertyHandle.h" | 39 #include "core/animation/TimingInput.h" |
| 45 #include "core/dom/Element.h" | 40 #include "core/dom/Element.h" |
| 46 #include "core/dom/NodeComputedStyle.h" | |
| 47 #include "core/frame/UseCounter.h" | 41 #include "core/frame/UseCounter.h" |
| 48 #include "core/paint/PaintLayer.h" | |
| 49 #include "core/svg/SVGElement.h" | |
| 50 | 42 |
| 51 namespace blink { | 43 namespace blink { |
| 52 | 44 |
| 53 KeyframeEffect* KeyframeEffect::create( | 45 KeyframeEffect* KeyframeEffect::create( |
| 54 Element* target, | 46 Element* target, |
| 55 EffectModel* model, | 47 EffectModel* model, |
| 56 const Timing& timing, | 48 const Timing& timing, |
| 57 KeyframeEffectReadOnly::Priority priority, | 49 KeyframeEffectReadOnly::Priority priority, |
| 58 EventDelegate* eventDelegate) { | 50 EventDelegate* eventDelegate) { |
| 59 return new KeyframeEffect(target, model, timing, priority, eventDelegate); | 51 return new KeyframeEffect(target, model, timing, priority, eventDelegate); |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 | 107 |
| 116 KeyframeEffect::KeyframeEffect(Element* target, | 108 KeyframeEffect::KeyframeEffect(Element* target, |
| 117 EffectModel* model, | 109 EffectModel* model, |
| 118 const Timing& timing, | 110 const Timing& timing, |
| 119 KeyframeEffectReadOnly::Priority priority, | 111 KeyframeEffectReadOnly::Priority priority, |
| 120 EventDelegate* eventDelegate) | 112 EventDelegate* eventDelegate) |
| 121 : KeyframeEffectReadOnly(target, model, timing, priority, eventDelegate) {} | 113 : KeyframeEffectReadOnly(target, model, timing, priority, eventDelegate) {} |
| 122 | 114 |
| 123 KeyframeEffect::~KeyframeEffect() {} | 115 KeyframeEffect::~KeyframeEffect() {} |
| 124 | 116 |
| 125 void KeyframeEffect::attach(Animation* animation) { | |
| 126 if (m_target) { | |
| 127 m_target->ensureElementAnimations().animations().add(animation); | |
| 128 m_target->setNeedsAnimationStyleRecalc(); | |
| 129 if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && | |
| 130 m_target->isSVGElement()) | |
| 131 toSVGElement(m_target)->setWebAnimationsPending(); | |
| 132 } | |
| 133 AnimationEffectReadOnly::attach(animation); | |
| 134 } | |
| 135 | |
| 136 void KeyframeEffect::detach() { | |
| 137 if (m_target) | |
| 138 m_target->elementAnimations()->animations().remove(animation()); | |
| 139 if (m_sampledEffect) | |
| 140 clearEffects(); | |
| 141 AnimationEffectReadOnly::detach(); | |
| 142 } | |
| 143 | |
| 144 void KeyframeEffect::specifiedTimingChanged() { | |
| 145 if (animation()) { | |
| 146 // FIXME: Needs to consider groups when added. | |
| 147 DCHECK_EQ(animation()->effect(), this); | |
| 148 animation()->setCompositorPending(true); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 static AnimationStack& ensureAnimationStack(Element* element) { | |
| 153 return element->ensureElementAnimations().animationStack(); | |
| 154 } | |
| 155 | |
| 156 bool KeyframeEffect::hasMultipleTransformProperties() const { | |
| 157 if (!m_target->computedStyle()) | |
| 158 return false; | |
| 159 | |
| 160 unsigned transformPropertyCount = 0; | |
| 161 if (m_target->computedStyle()->hasTransformOperations()) | |
| 162 transformPropertyCount++; | |
| 163 if (m_target->computedStyle()->rotate()) | |
| 164 transformPropertyCount++; | |
| 165 if (m_target->computedStyle()->scale()) | |
| 166 transformPropertyCount++; | |
| 167 if (m_target->computedStyle()->translate()) | |
| 168 transformPropertyCount++; | |
| 169 return transformPropertyCount > 1; | |
| 170 } | |
| 171 | |
| 172 // Returns true if transform, translate, rotate or scale is composited | |
| 173 // and a motion path or other transform properties | |
| 174 // has been introduced on the element | |
| 175 bool KeyframeEffect::hasIncompatibleStyle() { | |
| 176 if (!m_target->computedStyle()) | |
| 177 return false; | |
| 178 | |
| 179 bool affectsTransform = | |
| 180 animation()->affects(*m_target, CSSPropertyTransform) || | |
| 181 animation()->affects(*m_target, CSSPropertyScale) || | |
| 182 animation()->affects(*m_target, CSSPropertyRotate) || | |
| 183 animation()->affects(*m_target, CSSPropertyTranslate); | |
| 184 | |
| 185 if (animation()->hasActiveAnimationsOnCompositor()) { | |
| 186 if (m_target->computedStyle()->hasOffset() && affectsTransform) | |
| 187 return true; | |
| 188 return hasMultipleTransformProperties(); | |
| 189 } | |
| 190 | |
| 191 return false; | |
| 192 } | |
| 193 | |
| 194 void KeyframeEffect::applyEffects() { | |
| 195 DCHECK(isInEffect()); | |
| 196 DCHECK(animation()); | |
| 197 if (!m_target || !m_model) | |
| 198 return; | |
| 199 | |
| 200 if (hasIncompatibleStyle()) | |
| 201 animation()->cancelAnimationOnCompositor(); | |
| 202 | |
| 203 double iteration = currentIteration(); | |
| 204 DCHECK_GE(iteration, 0); | |
| 205 bool changed = false; | |
| 206 if (m_sampledEffect) { | |
| 207 changed = m_model->sample(clampTo<int>(iteration, 0), progress(), | |
| 208 iterationDuration(), | |
| 209 m_sampledEffect->mutableInterpolations()); | |
| 210 } else { | |
| 211 Vector<RefPtr<Interpolation>> interpolations; | |
| 212 m_model->sample(clampTo<int>(iteration, 0), progress(), iterationDuration(), | |
| 213 interpolations); | |
| 214 if (!interpolations.isEmpty()) { | |
| 215 SampledEffect* sampledEffect = SampledEffect::create(this); | |
| 216 sampledEffect->mutableInterpolations().swap(interpolations); | |
| 217 m_sampledEffect = sampledEffect; | |
| 218 ensureAnimationStack(m_target).add(sampledEffect); | |
| 219 changed = true; | |
| 220 } else { | |
| 221 return; | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 if (changed) { | |
| 226 m_target->setNeedsAnimationStyleRecalc(); | |
| 227 if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && | |
| 228 m_target->isSVGElement()) | |
| 229 toSVGElement(*m_target).setWebAnimationsPending(); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 void KeyframeEffect::clearEffects() { | |
| 234 DCHECK(animation()); | |
| 235 DCHECK(m_sampledEffect); | |
| 236 | |
| 237 m_sampledEffect->clear(); | |
| 238 m_sampledEffect = nullptr; | |
| 239 restartAnimationOnCompositor(); | |
| 240 m_target->setNeedsAnimationStyleRecalc(); | |
| 241 if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && | |
| 242 m_target->isSVGElement()) | |
| 243 toSVGElement(*m_target).clearWebAnimatedAttributes(); | |
| 244 invalidate(); | |
| 245 } | |
| 246 | |
| 247 void KeyframeEffect::updateChildrenAndEffects() const { | |
| 248 if (!m_model) | |
| 249 return; | |
| 250 DCHECK(animation()); | |
| 251 if (isInEffect() && !animation()->effectSuppressed()) | |
| 252 const_cast<KeyframeEffect*>(this)->applyEffects(); | |
| 253 else if (m_sampledEffect) | |
| 254 const_cast<KeyframeEffect*>(this)->clearEffects(); | |
| 255 } | |
| 256 | |
| 257 double KeyframeEffect::calculateTimeToEffectChange( | |
| 258 bool forwards, | |
| 259 double localTime, | |
| 260 double timeToNextIteration) const { | |
| 261 const double startTime = specifiedTiming().startDelay; | |
| 262 const double endTimeMinusEndDelay = startTime + activeDurationInternal(); | |
| 263 const double endTime = endTimeMinusEndDelay + specifiedTiming().endDelay; | |
| 264 const double afterTime = std::min(endTimeMinusEndDelay, endTime); | |
| 265 | |
| 266 switch (getPhase()) { | |
| 267 case PhaseNone: | |
| 268 return std::numeric_limits<double>::infinity(); | |
| 269 case PhaseBefore: | |
| 270 DCHECK_GE(startTime, localTime); | |
| 271 return forwards ? startTime - localTime | |
| 272 : std::numeric_limits<double>::infinity(); | |
| 273 case PhaseActive: | |
| 274 if (forwards) { | |
| 275 // Need service to apply fill / fire events. | |
| 276 const double timeToEnd = afterTime - localTime; | |
| 277 if (requiresIterationEvents()) { | |
| 278 return std::min(timeToEnd, timeToNextIteration); | |
| 279 } | |
| 280 return timeToEnd; | |
| 281 } | |
| 282 return 0; | |
| 283 case PhaseAfter: | |
| 284 DCHECK_GE(localTime, afterTime); | |
| 285 // If this KeyframeEffect is still in effect then it will need to update | |
| 286 // when its parent goes out of effect. We have no way of knowing when | |
| 287 // that will be, however, so the parent will need to supply it. | |
| 288 return forwards ? std::numeric_limits<double>::infinity() | |
| 289 : localTime - afterTime; | |
| 290 default: | |
| 291 NOTREACHED(); | |
| 292 return std::numeric_limits<double>::infinity(); | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 void KeyframeEffect::notifySampledEffectRemovedFromAnimationStack() { | |
| 297 m_sampledEffect = nullptr; | |
| 298 } | |
| 299 | |
| 300 bool KeyframeEffect::isCandidateForAnimationOnCompositor( | |
| 301 double animationPlaybackRate) const { | |
| 302 // Do not put transforms on compositor if more than one of them are defined | |
| 303 // in computed style because they need to be explicitly ordered | |
| 304 if (!model() || !m_target || | |
| 305 (m_target->computedStyle() && m_target->computedStyle()->hasOffset()) || | |
| 306 hasMultipleTransformProperties()) | |
| 307 return false; | |
| 308 | |
| 309 return CompositorAnimations::isCandidateForAnimationOnCompositor( | |
| 310 specifiedTiming(), *m_target, animation(), *model(), | |
| 311 animationPlaybackRate); | |
| 312 } | |
| 313 | |
| 314 bool KeyframeEffect::maybeStartAnimationOnCompositor( | |
| 315 int group, | |
| 316 double startTime, | |
| 317 double currentTime, | |
| 318 double animationPlaybackRate) { | |
| 319 DCHECK(!hasActiveAnimationsOnCompositor()); | |
| 320 if (!isCandidateForAnimationOnCompositor(animationPlaybackRate)) | |
| 321 return false; | |
| 322 if (!CompositorAnimations::canStartAnimationOnCompositor(*m_target)) | |
| 323 return false; | |
| 324 CompositorAnimations::startAnimationOnCompositor( | |
| 325 *m_target, group, startTime, currentTime, specifiedTiming(), *animation(), | |
| 326 *model(), m_compositorAnimationIds, animationPlaybackRate); | |
| 327 DCHECK(!m_compositorAnimationIds.isEmpty()); | |
| 328 return true; | |
| 329 } | |
| 330 | |
| 331 bool KeyframeEffect::hasActiveAnimationsOnCompositor() const { | |
| 332 return !m_compositorAnimationIds.isEmpty(); | |
| 333 } | |
| 334 | |
| 335 bool KeyframeEffect::hasActiveAnimationsOnCompositor( | |
| 336 CSSPropertyID property) const { | |
| 337 return hasActiveAnimationsOnCompositor() && affects(PropertyHandle(property)); | |
| 338 } | |
| 339 | |
| 340 bool KeyframeEffect::affects(PropertyHandle property) const { | |
| 341 return m_model && m_model->affects(property); | |
| 342 } | |
| 343 | |
| 344 bool KeyframeEffect::cancelAnimationOnCompositor() { | |
| 345 // FIXME: cancelAnimationOnCompositor is called from withins style recalc. | |
| 346 // This queries compositingState, which is not necessarily up to date. | |
| 347 // https://code.google.com/p/chromium/issues/detail?id=339847 | |
| 348 DisableCompositingQueryAsserts disabler; | |
| 349 if (!hasActiveAnimationsOnCompositor()) | |
| 350 return false; | |
| 351 if (!m_target || !m_target->layoutObject()) | |
| 352 return false; | |
| 353 DCHECK(animation()); | |
| 354 for (const auto& compositorAnimationId : m_compositorAnimationIds) | |
| 355 CompositorAnimations::cancelAnimationOnCompositor(*m_target, *animation(), | |
| 356 compositorAnimationId); | |
| 357 m_compositorAnimationIds.clear(); | |
| 358 return true; | |
| 359 } | |
| 360 | |
| 361 void KeyframeEffect::restartAnimationOnCompositor() { | |
| 362 if (cancelAnimationOnCompositor()) | |
| 363 animation()->setCompositorPending(true); | |
| 364 } | |
| 365 | |
| 366 void KeyframeEffect::cancelIncompatibleAnimationsOnCompositor() { | |
| 367 if (m_target && animation() && model()) | |
| 368 CompositorAnimations::cancelIncompatibleAnimationsOnCompositor( | |
| 369 *m_target, *animation(), *model()); | |
| 370 } | |
| 371 | |
| 372 void KeyframeEffect::pauseAnimationForTestingOnCompositor(double pauseTime) { | |
| 373 DCHECK(hasActiveAnimationsOnCompositor()); | |
| 374 if (!m_target || !m_target->layoutObject()) | |
| 375 return; | |
| 376 DCHECK(animation()); | |
| 377 for (const auto& compositorAnimationId : m_compositorAnimationIds) | |
| 378 CompositorAnimations::pauseAnimationForTestingOnCompositor( | |
| 379 *m_target, *animation(), compositorAnimationId, pauseTime); | |
| 380 } | |
| 381 | |
| 382 void KeyframeEffect::attachCompositedLayers() { | |
| 383 DCHECK(m_target); | |
| 384 DCHECK(animation()); | |
| 385 CompositorAnimations::attachCompositedLayers(*m_target, *animation()); | |
| 386 } | |
| 387 | |
| 388 AnimationEffectTiming* KeyframeEffect::timing() { | 117 AnimationEffectTiming* KeyframeEffect::timing() { |
| 389 return AnimationEffectTiming::create(this); | 118 return AnimationEffectTiming::create(this); |
| 390 } | 119 } |
| 391 | 120 |
| 392 DEFINE_TRACE(KeyframeEffect) { | |
| 393 KeyframeEffectReadOnly::trace(visitor); | |
| 394 } | |
| 395 | |
| 396 } // namespace blink | 121 } // namespace blink |
| OLD | NEW |