#include "config.h"
#include "core/animation/css/CSSAnimations.h"
-#include "StylePropertyShorthand.h"
+#include "core/StylePropertyShorthand.h"
#include "core/animation/ActiveAnimations.h"
+#include "core/animation/AnimationTimeline.h"
#include "core/animation/CompositorAnimations.h"
-#include "core/animation/DocumentTimeline.h"
#include "core/animation/KeyframeEffectModel.h"
+#include "core/animation/LegacyStyleInterpolation.h"
#include "core/animation/css/CSSAnimatableValueFactory.h"
-#include "core/animation/css/CSSAnimationDataList.h"
-#include "core/animation/css/CSSPropertyAnimation.h"
+#include "core/animation/css/CSSPropertyEquality.h"
#include "core/css/CSSKeyframeRule.h"
+#include "core/css/CSSPropertyMetadata.h"
+#include "core/css/resolver/CSSToStyleMap.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/Element.h"
#include "core/dom/PseudoElement.h"
-#include "core/events/ThreadLocalEventNames.h"
+#include "core/dom/StyleEngine.h"
#include "core/events/TransitionEvent.h"
#include "core/events/WebKitAnimationEvent.h"
#include "core/frame/UseCounter.h"
#include "wtf/BitArray.h"
#include "wtf/HashSet.h"
-namespace WebCore {
+namespace blink {
namespace {
-bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference)
+CSSPropertyID propertyForAnimation(CSSPropertyID property)
{
- ASSERT(target != TimedItem::PhaseNone);
- ASSERT(reference != TimedItem::PhaseNone);
- return target < reference;
-}
-
-bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference)
-{
- ASSERT(target != TimedItem::PhaseNone);
- ASSERT(reference != TimedItem::PhaseNone);
- return target > reference;
-}
-
-static PassRefPtr<TimingFunction> generateTimingFunction(const KeyframeEffectModel::KeyframeVector keyframes, const HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions)
-{
- // Generate the chained timing function. Note that timing functions apply
- // from the keyframe in which they're specified to the next keyframe.
- bool isTimingFunctionLinearThroughout = true;
- RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction::create();
- for (size_t i = 0; i < keyframes.size() - 1; ++i) {
- double lowerBound = keyframes[i]->offset();
- ASSERT(lowerBound >=0 && lowerBound < 1);
- double upperBound = keyframes[i + 1]->offset();
- ASSERT(upperBound > 0 && upperBound <= 1);
- TimingFunction* timingFunction = perKeyframeTimingFunctions.get(lowerBound);
- isTimingFunctionLinearThroughout &= timingFunction->type() == TimingFunction::LinearFunction;
- chainedTimingFunction->appendSegment(upperBound, timingFunction);
- }
- if (isTimingFunctionLinearThroughout)
- return LinearTimingFunction::create();
- return chainedTimingFunction;
+ switch (property) {
+ case CSSPropertyWebkitPerspective:
+ return CSSPropertyPerspective;
+ case CSSPropertyWebkitTransform:
+ return CSSPropertyTransform;
+ case CSSPropertyWebkitPerspectiveOriginX:
+ case CSSPropertyWebkitPerspectiveOriginY:
+ return CSSPropertyPerspectiveOrigin;
+ case CSSPropertyWebkitTransformOriginX:
+ case CSSPropertyWebkitTransformOriginY:
+ case CSSPropertyWebkitTransformOriginZ:
+ return CSSPropertyTransformOrigin;
+ default:
+ break;
+ }
+ return property;
}
static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
- Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions)
+ AnimatableValueKeyframeVector& keyframes)
{
// When the element is null, use its parent for scoping purposes.
const Element* elementForScoping = element ? element : &parentElement;
if (!keyframesRule)
return;
- const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
+ const WillBeHeapVector<RefPtrWillBeMember<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
if (styleKeyframes.isEmpty())
return;
// Construct and populate the style for each keyframe
- PropertySet specifiedProperties;
- KeyframeEffectModel::KeyframeVector keyframes;
- HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions;
+ PropertySet specifiedPropertiesForUseCounter;
for (size_t i = 0; i < styleKeyframes.size(); ++i) {
const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
// It's OK to pass a null element here.
RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name);
- RefPtr<Keyframe> keyframe = Keyframe::create();
+ RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKeyframe::create();
const Vector<double>& offsets = styleKeyframe->keys();
ASSERT(!offsets.isEmpty());
keyframe->setOffset(offsets[0]);
- TimingFunction* timingFunction = defaultTimingFunction;
- const StylePropertySet* properties = styleKeyframe->properties();
- for (unsigned j = 0; j < properties->propertyCount(); j++) {
- CSSPropertyID property = properties->propertyAt(j).id();
- specifiedProperties.add(property);
- if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction)
- timingFunction = KeyframeValue::timingFunction(*keyframeStyle);
- else if (CSSAnimations::isAnimatableProperty(property))
+ keyframe->setEasing(defaultTimingFunction);
+ const StylePropertySet& properties = styleKeyframe->properties();
+ for (unsigned j = 0; j < properties.propertyCount(); j++) {
+ specifiedPropertiesForUseCounter.add(properties.propertyAt(j).id());
+ CSSPropertyID property = propertyForAnimation(properties.propertyAt(j).id());
+ if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) {
+ CSSValue* value = properties.propertyAt(j).value();
+ RefPtr<TimingFunction> timingFunction;
+ if (value->isInheritedValue() && parentStyle->animations())
+ timingFunction = parentStyle->animations()->timingFunctionList()[0];
+ else if (value->isInheritedValue() || value->isInitialValue())
+ timingFunction = CSSTimingData::initialTimingFunction();
+ else
+ timingFunction = CSSToStyleMap::mapAnimationTimingFunction(toCSSValueList(value)->item(0));
+ keyframe->setEasing(timingFunction.release());
+ } else if (CSSPropertyMetadata::isAnimatableProperty(property)) {
keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get());
+ }
}
keyframes.append(keyframe);
// The last keyframe specified at a given offset is used.
- perKeyframeTimingFunctions.set(offsets[0], timingFunction);
for (size_t j = 1; j < offsets.size(); ++j) {
- keyframes.append(keyframe->cloneWithOffset(offsets[j]));
- perKeyframeTimingFunctions.set(offsets[j], timingFunction);
+ keyframes.append(toAnimatableValueKeyframe(keyframe->cloneWithOffset(offsets[j]).get()));
}
}
ASSERT(!keyframes.isEmpty());
- if (!perKeyframeTimingFunctions.contains(0))
- perKeyframeTimingFunctions.set(0, defaultTimingFunction);
-
- for (PropertySet::const_iterator iter = specifiedProperties.begin(); iter != specifiedProperties.end(); ++iter) {
+ for (PropertySet::const_iterator iter = specifiedPropertiesForUseCounter.begin(); iter != specifiedPropertiesForUseCounter.end(); ++iter) {
const CSSPropertyID property = *iter;
ASSERT(property != CSSPropertyInvalid);
blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
keyframes.shrink(targetIndex + 1);
// Add 0% and 100% keyframes if absent.
- RefPtr<Keyframe> startKeyframe = keyframes[0];
+ RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = keyframes[0];
if (startKeyframe->offset()) {
- startKeyframe = Keyframe::create();
+ startKeyframe = AnimatableValueKeyframe::create();
startKeyframe->setOffset(0);
+ startKeyframe->setEasing(defaultTimingFunction);
keyframes.prepend(startKeyframe);
}
- RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1];
+ RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = keyframes[keyframes.size() - 1];
if (endKeyframe->offset() != 1) {
- endKeyframe = Keyframe::create();
+ endKeyframe = AnimatableValueKeyframe::create();
endKeyframe->setOffset(1);
+ endKeyframe->setEasing(defaultTimingFunction);
keyframes.append(endKeyframe);
}
ASSERT(keyframes.size() >= 2);
bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
if (!startNeedsValue && !endNeedsValue)
continue;
- RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
+ RefPtrWillBeRawPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
if (startNeedsValue)
startKeyframe->setPropertyValue(property, snapshotValue.get());
if (endNeedsValue)
}
ASSERT(startKeyframe->properties().size() == allProperties.size());
ASSERT(endKeyframe->properties().size() == allProperties.size());
-
- // Determine how many keyframes specify each property. Note that this must
- // be done after we've filled in end keyframes.
- typedef HashCountedSet<CSSPropertyID> PropertyCountedSet;
- PropertyCountedSet propertyCounts;
- for (size_t i = 0; i < numKeyframes; ++i) {
- const PropertySet& properties = keyframes[i]->properties();
- for (PropertySet::const_iterator iter = properties.begin(); iter != properties.end(); ++iter)
- propertyCounts.add(*iter);
- }
-
- // Split keyframes into groups, where each group contains only keyframes
- // which specify all properties used in that group. Each group is animated
- // in a separate animation, to allow per-keyframe timing functions to be
- // applied correctly.
- for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) {
- const CSSPropertyID property = iter->key;
- const size_t count = iter->value;
- ASSERT(count <= numKeyframes);
- if (count == numKeyframes)
- continue;
- KeyframeEffectModel::KeyframeVector splitOutKeyframes;
- for (size_t i = 0; i < numKeyframes; i++) {
- Keyframe* keyframe = keyframes[i].get();
- if (!keyframe->properties().contains(property)) {
- ASSERT(i && i != numKeyframes - 1);
- continue;
- }
- RefPtr<Keyframe> clonedKeyframe = Keyframe::create();
- clonedKeyframe->setOffset(keyframe->offset());
- clonedKeyframe->setComposite(keyframe->composite());
- clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(property));
- splitOutKeyframes.append(clonedKeyframe);
- // Note that it's OK if this keyframe ends up having no
- // properties. This can only happen when none of the properties
- // are specified in all keyframes, in which case we won't animate
- // anything with these keyframes.
- keyframe->clearPropertyValue(property);
- }
- ASSERT(!splitOutKeyframes.first()->offset());
- ASSERT(splitOutKeyframes.last()->offset() == 1);
-#ifndef NDEBUG
- for (size_t j = 0; j < splitOutKeyframes.size(); ++j)
- ASSERT(splitOutKeyframes[j]->properties().size() == 1);
-#endif
- keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, generateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions)));
- }
-
- unsigned numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties().size();
-#ifndef NDEBUG
- for (size_t i = 1; i < numKeyframes; ++i)
- ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllKeyframes);
-#endif
-
- // If the animation specifies any keyframes, we always provide at least one
- // vector of resolved keyframes, even if no properties are animated.
- if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEmpty())
- keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTimingFunction(keyframes, perKeyframeTimingFunctions)));
-}
-
-// Returns the default timing function.
-const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData* animationData, Timing& timing, bool& isPaused)
-{
- if (animationData->isDelaySet())
- timing.startDelay = animationData->delay();
- if (animationData->isDurationSet())
- timing.iterationDuration = animationData->duration();
- if (animationData->isIterationCountSet()) {
- if (animationData->iterationCount() == CSSAnimationData::IterationCountInfinite)
- timing.iterationCount = std::numeric_limits<double>::infinity();
- else
- timing.iterationCount = animationData->iterationCount();
- }
- if (animationData->isFillModeSet()) {
- switch (animationData->fillMode()) {
- case AnimationFillModeForwards:
- timing.fillMode = Timing::FillModeForwards;
- break;
- case AnimationFillModeBackwards:
- timing.fillMode = Timing::FillModeBackwards;
- break;
- case AnimationFillModeBoth:
- timing.fillMode = Timing::FillModeBoth;
- break;
- case AnimationFillModeNone:
- timing.fillMode = Timing::FillModeNone;
- break;
- default:
- ASSERT_NOT_REACHED();
- }
- } else {
- timing.fillMode = Timing::FillModeNone;
- }
- if (animationData->isDirectionSet()) {
- switch (animationData->direction()) {
- case CSSAnimationData::AnimationDirectionNormal:
- timing.direction = Timing::PlaybackDirectionNormal;
- break;
- case CSSAnimationData::AnimationDirectionAlternate:
- timing.direction = Timing::PlaybackDirectionAlternate;
- break;
- case CSSAnimationData::AnimationDirectionReverse:
- timing.direction = Timing::PlaybackDirectionReverse;
- break;
- case CSSAnimationData::AnimationDirectionAlternateReverse:
- timing.direction = Timing::PlaybackDirectionAlternateReverse;
- break;
- default:
- ASSERT_NOT_REACHED();
- }
- }
-
- // For CSS, the constraints on the timing properties are tighter than in
- // the general case of the Web Animations model.
- timing.assertValid();
- ASSERT(!timing.iterationStart);
- ASSERT(timing.playbackRate == 1);
- ASSERT(!std::isinf(timing.iterationDuration));
-
- isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused;
- return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
}
} // namespace
const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
{
- if (resolver->styleTreeHasOnlyScopedResolverForDocument())
- return resolver->styleTreeScopedStyleResolverForDocument()->keyframeStylesForAnimation(animationName);
+ // FIXME: This is all implementation detail of style resolver, CSSAnimations shouldn't be reaching into any of it.
+ if (resolver->document().styleEngine()->hasOnlyScopedResolverForDocument())
+ return element->document().scopedStyleResolver()->keyframeStylesForAnimation(animationName);
- Vector<ScopedStyleResolver*, 8> stack;
+ WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> stack;
resolver->styleTreeResolveScopedKeyframesRules(element, stack);
if (stack.isEmpty())
return 0;
return 0;
}
-PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
+CSSAnimations::CSSAnimations()
{
- OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
+}
+
+PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
+{
+ OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = adoptPtrWillBeNoop(new CSSAnimationUpdate());
calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
- calculateAnimationCompositableValues(update.get(), element);
+ calculateAnimationActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
calculateTransitionUpdate(update.get(), element, style);
- calculateTransitionCompositableValues(update.get(), element);
+ calculateTransitionActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
return update->isEmpty() ? nullptr : update.release();
}
{
const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
-#if ASSERT_DISABLED
+#if !ENABLE(ASSERT)
// If we're in an animation style change, no animations can have started, been cancelled or changed play state.
// When ASSERT is enabled, we verify this optimization.
if (activeAnimations && activeAnimations->isAnimationStyleChange())
return;
#endif
- const CSSAnimationDataList* animationDataList = style.animations();
+ const CSSAnimationData* animationData = style.animations();
const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
HashSet<AtomicString> inactive;
inactive.add(iter->key);
if (style.display() != NONE) {
- for (size_t i = 0; animationDataList && i < animationDataList->size(); ++i) {
- const CSSAnimationData* animationData = animationDataList->animation(i);
- if (animationData->isNoneAnimation())
+ for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) {
+ AtomicString animationName(animationData->nameList()[i]);
+ if (animationName == CSSAnimationData::initialName())
continue;
- ASSERT(animationData->isValidAnimation());
- AtomicString animationName(animationData->name());
+
+ bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused;
// Keyframes and animation properties are snapshotted when the
// animation starts, so we don't need to track changes to these,
AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName));
if (existing != cssAnimations->m_animations.end()) {
inactive.remove(animationName);
- const HashSet<RefPtr<Player> >& players = existing->value;
- ASSERT(!players.isEmpty());
- bool isFirstPlayerPaused = (*players.begin())->paused();
-#ifndef NDEBUG
- for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
- ASSERT((*iter)->paused() == isFirstPlayerPaused);
-#endif
- if ((animationData->playState() == AnimPlayStatePaused) != isFirstPlayerPaused) {
+ AnimationPlayer* player = existing->value.get();
+ if (isPaused != player->paused()) {
ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
update->toggleAnimationPaused(animationName);
}
}
}
- Timing timing;
- bool isPaused;
- RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationData(animationData, timing, isPaused);
- Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > > keyframesAndTimingFunctions;
- resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, defaultTimingFunction.get(), keyframesAndTimingFunctions);
- if (!keyframesAndTimingFunctions.isEmpty()) {
- HashSet<RefPtr<InertAnimation> > animations;
- for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j) {
- ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty());
- timing.timingFunction = keyframesAndTimingFunctions[j].second;
- // FIXME: crbug.com/268791 - Keyframes are already normalized, perhaps there should be a flag on KeyframeEffectModel to skip normalization.
- animations.add(InertAnimation::create(KeyframeEffectModel::create(keyframesAndTimingFunctions[j].first), timing, isPaused));
- }
+ Timing timing = animationData->convertToTiming(i);
+ RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction;
+ timing.timingFunction = Timing::defaults().timingFunction;
+ AnimatableValueKeyframeVector resolvedKeyframes;
+ resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes);
+ if (!resolvedKeyframes.isEmpty()) {
ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
- update->startAnimation(animationName, animations);
+ update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused));
}
}
}
ASSERT(inactive.isEmpty() || cssAnimations);
for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) {
ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
- update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter));
+ update->cancelAnimation(*iter, *cssAnimations->m_animations.get(*iter));
}
}
void CSSAnimations::maybeApplyPendingUpdate(Element* element)
{
if (!m_pendingUpdate) {
- m_previousCompositableValuesForAnimations.clear();
+ m_previousActiveInterpolationsForAnimations.clear();
return;
}
- OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
+ OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
- m_previousCompositableValuesForAnimations.swap(update->compositableValuesForAnimations());
+ m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations());
// FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here
// since we call this from recalc style.
DisableCompositingQueryAsserts disabler;
for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
- const HashSet<RefPtr<Player> >& players = m_animations.take(*iter);
- for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
- (*iter)->cancel();
+ RefPtr<AnimationPlayer> player = m_animations.take(*iter);
+ player->cancel();
+ player->update(TimingUpdateOnDemand);
}
for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
- const HashSet<RefPtr<Player> >& players = m_animations.get(*iter);
- ASSERT(!players.isEmpty());
- bool isFirstPlayerPaused = (*players.begin())->paused();
- for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter) {
- Player* player = iter->get();
- ASSERT(player->paused() == isFirstPlayerPaused);
- if (isFirstPlayerPaused)
- player->unpause();
- else
- player->pause();
- }
+ AnimationPlayer* player = m_animations.get(*iter);
+ if (player->paused())
+ player->unpause();
+ else
+ player->pause();
+ if (player->outdated())
+ player->update(TimingUpdateOnDemand);
}
- for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
- OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name));
- HashSet<RefPtr<Player> > players;
- for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = iter->animations.begin(); animationsIter != iter->animations.end(); ++animationsIter) {
- const InertAnimation* inertAnimation = animationsIter->get();
- // The event delegate is set on the the first animation only. We
- // rely on the behavior of OwnPtr::release() to achieve this.
- RefPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
- Player* player = element->document().timeline()->createPlayer(animation.get());
- if (inertAnimation->paused())
- player->pause();
- element->document().cssPendingAnimations().add(player);
- player->update();
- players.add(player);
- }
- m_animations.set(iter->name, players);
+ for (WillBeHeapVector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
+ const InertAnimation* inertAnimation = iter->animation.get();
+ OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillBeNoop(new AnimationEventDelegate(element, iter->name));
+ RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
+ RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get());
+ element->document().compositorPendingAnimations().add(player.get());
+ if (inertAnimation->paused())
+ player->pause();
+ player->update(TimingUpdateOnDemand);
+ m_animations.set(iter->name, player.get());
}
// Transitions that are run on the compositor only update main-thread state
// be when transitions are retargeted. Instead of triggering complete style
// recalculation, we find these cases by searching for new transitions that
// have matching cancelled animation property IDs on the compositor.
- HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedCompositorTransitions;
- const ActiveAnimations* activeAnimations = element->activeAnimations();
+ WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double> > retargetedCompositorTransitions;
for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
CSSPropertyID id = *iter;
ASSERT(m_transitions.contains(id));
- Player* player = m_transitions.take(id).transition->player();
- if (activeAnimations && activeAnimations->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
- retargetedCompositorTransitions.add(id, std::pair<RefPtr<Animation>, double>(toAnimation(player->source()), player->startTime()));
+
+ RefPtrWillBeRawPtr<AnimationPlayer> player = m_transitions.take(id).player;
+ Animation* animation = toAnimation(player->source());
+ if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
+ retargetedCompositorTransitions.add(id, std::pair<RefPtrWillBeMember<Animation>, double>(animation, player->startTimeInternal()));
player->cancel();
+ player->update(TimingUpdateOnDemand);
}
for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
CSSPropertyID id = newTransition.id;
InertAnimation* inertAnimation = newTransition.animation.get();
- OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id));
+ OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId));
- RefPtr<AnimationEffect> effect = inertAnimation->effect();
+ RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect();
if (retargetedCompositorTransitions.contains(id)) {
- const std::pair<RefPtr<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
- RefPtr<Animation> oldAnimation = oldTransition.first;
+ const std::pair<RefPtrWillBeMember<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
+ RefPtrWillBeRawPtr<Animation> oldAnimation = oldTransition.first;
double oldStartTime = oldTransition.second;
- double inheritedTime = isNull(oldStartTime) ? 0 : element->document().transitionTimeline()->currentTime() - oldStartTime;
- oldAnimation->updateInheritedTime(inheritedTime);
- KeyframeEffectModel* oldEffect = toKeyframeEffectModel(inertAnimation->effect());
- const KeyframeEffectModel::KeyframeVector& frames = oldEffect->getFrames();
- KeyframeEffectModel::KeyframeVector newFrames;
- newFrames.append(frames[0]->clone());
+ double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime;
+
+ AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect());
+ const KeyframeVector& frames = oldEffect->getFrames();
+
+ AnimatableValueKeyframeVector newFrames;
+ newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get()));
+ newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get()));
+
newFrames[0]->clearPropertyValue(id);
- ASSERT(oldAnimation->compositableValues()->size() == 1);
- const AnimationEffect::CompositableValue* compositableValue = oldAnimation->compositableValues()->at(0).second.get();
- ASSERT(!compositableValue->dependsOnUnderlyingValue());
- newFrames[0]->setPropertyValue(id, compositableValue->compositeOnto(0).get());
- newFrames.append(frames[1]->clone());
- effect = KeyframeEffectModel::create(newFrames);
+ RefPtrWillBeRawPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false);
+ OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample = inertAnimationForSampling->sample(inheritedTime);
+ ASSERT(sample->size() == 1);
+ newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue());
+
+ effect = AnimatableValueKeyframeEffectModel::create(newFrames);
}
- RefPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
- RefPtr<Player> player = element->document().transitionTimeline()->createPlayer(transition.get());
- player->update();
- element->document().cssPendingAnimations().add(player.get());
- runningTransition.transition = transition.get();
+
+ RefPtrWillBeRawPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
+ RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get());
+ element->document().compositorPendingAnimations().add(player.get());
+ player->update(TimingUpdateOnDemand);
+ runningTransition.player = player;
m_transitions.set(id, runningTransition);
ASSERT(id != CSSPropertyInvalid);
blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id));
}
}
-void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const CSSAnimationData* anim, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
+void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, CSSPropertyID eventId, const CSSTransitionData& transitionData, size_t transitionIndex, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
{
- RefPtr<AnimatableValue> to;
+ RefPtrWillBeRawPtr<AnimatableValue> to = nullptr;
if (activeTransitions) {
TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
if (activeTransitionIter != activeTransitions->end()) {
}
}
- if (anim->duration() + anim->delay() <= 0)
- return;
-
- if (CSSPropertyAnimation::propertiesEqual(id, &oldStyle, &style))
+ if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style))
return;
if (!to)
to = CSSAnimatableValueFactory::create(id, style);
- RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
+ RefPtrWillBeRawPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
// If we have multiple transitions on the same property, we will use the
// last one since we iterate over them in order.
if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
return;
- KeyframeEffectModel::KeyframeVector keyframes;
+ Timing timing = transitionData.convertToTiming(transitionIndex);
+ if (timing.startDelay + timing.iterationDuration <= 0)
+ return;
+
+ AnimatableValueKeyframeVector keyframes;
- RefPtr<Keyframe> startKeyframe = Keyframe::create();
+ RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::create();
startKeyframe->setPropertyValue(id, from.get());
startKeyframe->setOffset(0);
+ startKeyframe->setEasing(timing.timingFunction.release());
+ timing.timingFunction = LinearTimingFunction::shared();
keyframes.append(startKeyframe);
- RefPtr<Keyframe> endKeyframe = Keyframe::create();
+ RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::create();
endKeyframe->setPropertyValue(id, to.get());
endKeyframe->setOffset(1);
keyframes.append(endKeyframe);
- RefPtr<KeyframeEffectModel> effect = KeyframeEffectModel::create(keyframes);
-
- Timing timing;
- bool isPaused;
- RefPtr<TimingFunction> timingFunction = timingFromAnimationData(anim, timing, isPaused);
- ASSERT(!isPaused);
- timing.timingFunction = timingFunction;
- // Note that the backwards part is required for delay to work.
- timing.fillMode = Timing::FillModeBoth;
-
- update->startTransition(id, from.get(), to.get(), InertAnimation::create(effect, timing, isPaused));
+ RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes);
+ update->startTransition(id, eventId, from.get(), to.get(), InertAnimation::create(effect, timing, false));
ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
}
ActiveAnimations* activeAnimations = element->activeAnimations();
const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
+ const CSSTransitionData* transitionData = style.transitions();
-#if ASSERT_DISABLED
- // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
- const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
-#else
+#if ENABLE(ASSERT)
// In debug builds we verify that it would have been safe to avoid populating and testing listedProperties if the style recalc is due to animation.
const bool animationStyleRecalc = false;
+#else
+ // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
+ const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
#endif
BitArray<numCSSProperties> listedProperties;
- bool anyTransitionHadAnimateAll = false;
+ bool anyTransitionHadTransitionAll = false;
const RenderObject* renderer = element->renderer();
- if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && style.transitions()) {
+ if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && transitionData) {
const RenderStyle& oldStyle = *renderer->style();
- for (size_t i = 0; i < style.transitions()->size(); ++i) {
- const CSSAnimationData* anim = style.transitions()->animation(i);
- CSSAnimationData::AnimationMode mode = anim->animationMode();
- if (mode == CSSAnimationData::AnimateNone)
+ for (size_t i = 0; i < transitionData->propertyList().size(); ++i) {
+ const CSSTransitionData::TransitionProperty& transitionProperty = transitionData->propertyList()[i];
+ CSSTransitionData::TransitionPropertyType mode = transitionProperty.propertyType;
+ CSSPropertyID property = transitionProperty.propertyId;
+ if (mode == CSSTransitionData::TransitionNone || mode == CSSTransitionData::TransitionUnknown)
continue;
- bool animateAll = mode == CSSAnimationData::AnimateAll;
- ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
+ bool animateAll = mode == CSSTransitionData::TransitionAll;
+ ASSERT(animateAll || mode == CSSTransitionData::TransitionSingleProperty);
if (animateAll)
- anyTransitionHadAnimateAll = true;
- const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
+ anyTransitionHadTransitionAll = true;
+ const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(property);
// If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
- CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : anim->property();
+ CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : property;
+ CSSPropertyID eventId = id;
if (!animateAll) {
- if (CSSAnimations::isAnimatableProperty(id))
+ id = propertyForAnimation(id);
+ if (CSSPropertyMetadata::isAnimatableProperty(id))
listedProperties.set(id);
else
continue;
// FIXME: We should transition if an !important property changes even when an animation is running,
// but this is a bit hard to do with the current applyMatchedProperties system.
- if (!update->compositableValuesForAnimations().contains(id)
- && (!activeAnimations || !activeAnimations->cssAnimations().m_previousCompositableValuesForAnimations.contains(id))) {
- calculateTransitionUpdateForProperty(id, anim, oldStyle, style, activeTransitions, update, element);
+ if (!update->activeInterpolationsForAnimations().contains(id)
+ && (!activeAnimations || !activeAnimations->cssAnimations().m_previousActiveInterpolationsForAnimations.contains(id))) {
+ calculateTransitionUpdateForProperty(id, eventId, *transitionData, i, oldStyle, style, activeTransitions, update, element);
}
}
}
if (activeTransitions) {
for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
- const TimedItem* timedItem = iter->value.transition;
+ const AnimationPlayer& player = *iter->value.player;
CSSPropertyID id = iter->key;
- if (timedItem->phase() == TimedItem::PhaseAfter || (!anyTransitionHadAnimateAll && !animationStyleRecalc && !listedProperties.get(id))) {
- ASSERT(timedItem->phase() == TimedItem::PhaseAfter || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
+ if (player.finishedInternal() || (!anyTransitionHadTransitionAll && !animationStyleRecalc && !listedProperties.get(id))) {
+ // TODO: Figure out why this fails on Chrome OS login page. crbug.com/365507
+ // ASSERT(player.finishedInternal() || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
update->cancelTransition(id);
}
}
void CSSAnimations::cancel()
{
for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animations.end(); ++iter) {
- const HashSet<RefPtr<Player> >& players = iter->value;
- for (HashSet<RefPtr<Player> >::const_iterator animationsIter = players.begin(); animationsIter != players.end(); ++animationsIter)
- (*animationsIter)->cancel();
+ iter->value->cancel();
+ iter->value->update(TimingUpdateOnDemand);
}
- for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter)
- iter->value.transition->player()->cancel();
+ for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter) {
+ iter->value.player->cancel();
+ iter->value.player->update(TimingUpdateOnDemand);
+ }
m_animations.clear();
m_transitions.clear();
m_pendingUpdate = nullptr;
}
-void CSSAnimations::calculateAnimationCompositableValues(CSSAnimationUpdate* update, const Element* element)
+void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
{
ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
- if (update->newAnimations().isEmpty() && update->cancelledAnimationPlayers().isEmpty()) {
- AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, 0, 0, Animation::DefaultPriority));
- update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
+ if (update->newAnimations().isEmpty() && update->cancelledAnimationAnimationPlayers().isEmpty()) {
+ WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::DefaultPriority, timelineCurrentTime));
+ update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
return;
}
- Vector<InertAnimation*> newAnimations;
+ WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newAnimations;
for (size_t i = 0; i < update->newAnimations().size(); ++i) {
- HashSet<RefPtr<InertAnimation> > animations = update->newAnimations()[i].animations;
- for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = animations.begin(); animationsIter != animations.end(); ++animationsIter)
- newAnimations.append(animationsIter->get());
+ newAnimations.append(update->newAnimations()[i].animation.get());
}
- AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, &newAnimations, &update->cancelledAnimationPlayers(), Animation::DefaultPriority));
- update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
+ WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, &newAnimations, &update->cancelledAnimationAnimationPlayers(), Animation::DefaultPriority, timelineCurrentTime));
+ update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
}
-void CSSAnimations::calculateTransitionCompositableValues(CSSAnimationUpdate* update, const Element* element)
+void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
{
ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
- AnimationEffect::CompositableValueMap compositableValuesForTransitions;
+ WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForTransitions;
if (update->newTransitions().isEmpty() && update->cancelledTransitions().isEmpty()) {
- compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, 0, 0, Animation::TransitionPriority);
+ activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::TransitionPriority, timelineCurrentTime);
} else {
- Vector<InertAnimation*> newTransitions;
+ WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newTransitions;
for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
newTransitions.append(iter->value.animation.get());
- HashSet<const Player*> cancelledPlayers;
+ WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> > cancelledAnimationPlayers;
if (!update->cancelledTransitions().isEmpty()) {
ASSERT(activeAnimations);
const TransitionMap& transitionMap = activeAnimations->cssAnimations().m_transitions;
for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
ASSERT(transitionMap.contains(*iter));
- cancelledPlayers.add(transitionMap.get(*iter).transition->player());
+ cancelledAnimationPlayers.add(transitionMap.get(*iter).player.get());
}
}
- compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, &newTransitions, &cancelledPlayers, Animation::TransitionPriority);
+ activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, &newTransitions, &cancelledAnimationPlayers, Animation::TransitionPriority, timelineCurrentTime);
}
// Properties being animated by animations don't get values from transitions applied.
- if (!update->compositableValuesForAnimations().isEmpty() && !compositableValuesForTransitions.isEmpty()) {
- for (AnimationEffect::CompositableValueMap::const_iterator iter = update->compositableValuesForAnimations().begin(); iter != update->compositableValuesForAnimations().end(); ++iter)
- compositableValuesForTransitions.remove(iter->key);
+ if (!update->activeInterpolationsForAnimations().isEmpty() && !activeInterpolationsForTransitions.isEmpty()) {
+ for (WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >::const_iterator iter = update->activeInterpolationsForAnimations().begin(); iter != update->activeInterpolationsForAnimations().end(); ++iter)
+ activeInterpolationsForTransitions.remove(iter->key);
}
- update->adoptCompositableValuesForTransitions(compositableValuesForTransitions);
+ update->adoptActiveInterpolationsForTransitions(activeInterpolationsForTransitions);
}
void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
{
- if (m_target->document().hasListenerType(listenerType))
- m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnimationEvent::create(eventName, m_name, elapsedTime));
+ if (m_target->document().hasListenerType(listenerType)) {
+ RefPtrWillBeRawPtr<WebKitAnimationEvent> event = WebKitAnimationEvent::create(eventName, m_name, elapsedTime);
+ event->setTarget(m_target);
+ m_target->document().enqueueAnimationFrameEvent(event);
+ }
}
-void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+void CSSAnimations::AnimationEventDelegate::onEventCondition(const AnimationNode* animationNode)
{
- // Events for a single document are queued and dispatched as a group at
- // the end of DocumentTimeline::serviceAnimations.
- // FIXME: Events which are queued outside of serviceAnimations should
- // trigger a timer to dispatch when control is released.
- const TimedItem::Phase currentPhase = timedItem->phase();
- const double currentIteration = timedItem->currentIteration();
-
- // Note that the elapsedTime is measured from when the animation starts playing.
- if (!isFirstSample && previousPhase == TimedItem::PhaseActive && currentPhase == TimedItem::PhaseActive && previousIteration != currentIteration) {
- ASSERT(!isNull(previousIteration));
- ASSERT(!isNull(currentIteration));
+ const AnimationNode::Phase currentPhase = animationNode->phase();
+ const double currentIteration = animationNode->currentIteration();
+
+ if (m_previousPhase != currentPhase
+ && (currentPhase == AnimationNode::PhaseActive || currentPhase == AnimationNode::PhaseAfter)
+ && (m_previousPhase == AnimationNode::PhaseNone || m_previousPhase == AnimationNode::PhaseBefore)) {
+ // The spec states that the elapsed time should be
+ // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
+ // implementation. See crbug.com/279611
+ maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
+ }
+
+ if (currentPhase == AnimationNode::PhaseActive && m_previousPhase == currentPhase && m_previousIteration != currentIteration) {
// We fire only a single event for all iterations thast terminate
// between a single pair of samples. See http://crbug.com/275263. For
// compatibility with the existing implementation, this event uses
// the elapsedTime for the first iteration in question.
- ASSERT(!std::isnan(timedItem->specifiedTiming().iterationDuration));
- const double elapsedTime = timedItem->specifiedTiming().iterationDuration * (previousIteration + 1);
+ ASSERT(!std::isnan(animationNode->specifiedTiming().iterationDuration));
+ const double elapsedTime = animationNode->specifiedTiming().iterationDuration * (m_previousIteration + 1);
maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::animationiteration, elapsedTime);
- return;
- }
- if ((isFirstSample || previousPhase == TimedItem::PhaseBefore) && isLaterPhase(currentPhase, TimedItem::PhaseBefore)) {
- ASSERT(timedItem->specifiedTiming().startDelay > 0 || isFirstSample);
- // The spec states that the elapsed time should be
- // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
- // implementation. See crbug.com/279611
- maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
}
- if ((isFirstSample || isEarlierPhase(previousPhase, TimedItem::PhaseAfter)) && currentPhase == TimedItem::PhaseAfter)
- maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, timedItem->activeDuration());
+
+ if (currentPhase == AnimationNode::PhaseAfter && m_previousPhase != AnimationNode::PhaseAfter)
+ maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, animationNode->activeDurationInternal());
+
+ m_previousPhase = currentPhase;
+ m_previousIteration = currentIteration;
}
-void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+void CSSAnimations::AnimationEventDelegate::trace(Visitor* visitor)
{
- // Events for a single document are queued and dispatched as a group at
- // the end of DocumentTimeline::serviceAnimations.
- // FIXME: Events which are queued outside of serviceAnimations should
- // trigger a timer to dispatch when control is released.
- const TimedItem::Phase currentPhase = timedItem->phase();
- if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase != currentPhase) && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
+ visitor->trace(m_target);
+ AnimationNode::EventDelegate::trace(visitor);
+}
+
+void CSSAnimations::TransitionEventDelegate::onEventCondition(const AnimationNode* animationNode)
+{
+ const AnimationNode::Phase currentPhase = animationNode->phase();
+ if (currentPhase == AnimationNode::PhaseAfter && currentPhase != m_previousPhase && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
String propertyName = getPropertyNameString(m_property);
- const Timing& timing = timedItem->specifiedTiming();
+ const Timing& timing = animationNode->specifiedTiming();
double elapsedTime = timing.iterationDuration;
const AtomicString& eventType = EventTypeNames::transitionend;
String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
- m_target->document().transitionTimeline()->addEventToDispatch(m_target, TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement));
+ RefPtrWillBeRawPtr<TransitionEvent> event = TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement);
+ event->setTarget(m_target);
+ m_target->document().enqueueAnimationFrameEvent(event);
}
-}
+ m_previousPhase = currentPhase;
+}
-bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
+void CSSAnimations::TransitionEventDelegate::trace(Visitor* visitor)
{
- switch (property) {
- case CSSPropertyBackgroundColor:
- case CSSPropertyBackgroundImage:
- case CSSPropertyBackgroundPositionX:
- case CSSPropertyBackgroundPositionY:
- case CSSPropertyBackgroundSize:
- case CSSPropertyBaselineShift:
- case CSSPropertyBorderBottomColor:
- case CSSPropertyBorderBottomLeftRadius:
- case CSSPropertyBorderBottomRightRadius:
- case CSSPropertyBorderBottomWidth:
- case CSSPropertyBorderImageOutset:
- case CSSPropertyBorderImageSlice:
- case CSSPropertyBorderImageSource:
- case CSSPropertyBorderImageWidth:
- case CSSPropertyBorderLeftColor:
- case CSSPropertyBorderLeftWidth:
- case CSSPropertyBorderRightColor:
- case CSSPropertyBorderRightWidth:
- case CSSPropertyBorderTopColor:
- case CSSPropertyBorderTopLeftRadius:
- case CSSPropertyBorderTopRightRadius:
- case CSSPropertyBorderTopWidth:
- case CSSPropertyBottom:
- case CSSPropertyBoxShadow:
- case CSSPropertyClip:
- case CSSPropertyColor:
- case CSSPropertyFill:
- case CSSPropertyFillOpacity:
- case CSSPropertyFlexBasis:
- case CSSPropertyFlexGrow:
- case CSSPropertyFlexShrink:
- case CSSPropertyFloodColor:
- case CSSPropertyFloodOpacity:
- case CSSPropertyFontSize:
- case CSSPropertyHeight:
- case CSSPropertyKerning:
- case CSSPropertyLeft:
- case CSSPropertyLetterSpacing:
- case CSSPropertyLightingColor:
- case CSSPropertyLineHeight:
- case CSSPropertyListStyleImage:
- case CSSPropertyMarginBottom:
- case CSSPropertyMarginLeft:
- case CSSPropertyMarginRight:
- case CSSPropertyMarginTop:
- case CSSPropertyMaxHeight:
- case CSSPropertyMaxWidth:
- case CSSPropertyMinHeight:
- case CSSPropertyMinWidth:
- case CSSPropertyObjectPosition:
- case CSSPropertyOpacity:
- case CSSPropertyOrphans:
- case CSSPropertyOutlineColor:
- case CSSPropertyOutlineOffset:
- case CSSPropertyOutlineWidth:
- case CSSPropertyPaddingBottom:
- case CSSPropertyPaddingLeft:
- case CSSPropertyPaddingRight:
- case CSSPropertyPaddingTop:
- case CSSPropertyRight:
- case CSSPropertyStopColor:
- case CSSPropertyStopOpacity:
- case CSSPropertyStroke:
- case CSSPropertyStrokeDasharray:
- case CSSPropertyStrokeDashoffset:
- case CSSPropertyStrokeMiterlimit:
- case CSSPropertyStrokeOpacity:
- case CSSPropertyStrokeWidth:
- case CSSPropertyTextDecorationColor:
- case CSSPropertyTextIndent:
- case CSSPropertyTextShadow:
- case CSSPropertyTop:
- case CSSPropertyVisibility:
- case CSSPropertyWebkitBackgroundSize:
- case CSSPropertyWebkitBorderHorizontalSpacing:
- case CSSPropertyWebkitBorderVerticalSpacing:
- case CSSPropertyWebkitBoxShadow:
- case CSSPropertyWebkitClipPath:
- case CSSPropertyWebkitColumnCount:
- case CSSPropertyWebkitColumnGap:
- case CSSPropertyWebkitColumnRuleColor:
- case CSSPropertyWebkitColumnRuleWidth:
- case CSSPropertyWebkitColumnWidth:
- case CSSPropertyWebkitFilter:
- case CSSPropertyWebkitMaskBoxImageOutset:
- case CSSPropertyWebkitMaskBoxImageSlice:
- case CSSPropertyWebkitMaskBoxImageSource:
- case CSSPropertyWebkitMaskBoxImageWidth:
- case CSSPropertyWebkitMaskImage:
- case CSSPropertyWebkitMaskPositionX:
- case CSSPropertyWebkitMaskPositionY:
- case CSSPropertyWebkitMaskSize:
- case CSSPropertyWebkitPerspective:
- case CSSPropertyWebkitPerspectiveOriginX:
- case CSSPropertyWebkitPerspectiveOriginY:
- case CSSPropertyShapeInside:
- case CSSPropertyShapeOutside:
- case CSSPropertyShapeMargin:
- case CSSPropertyShapeImageThreshold:
- case CSSPropertyWebkitTextStrokeColor:
- case CSSPropertyWebkitTransform:
- case CSSPropertyWebkitTransformOriginX:
- case CSSPropertyWebkitTransformOriginY:
- case CSSPropertyWebkitTransformOriginZ:
- case CSSPropertyWidows:
- case CSSPropertyWidth:
- case CSSPropertyWordSpacing:
- case CSSPropertyZIndex:
- case CSSPropertyZoom:
- return true;
- default:
- return false;
- }
+ visitor->trace(m_target);
+ AnimationNode::EventDelegate::trace(visitor);
}
const StylePropertyShorthand& CSSAnimations::animatableProperties()
if (properties.isEmpty()) {
for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
CSSPropertyID id = convertToCSSPropertyID(i);
- if (isAnimatableProperty(id))
+ if (CSSPropertyMetadata::isAnimatableProperty(id))
properties.append(id);
}
propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
return propertyShorthand;
}
-} // namespace WebCore
+// Animation properties are not allowed to be affected by Web Animations.
+// http://dev.w3.org/fxtf/web-animations/#not-animatable
+bool CSSAnimations::isAllowedAnimation(CSSPropertyID property)
+{
+ switch (property) {
+ case CSSPropertyAnimation:
+ case CSSPropertyAnimationDelay:
+ case CSSPropertyAnimationDirection:
+ case CSSPropertyAnimationDuration:
+ case CSSPropertyAnimationFillMode:
+ case CSSPropertyAnimationIterationCount:
+ case CSSPropertyAnimationName:
+ case CSSPropertyAnimationPlayState:
+ case CSSPropertyAnimationTimingFunction:
+ case CSSPropertyDisplay:
+ case CSSPropertyTransition:
+ case CSSPropertyTransitionDelay:
+ case CSSPropertyTransitionDuration:
+ case CSSPropertyTransitionProperty:
+ case CSSPropertyTransitionTimingFunction:
+ case CSSPropertyWebkitAnimation:
+ case CSSPropertyWebkitAnimationDelay:
+ case CSSPropertyWebkitAnimationDirection:
+ case CSSPropertyWebkitAnimationDuration:
+ case CSSPropertyWebkitAnimationFillMode:
+ case CSSPropertyWebkitAnimationIterationCount:
+ case CSSPropertyWebkitAnimationName:
+ case CSSPropertyWebkitAnimationPlayState:
+ case CSSPropertyWebkitAnimationTimingFunction:
+ case CSSPropertyWebkitTransition:
+ case CSSPropertyWebkitTransitionDelay:
+ case CSSPropertyWebkitTransitionDuration:
+ case CSSPropertyWebkitTransitionProperty:
+ case CSSPropertyWebkitTransitionTimingFunction:
+ return false;
+ default:
+ return true;
+ }
+}
+
+void CSSAnimations::trace(Visitor* visitor)
+{
+#if ENABLE(OILPAN)
+ visitor->trace(m_transitions);
+ visitor->trace(m_pendingUpdate);
+ visitor->trace(m_animations);
+ visitor->trace(m_previousActiveInterpolationsForAnimations);
+#endif
+}
+
+void CSSAnimationUpdate::trace(Visitor* visitor)
+{
+#if ENABLE(OILPAN)
+ visitor->trace(m_newTransitions);
+ visitor->trace(m_activeInterpolationsForAnimations);
+ visitor->trace(m_activeInterpolationsForTransitions);
+ visitor->trace(m_newAnimations);
+ visitor->trace(m_cancelledAnimationPlayers);
+#endif
+}
+
+} // namespace blink