| Index: third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
|
| diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
|
| index 721ec23998fabdf961947d9e22d362a9c58edf99..c7b469e6f7050ebbf58e418fcb431880c41c6df2 100644
|
| --- a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
|
| +++ b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
|
| @@ -6,13 +6,21 @@
|
|
|
| #include "bindings/core/v8/Dictionary.h"
|
| #include "bindings/core/v8/ExceptionState.h"
|
| +#include "core/animation/Animation.h"
|
| +#include "core/animation/CompositorAnimations.h"
|
| #include "core/animation/EffectInput.h"
|
| +#include "core/animation/ElementAnimations.h"
|
| +#include "core/animation/Interpolation.h"
|
| #include "core/animation/KeyframeEffect.h"
|
| #include "core/animation/KeyframeEffectOptions.h"
|
| +#include "core/animation/PropertyHandle.h"
|
| #include "core/animation/SampledEffect.h"
|
| #include "core/animation/TimingInput.h"
|
| #include "core/dom/Element.h"
|
| +#include "core/dom/NodeComputedStyle.h"
|
| #include "core/frame/UseCounter.h"
|
| +#include "core/paint/PaintLayer.h"
|
| +#include "core/svg/SVGElement.h"
|
|
|
| namespace blink {
|
|
|
| @@ -60,6 +68,273 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(Element* target,
|
| m_sampledEffect(nullptr),
|
| m_priority(priority) {}
|
|
|
| +void KeyframeEffectReadOnly::attach(Animation* animation) {
|
| + if (m_target) {
|
| + m_target->ensureElementAnimations().animations().add(animation);
|
| + m_target->setNeedsAnimationStyleRecalc();
|
| + if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() &&
|
| + m_target->isSVGElement())
|
| + toSVGElement(m_target)->setWebAnimationsPending();
|
| + }
|
| + AnimationEffectReadOnly::attach(animation);
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::detach() {
|
| + if (m_target)
|
| + m_target->elementAnimations()->animations().remove(animation());
|
| + if (m_sampledEffect)
|
| + clearEffects();
|
| + AnimationEffectReadOnly::detach();
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::specifiedTimingChanged() {
|
| + if (animation()) {
|
| + // FIXME: Needs to consider groups when added.
|
| + DCHECK_EQ(animation()->effect(), this);
|
| + animation()->setCompositorPending(true);
|
| + }
|
| +}
|
| +
|
| +static AnimationStack& ensureAnimationStack(Element* element) {
|
| + return element->ensureElementAnimations().animationStack();
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::hasMultipleTransformProperties() const {
|
| + if (!m_target->computedStyle())
|
| + return false;
|
| +
|
| + unsigned transformPropertyCount = 0;
|
| + if (m_target->computedStyle()->hasTransformOperations())
|
| + transformPropertyCount++;
|
| + if (m_target->computedStyle()->rotate())
|
| + transformPropertyCount++;
|
| + if (m_target->computedStyle()->scale())
|
| + transformPropertyCount++;
|
| + if (m_target->computedStyle()->translate())
|
| + transformPropertyCount++;
|
| + return transformPropertyCount > 1;
|
| +}
|
| +
|
| +// Returns true if transform, translate, rotate or scale is composited
|
| +// and a motion path or other transform properties
|
| +// has been introduced on the element
|
| +bool KeyframeEffectReadOnly::hasIncompatibleStyle() {
|
| + if (!m_target->computedStyle())
|
| + return false;
|
| +
|
| + bool affectsTransform =
|
| + animation()->affects(*m_target, CSSPropertyTransform) ||
|
| + animation()->affects(*m_target, CSSPropertyScale) ||
|
| + animation()->affects(*m_target, CSSPropertyRotate) ||
|
| + animation()->affects(*m_target, CSSPropertyTranslate);
|
| +
|
| + if (animation()->hasActiveAnimationsOnCompositor()) {
|
| + if (m_target->computedStyle()->hasOffset() && affectsTransform)
|
| + return true;
|
| + return hasMultipleTransformProperties();
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::applyEffects() {
|
| + DCHECK(isInEffect());
|
| + DCHECK(animation());
|
| + if (!m_target || !m_model)
|
| + return;
|
| +
|
| + if (hasIncompatibleStyle())
|
| + animation()->cancelAnimationOnCompositor();
|
| +
|
| + double iteration = currentIteration();
|
| + DCHECK_GE(iteration, 0);
|
| + bool changed = false;
|
| + if (m_sampledEffect) {
|
| + changed = m_model->sample(clampTo<int>(iteration, 0), progress(),
|
| + iterationDuration(),
|
| + m_sampledEffect->mutableInterpolations());
|
| + } else {
|
| + Vector<RefPtr<Interpolation>> interpolations;
|
| + m_model->sample(clampTo<int>(iteration, 0), progress(), iterationDuration(),
|
| + interpolations);
|
| + if (!interpolations.isEmpty()) {
|
| + SampledEffect* sampledEffect = SampledEffect::create(this);
|
| + sampledEffect->mutableInterpolations().swap(interpolations);
|
| + m_sampledEffect = sampledEffect;
|
| + ensureAnimationStack(m_target).add(sampledEffect);
|
| + changed = true;
|
| + } else {
|
| + return;
|
| + }
|
| + }
|
| +
|
| + if (changed) {
|
| + m_target->setNeedsAnimationStyleRecalc();
|
| + if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() &&
|
| + m_target->isSVGElement())
|
| + toSVGElement(*m_target).setWebAnimationsPending();
|
| + }
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::clearEffects() {
|
| + DCHECK(animation());
|
| + DCHECK(m_sampledEffect);
|
| +
|
| + m_sampledEffect->clear();
|
| + m_sampledEffect = nullptr;
|
| + restartAnimationOnCompositor();
|
| + m_target->setNeedsAnimationStyleRecalc();
|
| + if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() &&
|
| + m_target->isSVGElement())
|
| + toSVGElement(*m_target).clearWebAnimatedAttributes();
|
| + invalidate();
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::updateChildrenAndEffects() const {
|
| + if (!m_model)
|
| + return;
|
| + DCHECK(animation());
|
| + if (isInEffect() && !animation()->effectSuppressed())
|
| + const_cast<KeyframeEffectReadOnly*>(this)->applyEffects();
|
| + else if (m_sampledEffect)
|
| + const_cast<KeyframeEffectReadOnly*>(this)->clearEffects();
|
| +}
|
| +
|
| +double KeyframeEffectReadOnly::calculateTimeToEffectChange(
|
| + bool forwards,
|
| + double localTime,
|
| + double timeToNextIteration) const {
|
| + const double startTime = specifiedTiming().startDelay;
|
| + const double endTimeMinusEndDelay = startTime + activeDurationInternal();
|
| + const double endTime = endTimeMinusEndDelay + specifiedTiming().endDelay;
|
| + const double afterTime = std::min(endTimeMinusEndDelay, endTime);
|
| +
|
| + switch (getPhase()) {
|
| + case PhaseNone:
|
| + return std::numeric_limits<double>::infinity();
|
| + case PhaseBefore:
|
| + DCHECK_GE(startTime, localTime);
|
| + return forwards ? startTime - localTime
|
| + : std::numeric_limits<double>::infinity();
|
| + case PhaseActive:
|
| + if (forwards) {
|
| + // Need service to apply fill / fire events.
|
| + const double timeToEnd = afterTime - localTime;
|
| + if (requiresIterationEvents()) {
|
| + return std::min(timeToEnd, timeToNextIteration);
|
| + }
|
| + return timeToEnd;
|
| + }
|
| + return 0;
|
| + case PhaseAfter:
|
| + DCHECK_GE(localTime, afterTime);
|
| + // If this KeyframeEffect is still in effect then it will need to update
|
| + // when its parent goes out of effect. We have no way of knowing when
|
| + // that will be, however, so the parent will need to supply it.
|
| + return forwards ? std::numeric_limits<double>::infinity()
|
| + : localTime - afterTime;
|
| + default:
|
| + NOTREACHED();
|
| + return std::numeric_limits<double>::infinity();
|
| + }
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::notifySampledEffectRemovedFromAnimationStack() {
|
| + m_sampledEffect = nullptr;
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::isCandidateForAnimationOnCompositor(
|
| + double animationPlaybackRate) const {
|
| + // Do not put transforms on compositor if more than one of them are defined
|
| + // in computed style because they need to be explicitly ordered
|
| + if (!model() || !m_target ||
|
| + (m_target->computedStyle() && m_target->computedStyle()->hasOffset()) ||
|
| + hasMultipleTransformProperties())
|
| + return false;
|
| +
|
| + return CompositorAnimations::isCandidateForAnimationOnCompositor(
|
| + specifiedTiming(), *m_target, animation(), *model(),
|
| + animationPlaybackRate);
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::maybeStartAnimationOnCompositor(
|
| + int group,
|
| + double startTime,
|
| + double currentTime,
|
| + double animationPlaybackRate) {
|
| + DCHECK(!hasActiveAnimationsOnCompositor());
|
| + if (!isCandidateForAnimationOnCompositor(animationPlaybackRate))
|
| + return false;
|
| + if (!CompositorAnimations::canStartAnimationOnCompositor(*m_target))
|
| + return false;
|
| + CompositorAnimations::startAnimationOnCompositor(
|
| + *m_target, group, startTime, currentTime, specifiedTiming(), *animation(),
|
| + *model(), m_compositorAnimationIds, animationPlaybackRate);
|
| + DCHECK(!m_compositorAnimationIds.isEmpty());
|
| + return true;
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::hasActiveAnimationsOnCompositor() const {
|
| + return !m_compositorAnimationIds.isEmpty();
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::hasActiveAnimationsOnCompositor(
|
| + CSSPropertyID property) const {
|
| + return hasActiveAnimationsOnCompositor() && affects(PropertyHandle(property));
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::affects(PropertyHandle property) const {
|
| + return m_model && m_model->affects(property);
|
| +}
|
| +
|
| +bool KeyframeEffectReadOnly::cancelAnimationOnCompositor() {
|
| + // FIXME: cancelAnimationOnCompositor is called from withins style recalc.
|
| + // This queries compositingState, which is not necessarily up to date.
|
| + // https://code.google.com/p/chromium/issues/detail?id=339847
|
| + DisableCompositingQueryAsserts disabler;
|
| + if (!hasActiveAnimationsOnCompositor())
|
| + return false;
|
| + if (!m_target || !m_target->layoutObject())
|
| + return false;
|
| + DCHECK(animation());
|
| + for (const auto& compositorAnimationId : m_compositorAnimationIds) {
|
| + CompositorAnimations::cancelAnimationOnCompositor(*m_target, *animation(),
|
| + compositorAnimationId);
|
| + }
|
| + m_compositorAnimationIds.clear();
|
| + return true;
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::restartAnimationOnCompositor() {
|
| + if (cancelAnimationOnCompositor())
|
| + animation()->setCompositorPending(true);
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::cancelIncompatibleAnimationsOnCompositor() {
|
| + if (m_target && animation() && model()) {
|
| + CompositorAnimations::cancelIncompatibleAnimationsOnCompositor(
|
| + *m_target, *animation(), *model());
|
| + }
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::pauseAnimationForTestingOnCompositor(
|
| + double pauseTime) {
|
| + DCHECK(hasActiveAnimationsOnCompositor());
|
| + if (!m_target || !m_target->layoutObject())
|
| + return;
|
| + DCHECK(animation());
|
| + for (const auto& compositorAnimationId : m_compositorAnimationIds) {
|
| + CompositorAnimations::pauseAnimationForTestingOnCompositor(
|
| + *m_target, *animation(), compositorAnimationId, pauseTime);
|
| + }
|
| +}
|
| +
|
| +void KeyframeEffectReadOnly::attachCompositedLayers() {
|
| + DCHECK(m_target);
|
| + DCHECK(animation());
|
| + CompositorAnimations::attachCompositedLayers(*m_target, *animation());
|
| +}
|
| +
|
| DEFINE_TRACE(KeyframeEffectReadOnly) {
|
| visitor->trace(m_target);
|
| visitor->trace(m_model);
|
|
|