diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/third_party/WebKit/Source/core/animation | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/animation')
161 files changed, 10694 insertions, 5782 deletions
diff --git a/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.cpp b/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.cpp index d27161d6a10..8743e5c4c8b 100644 --- a/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.cpp @@ -31,71 +31,71 @@ #include "config.h" #include "core/animation/ActiveAnimations.h" -#include "core/frame/animation/AnimationController.h" #include "core/rendering/RenderObject.h" namespace WebCore { -bool shouldCompositeForActiveAnimations(const RenderObject& renderer) +ActiveAnimations::~ActiveAnimations() { - ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); - - if (!renderer.node() || !renderer.node()->isElementNode()) - return false; - - const Element* element = toElement(renderer.node()); - if (const ActiveAnimations* activeAnimations = element->activeAnimations()) { - if (activeAnimations->hasActiveAnimations(CSSPropertyOpacity) - || activeAnimations->hasActiveAnimations(CSSPropertyWebkitTransform) - || activeAnimations->hasActiveAnimations(CSSPropertyWebkitFilter)) - return true; - } - - return false; +#if !ENABLE(OILPAN) + for (size_t i = 0; i < m_animations.size(); ++i) + m_animations[i]->notifyElementDestroyed(); + m_animations.clear(); +#endif } -bool hasActiveAnimations(const RenderObject& renderer, CSSPropertyID property) +void ActiveAnimations::addPlayer(AnimationPlayer* player) { - ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); - - if (!renderer.node() || !renderer.node()->isElementNode()) - return false; - - const Element* element = toElement(renderer.node()); - if (const ActiveAnimations* activeAnimations = element->activeAnimations()) - return activeAnimations->hasActiveAnimations(property); - - return false; + ++m_players.add(player, 0).storedValue->value; } -bool hasActiveAnimationsOnCompositor(const RenderObject& renderer, CSSPropertyID property) +void ActiveAnimations::removePlayer(AnimationPlayer* player) { - ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); - - if (!renderer.node() || !renderer.node()->isElementNode()) - return false; - - const Element* element = toElement(renderer.node()); - if (const ActiveAnimations* activeAnimations = element->activeAnimations()) - return activeAnimations->hasActiveAnimationsOnCompositor(property); - - return false; + AnimationPlayerCountedSet::iterator it = m_players.find(player); + ASSERT(it != m_players.end()); + ASSERT(it->value > 0); + --it->value; + if (!it->value) + m_players.remove(it); } -bool ActiveAnimations::hasActiveAnimations(CSSPropertyID property) const +void ActiveAnimations::updateAnimationFlags(RenderStyle& style) { - return m_defaultStack.affects(property); -} + for (AnimationPlayerCountedSet::const_iterator it = m_players.begin(); it != m_players.end(); ++it) { + const AnimationPlayer& player = *it->key; + ASSERT(player.source()); + // FIXME: Needs to consider AnimationGroup once added. + ASSERT(player.source()->isAnimation()); + const Animation& animation = *toAnimation(player.source()); + if (animation.isCurrent()) { + if (animation.affects(CSSPropertyOpacity)) + style.setHasCurrentOpacityAnimation(true); + if (animation.affects(CSSPropertyTransform)) + style.setHasCurrentTransformAnimation(true); + if (animation.affects(CSSPropertyWebkitFilter)) + style.setHasCurrentFilterAnimation(true); + } + } -bool ActiveAnimations::hasActiveAnimationsOnCompositor(CSSPropertyID property) const -{ - return m_defaultStack.hasActiveAnimationsOnCompositor(property); + if (style.hasCurrentOpacityAnimation()) + style.setIsRunningOpacityAnimationOnCompositor(m_defaultStack.hasActiveAnimationsOnCompositor(CSSPropertyOpacity)); + if (style.hasCurrentTransformAnimation()) + style.setIsRunningTransformAnimationOnCompositor(m_defaultStack.hasActiveAnimationsOnCompositor(CSSPropertyTransform)); + if (style.hasCurrentFilterAnimation()) + style.setIsRunningFilterAnimationOnCompositor(m_defaultStack.hasActiveAnimationsOnCompositor(CSSPropertyWebkitFilter)); } void ActiveAnimations::cancelAnimationOnCompositor() { - for (PlayerSet::iterator it = m_players.begin(); it != players().end(); ++it) + for (AnimationPlayerCountedSet::iterator it = m_players.begin(); it != m_players.end(); ++it) it->key->cancelAnimationOnCompositor(); } +void ActiveAnimations::trace(Visitor* visitor) +{ + visitor->trace(m_cssAnimations); + visitor->trace(m_defaultStack); + visitor->trace(m_players); +} + } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.h b/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.h index b12a9bf95dd..ad5ba4b6891 100644 --- a/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.h +++ b/chromium/third_party/WebKit/Source/core/animation/ActiveAnimations.h @@ -44,15 +44,15 @@ class CSSAnimations; class RenderObject; class Element; -// FIXME: Move these to CompositorAnimations -bool shouldCompositeForActiveAnimations(const RenderObject&); -bool hasActiveAnimations(const RenderObject&, CSSPropertyID); -bool hasActiveAnimationsOnCompositor(const RenderObject&, CSSPropertyID); - -class ActiveAnimations { +class ActiveAnimations : public NoBaseWillBeGarbageCollectedFinalized<ActiveAnimations> { + WTF_MAKE_NONCOPYABLE(ActiveAnimations); public: ActiveAnimations() - : m_animationStyleChange(false) { } + : m_animationStyleChange(false) + { + } + + ~ActiveAnimations(); // Animations that are currently active for this element, their effects will be applied // during a style recalc. CSS Transitions are included in this stack. @@ -63,27 +63,44 @@ public: CSSAnimations& cssAnimations() { return m_cssAnimations; } const CSSAnimations& cssAnimations() const { return m_cssAnimations; } - typedef HashCountedSet<Player*> PlayerSet; - // Players which have animations targeting this element. - const PlayerSet& players() const { return m_players; } - PlayerSet& players() { return m_players; } + typedef WillBeHeapHashMap<RawPtrWillBeWeakMember<AnimationPlayer>, int> AnimationPlayerCountedSet; + // AnimationPlayers which have animations targeting this element. + const AnimationPlayerCountedSet& players() const { return m_players; } + void addPlayer(AnimationPlayer*); + void removePlayer(AnimationPlayer*); +#if ENABLE(OILPAN) bool isEmpty() const { return m_defaultStack.isEmpty() && m_cssAnimations.isEmpty(); } +#else + bool isEmpty() const { return m_defaultStack.isEmpty() && m_cssAnimations.isEmpty() && m_animations.isEmpty(); } +#endif - bool hasActiveAnimations(CSSPropertyID) const; - bool hasActiveAnimationsOnCompositor(CSSPropertyID) const; void cancelAnimationOnCompositor(); + void updateAnimationFlags(RenderStyle&); void setAnimationStyleChange(bool animationStyleChange) { m_animationStyleChange = animationStyleChange; } +#if !ENABLE(OILPAN) + void addAnimation(Animation* animation) { m_animations.append(animation); } + void notifyAnimationDestroyed(Animation* animation) { m_animations.remove(m_animations.find(animation)); } +#endif + + void trace(Visitor*); + private: bool isAnimationStyleChange() const { return m_animationStyleChange; } AnimationStack m_defaultStack; CSSAnimations m_cssAnimations; - PlayerSet m_players; + AnimationPlayerCountedSet m_players; bool m_animationStyleChange; +#if !ENABLE(OILPAN) + // FIXME: Oilpan: This is to avoid a reference cycle that keeps Elements alive + // and won't be needed once the Node hierarchy becomes traceable. + Vector<Animation*> m_animations; +#endif + // CSSAnimations checks if a style change is due to animation. friend class CSSAnimations; }; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.cpp index f539df4d1fb..1b1705a08c9 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.cpp @@ -33,19 +33,28 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableClipPathOperation::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableClipPathOperation::usesDefaultInterpolationWith(const AnimatableValue* value) const { const AnimatableClipPathOperation* toOperation = toAnimatableClipPathOperation(value); if (m_operation->type() != ClipPathOperation::SHAPE || toOperation->m_operation->type() != ClipPathOperation::SHAPE) - return defaultInterpolateTo(this, value, fraction); + return true; const BasicShape* fromShape = toShapeClipPathOperation(clipPathOperation())->basicShape(); const BasicShape* toShape = toShapeClipPathOperation(toOperation->clipPathOperation())->basicShape(); - if (!fromShape->canBlend(toShape)) + return !fromShape->canBlend(toShape); +} + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableClipPathOperation::interpolateTo(const AnimatableValue* value, double fraction) const +{ + if (usesDefaultInterpolationWith(value)) return defaultInterpolateTo(this, value, fraction); + const AnimatableClipPathOperation* toOperation = toAnimatableClipPathOperation(value); + const BasicShape* fromShape = toShapeClipPathOperation(clipPathOperation())->basicShape(); + const BasicShape* toShape = toShapeClipPathOperation(toOperation->clipPathOperation())->basicShape(); + return AnimatableClipPathOperation::create(ShapeClipPathOperation::create(toShape->blend(fromShape, fraction)).get()); } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.h index 0a2401f2bdb..0a0188d7bd8 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableClipPathOperation.h @@ -36,17 +36,20 @@ namespace WebCore { -class AnimatableClipPathOperation : public AnimatableValue { +class AnimatableClipPathOperation FINAL : public AnimatableValue { public: virtual ~AnimatableClipPathOperation() { } - static PassRefPtr<AnimatableClipPathOperation> create(ClipPathOperation* operation) + static PassRefPtrWillBeRawPtr<AnimatableClipPathOperation> create(ClipPathOperation* operation) { - return adoptRef(new AnimatableClipPathOperation(operation)); + return adoptRefWillBeNoop(new AnimatableClipPathOperation(operation)); } ClipPathOperation* clipPathOperation() const { return m_operation.get(); } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: AnimatableClipPathOperation(ClipPathOperation* operation) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.cpp index d501a81f4c1..8170b6cc801 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.cpp @@ -34,6 +34,15 @@ #include "platform/animation/AnimationUtilities.h" #include "wtf/MathExtras.h" +namespace { + +double square(double x) +{ + return x * x; +} + +} // namespace + namespace WebCore { AnimatableColorImpl::AnimatableColorImpl(float red, float green, float blue, float alpha) @@ -67,14 +76,6 @@ AnimatableColorImpl AnimatableColorImpl::interpolateTo(const AnimatableColorImpl blend(m_alpha, to.m_alpha, fraction)); } -AnimatableColorImpl AnimatableColorImpl::addWith(const AnimatableColorImpl& addend) const -{ - return AnimatableColorImpl(m_red + addend.m_red, - m_green + addend.m_green, - m_blue + addend.m_blue, - m_alpha + addend.m_alpha); -} - bool AnimatableColorImpl::operator==(const AnimatableColorImpl& other) const { return m_red == other.m_red @@ -83,29 +84,36 @@ bool AnimatableColorImpl::operator==(const AnimatableColorImpl& other) const && m_alpha == other.m_alpha; } -PassRefPtr<AnimatableColor> AnimatableColor::create(const AnimatableColorImpl& color, const AnimatableColorImpl& visitedLinkColor) +double AnimatableColorImpl::distanceTo(const AnimatableColorImpl& other) const { - return adoptRef(new AnimatableColor(color, visitedLinkColor)); + return sqrt(square(m_red - other.m_red) + + square(m_green - other.m_green) + + square(m_blue - other.m_blue) + + square(m_alpha - other.m_alpha)); } -PassRefPtr<AnimatableValue> AnimatableColor::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableColor> AnimatableColor::create(const AnimatableColorImpl& color, const AnimatableColorImpl& visitedLinkColor) +{ + return adoptRefWillBeNoop(new AnimatableColor(color, visitedLinkColor)); +} + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableColor::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableColor* color = toAnimatableColor(value); return create(m_color.interpolateTo(color->m_color, fraction), m_visitedLinkColor.interpolateTo(color->m_visitedLinkColor, fraction)); } -PassRefPtr<AnimatableValue> AnimatableColor::addWith(const AnimatableValue* value) const +bool AnimatableColor::equalTo(const AnimatableValue* value) const { const AnimatableColor* color = toAnimatableColor(value); - return create(m_color.addWith(color->m_color), - m_visitedLinkColor.addWith(color->m_visitedLinkColor)); + return m_color == color->m_color && m_visitedLinkColor == color->m_visitedLinkColor; } -bool AnimatableColor::equalTo(const AnimatableValue* value) const +double AnimatableColor::distanceTo(const AnimatableValue* value) const { const AnimatableColor* color = toAnimatableColor(value); - return m_color == color->m_color && m_visitedLinkColor == color->m_visitedLinkColor; + return m_color.distanceTo(color->m_color); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.h index 57c8fd3cfeb..87196a0aa60 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableColor.h @@ -42,8 +42,8 @@ public: AnimatableColorImpl(Color); Color toColor() const; AnimatableColorImpl interpolateTo(const AnimatableColorImpl&, double fraction) const; - AnimatableColorImpl addWith(const AnimatableColorImpl&) const; bool operator==(const AnimatableColorImpl&) const; + double distanceTo(const AnimatableColorImpl&) const; private: float m_alpha; @@ -56,15 +56,16 @@ private: // property. Currently it is used for all properties, even those which do not // support a separate 'visited link' color (eg SVG properties). This is correct // but inefficient. -class AnimatableColor : public AnimatableValue { +class AnimatableColor FINAL : public AnimatableValue { public: - static PassRefPtr<AnimatableColor> create(const AnimatableColorImpl&, const AnimatableColorImpl& visitedLinkColor); + static PassRefPtrWillBeRawPtr<AnimatableColor> create(const AnimatableColorImpl&, const AnimatableColorImpl& visitedLinkColor); Color color() const { return m_color.toColor(); } Color visitedLinkColor() const { return m_visitedLinkColor.toColor(); } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: AnimatableColor(const AnimatableColorImpl& color, const AnimatableColorImpl& visitedLinkColor) @@ -74,6 +75,7 @@ private: } virtual AnimatableType type() const OVERRIDE { return TypeColor; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; + virtual double distanceTo(const AnimatableValue*) const OVERRIDE; const AnimatableColorImpl m_color; const AnimatableColorImpl m_visitedLinkColor; }; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableColorTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableColorTest.cpp index 1e6ae355430..de4de666f37 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableColorTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableColorTest.cpp @@ -40,10 +40,8 @@ namespace { TEST(AnimationAnimatableColorTest, ToColor) { Color transparent = AnimatableColorImpl(Color::transparent).toColor(); - EXPECT_TRUE(transparent.isValid()); EXPECT_EQ(transparent.rgb(), Color::transparent); Color red = AnimatableColorImpl(Color(0xFFFF0000)).toColor(); - EXPECT_TRUE(red.isValid()); EXPECT_EQ(red.rgb(), 0xFFFF0000); } @@ -67,15 +65,16 @@ TEST(AnimationAnimatableColorTest, Interpolate) EXPECT_EQ(AnimatableColorImpl(Color(0x10204080)).interpolateTo(Color(0x104080C0), 0.5).toColor().rgb(), 0x103060A0u); } -TEST(AnimationAnimatableColorTest, Add) +TEST(AnimationAnimatableColorTest, Distance) { - EXPECT_EQ(AnimatableColorImpl(Color(0xFF012345)).addWith(Color(0xFF543210)).toColor().rgb(), 0xFF555555); - EXPECT_EQ(AnimatableColorImpl(Color(0xFF808080)).addWith(Color(0xFF808080)).toColor().rgb(), 0xFFFFFFFF); - EXPECT_EQ(AnimatableColorImpl(Color(0x80FFFFFF)).addWith(Color(0x80FFFFFF)).toColor().rgb(), 0xFFFFFFFF); - EXPECT_EQ(AnimatableColorImpl(Color(0x40FFFFFF)).addWith(Color(0x40FFFFFF)).toColor().rgb(), 0x80FFFFFF); - EXPECT_EQ(AnimatableColorImpl(Color(0x40004080)).addWith(Color(0x80804000)).toColor().rgb(), 0xC055402B); - EXPECT_EQ(AnimatableColorImpl(Color(0x10204080)).addWith(Color(0x104080C0)).toColor().rgb(), 0x203060A0u); -} + EXPECT_NEAR(1.0, AnimatableColorImpl(Color(0xFF000000)).distanceTo(Color(0xFFFF0000)), 0.00000001); + EXPECT_NEAR(13.0 / 255, AnimatableColorImpl(Color(0xFF53647C)).distanceTo(Color(0xFF506070)), 0.00000001); + EXPECT_NEAR(60.0 / 255, AnimatableColorImpl(Color(0x3C000000)).distanceTo(Color(0x00FFFFFF)), 0.00000001); + EXPECT_NEAR(60.0 / 255, AnimatableColorImpl(Color(0x3C000000)).distanceTo(Color(0x3C00FF00)), 0.00000001); + RefPtrWillBeRawPtr<AnimatableColor> first = AnimatableColor::create(AnimatableColorImpl(Color(0xFF53647C)), AnimatableColorImpl(Color(0xFF000000))); + RefPtrWillBeRawPtr<AnimatableColor> second = AnimatableColor::create(AnimatableColorImpl(Color(0xFF506070)), AnimatableColorImpl(Color(0xFF000000))); + EXPECT_NEAR(13.0 / 255, AnimatableValue::distance(first.get(), second.get()), 0.00000001); } +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.cpp index 0e6cc0e2851..b27e515056f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.cpp @@ -31,18 +31,18 @@ #include "config.h" #include "core/animation/AnimatableDouble.h" -#include "core/css/CSSPrimitiveValue.h" -#include "core/css/CSSValuePool.h" #include "platform/animation/AnimationUtilities.h" +#include <math.h> namespace WebCore { -PassRefPtr<CSSValue> AnimatableDouble::toCSSValue() const +bool AnimatableDouble::usesDefaultInterpolationWith(const AnimatableValue* value) const { - return cssValuePool().createValue(m_number, CSSPrimitiveValue::CSS_NUMBER); + const AnimatableDouble* other = toAnimatableDouble(value); + return (m_constraint == InterpolationIsNonContinuousWithZero) && (!m_number || !other->m_number); } -PassRefPtr<AnimatableValue> AnimatableDouble::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableDouble::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableDouble* other = toAnimatableDouble(value); ASSERT(m_constraint == other->m_constraint); @@ -51,21 +51,15 @@ PassRefPtr<AnimatableValue> AnimatableDouble::interpolateTo(const AnimatableValu return AnimatableDouble::create(blend(m_number, other->m_number, fraction)); } -PassRefPtr<AnimatableValue> AnimatableDouble::addWith(const AnimatableValue* value) const +bool AnimatableDouble::equalTo(const AnimatableValue* value) const { - // Optimization for adding with 0. - if (!m_number) - return takeConstRef(value); - const AnimatableDouble* other = toAnimatableDouble(value); - if (!other->m_number) - return takeConstRef(this); - - return AnimatableDouble::create(m_number + other->m_number); + return m_number == toAnimatableDouble(value)->m_number; } -bool AnimatableDouble::equalTo(const AnimatableValue* value) const +double AnimatableDouble::distanceTo(const AnimatableValue* value) const { - return m_number == toAnimatableDouble(value)->m_number; + const AnimatableDouble* other = toAnimatableDouble(value); + return fabs(m_number - other->m_number); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.h index b2b71165a6a..88ef247c4e5 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableDouble.h @@ -32,11 +32,10 @@ #define AnimatableDouble_h #include "core/animation/AnimatableValue.h" -#include "core/css/CSSValue.h" namespace WebCore { -class AnimatableDouble : public AnimatableValue { +class AnimatableDouble FINAL : public AnimatableValue { public: virtual ~AnimatableDouble() { } @@ -45,17 +44,18 @@ public: InterpolationIsNonContinuousWithZero, }; - static PassRefPtr<AnimatableDouble> create(double number, Constraint constraint = Unconstrained) + static PassRefPtrWillBeRawPtr<AnimatableDouble> create(double number, Constraint constraint = Unconstrained) { - return adoptRef(new AnimatableDouble(number, constraint)); + return adoptRefWillBeNoop(new AnimatableDouble(number, constraint)); } - PassRefPtr<CSSValue> toCSSValue() const; double toDouble() const { return m_number; } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: AnimatableDouble(double number, Constraint constraint) @@ -65,6 +65,7 @@ private: } virtual AnimatableType type() const OVERRIDE { return TypeDouble; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; + virtual double distanceTo(const AnimatableValue*) const OVERRIDE; double m_number; Constraint m_constraint; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableDoubleTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableDoubleTest.cpp index ce8510bd544..f1948fb18b8 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableDoubleTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableDoubleTest.cpp @@ -31,8 +31,6 @@ #include "config.h" #include "core/animation/AnimatableDouble.h" -#include "core/css/CSSPrimitiveValue.h" - #include <gtest/gtest.h> using namespace WebCore; @@ -51,14 +49,6 @@ TEST(AnimationAnimatableDoubleTest, Equal) EXPECT_FALSE(AnimatableDouble::create(5)->equals(AnimatableDouble::create(10).get())); } -TEST(AnimationAnimatableDoubleTest, ToCSSValue) -{ - RefPtr<CSSValue> cssValue5 = CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_NUMBER); - RefPtr<CSSValue> cssValue10 = CSSPrimitiveValue::create(10, CSSPrimitiveValue::CSS_NUMBER); - EXPECT_TRUE(AnimatableDouble::create(5)->toCSSValue()->equals(*cssValue5.get())); - EXPECT_FALSE(AnimatableDouble::create(5)->toCSSValue()->equals(*cssValue10.get())); -} - TEST(AnimationAnimatableDoubleTest, ToDouble) { EXPECT_EQ(5.9, AnimatableDouble::create(5.9)->toDouble()); @@ -68,8 +58,8 @@ TEST(AnimationAnimatableDoubleTest, ToDouble) TEST(AnimationAnimatableDoubleTest, Interpolate) { - RefPtr<AnimatableDouble> from10 = AnimatableDouble::create(10); - RefPtr<AnimatableDouble> to20 = AnimatableDouble::create(20); + RefPtrWillBeRawPtr<AnimatableDouble> from10 = AnimatableDouble::create(10); + RefPtrWillBeRawPtr<AnimatableDouble> to20 = AnimatableDouble::create(20); EXPECT_EQ(5, toAnimatableDouble(AnimatableValue::interpolate(from10.get(), to20.get(), -0.5).get())->toDouble()); EXPECT_EQ(10, toAnimatableDouble(AnimatableValue::interpolate(from10.get(), to20.get(), 0).get())->toDouble()); EXPECT_EQ(14, toAnimatableDouble(AnimatableValue::interpolate(from10.get(), to20.get(), 0.4).get())->toDouble()); @@ -79,13 +69,15 @@ TEST(AnimationAnimatableDoubleTest, Interpolate) EXPECT_EQ(25, toAnimatableDouble(AnimatableValue::interpolate(from10.get(), to20.get(), 1.5).get())->toDouble()); } -TEST(AnimationAnimatableDoubleTest, Add) +TEST(AnimationAnimatableDoubleTest, Distance) { - EXPECT_EQ(-10, toAnimatableDouble(AnimatableValue::add(AnimatableDouble::create(-2).get(), AnimatableDouble::create(-8).get()).get())->toDouble()); - EXPECT_EQ(0, toAnimatableDouble(AnimatableValue::add(AnimatableDouble::create(50).get(), AnimatableDouble::create(-50).get()).get())->toDouble()); - EXPECT_EQ(10, toAnimatableDouble(AnimatableValue::add(AnimatableDouble::create(4).get(), AnimatableDouble::create(6).get()).get())->toDouble()); - EXPECT_EQ(20, toAnimatableDouble(AnimatableValue::add(AnimatableDouble::create(0).get(), AnimatableDouble::create(20).get()).get())->toDouble()); - EXPECT_EQ(30, toAnimatableDouble(AnimatableValue::add(AnimatableDouble::create(30).get(), AnimatableDouble::create(0).get()).get())->toDouble()); + RefPtrWillBeRawPtr<AnimatableDouble> first = AnimatableDouble::create(-1.5); + RefPtrWillBeRawPtr<AnimatableDouble> second = AnimatableDouble::create(2.25); + RefPtrWillBeRawPtr<AnimatableDouble> third = AnimatableDouble::create(3); + + EXPECT_DOUBLE_EQ(3.75, AnimatableValue::distance(first.get(), second.get())); + EXPECT_DOUBLE_EQ(0.75, AnimatableValue::distance(second.get(), third.get())); + EXPECT_DOUBLE_EQ(4.5, AnimatableValue::distance(third.get(), first.get())); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.cpp index 31edeabb24b..d0aebf007d4 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.cpp @@ -35,12 +35,18 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableFilterOperations::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableFilterOperations::usesDefaultInterpolationWith(const AnimatableValue* value) const { const AnimatableFilterOperations* target = toAnimatableFilterOperations(value); - if (!operations().canInterpolateWith(target->operations())) + return !operations().canInterpolateWith(target->operations()); +} + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableFilterOperations::interpolateTo(const AnimatableValue* value, double fraction) const +{ + if (usesDefaultInterpolationWith(value)) return defaultInterpolateTo(this, value, fraction); + const AnimatableFilterOperations* target = toAnimatableFilterOperations(value); FilterOperations result; size_t fromSize = operations().size(); size_t toSize = target->operations().size(); @@ -57,12 +63,6 @@ PassRefPtr<AnimatableValue> AnimatableFilterOperations::interpolateTo(const Anim return AnimatableFilterOperations::create(result); } -PassRefPtr<AnimatableValue> AnimatableFilterOperations::addWith(const AnimatableValue* value) const -{ - ASSERT_WITH_MESSAGE(false, "Web Animations not yet implemented: AnimatableFilterOperations::addWith()"); - return defaultAddWith(this, value); -} - bool AnimatableFilterOperations::equalTo(const AnimatableValue* value) const { return operations() == toAnimatableFilterOperations(value)->operations(); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.h index 17c694474ce..e047201652f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableFilterOperations.h @@ -36,18 +36,20 @@ namespace WebCore { -class AnimatableFilterOperations : public AnimatableValue { +class AnimatableFilterOperations FINAL : public AnimatableValue { public: virtual ~AnimatableFilterOperations() { } - static PassRefPtr<AnimatableFilterOperations> create(const FilterOperations& operations) + static PassRefPtrWillBeRawPtr<AnimatableFilterOperations> create(const FilterOperations& operations) { - return adoptRef(new AnimatableFilterOperations(operations)); + return adoptRefWillBeNoop(new AnimatableFilterOperations(operations)); } const FilterOperations& operations() const { return m_operations; } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: AnimatableFilterOperations(const FilterOperations& operations) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.cpp index eee86c39987..a347e82696f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.cpp @@ -37,45 +37,32 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableImage::interpolateTo(const AnimatableValue* value, double fraction) const +// FIXME: Once cross-fade works on generated image types, remove this method. +bool AnimatableImage::usesDefaultInterpolationWith(const AnimatableValue* value) const { - if (fraction <= 0 || fraction >= 1) - return defaultInterpolateTo(this, value, fraction); - RefPtr<CSSValue> fromValue = this->toCSSValue(); - // FIXME: Once cross-fade works on generated image types, remove this check. - if (fromValue->isImageGeneratorValue()) - return defaultInterpolateTo(this, value, fraction); - if (!fromValue->isImageValue() && !fromValue->isImageGeneratorValue()) { - if (!m_image->isImageResource()) - return defaultInterpolateTo(this, value, fraction); - ImageResource* resource = static_cast<ImageResource*>(m_image->data()); - fromValue = CSSImageValue::create(resource->url(), m_image.get()); - } - const AnimatableImage* image = toAnimatableImage(value); - RefPtr<CSSValue> toValue = image->toCSSValue(); - // FIXME: Once cross-fade works on generated image types, remove this check. - if (toValue->isImageGeneratorValue()) - return defaultInterpolateTo(this, value, fraction); - if (!toValue->isImageValue() && !toValue->isImageGeneratorValue()) { - if (!image->m_image->isImageResource()) - return defaultInterpolateTo(this, value, fraction); - ImageResource* resource = static_cast<ImageResource*>(image->m_image->data()); - toValue = CSSImageValue::create(resource->url(), image->m_image.get()); - } - RefPtr<CSSCrossfadeValue> crossfadeValue = CSSCrossfadeValue::create(fromValue, toValue); - crossfadeValue->setPercentage(CSSPrimitiveValue::create(fraction, CSSPrimitiveValue::CSS_NUMBER)); - return create(StyleGeneratedImage::create(crossfadeValue.get()).get()); + if (!m_value->isImageValue()) + return true; + if (!toAnimatableImage(value)->toCSSValue()->isImageValue()) + return true; + return false; } -PassRefPtr<AnimatableValue> AnimatableImage::addWith(const AnimatableValue* value) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableImage::interpolateTo(const AnimatableValue* value, double fraction) const { - // FIXME: Correct procedure is defined here: http://dev.w3.org/fxtf/web-animations/#the--image--type - return defaultAddWith(this, value); + if (fraction <= 0 || fraction >= 1 || usesDefaultInterpolationWith(value)) + return defaultInterpolateTo(this, value, fraction); + + CSSValue* fromValue = toCSSValue(); + CSSValue* toValue = toAnimatableImage(value)->toCSSValue(); + + RefPtrWillBeRawPtr<CSSCrossfadeValue> crossfadeValue = CSSCrossfadeValue::create(fromValue, toValue); + crossfadeValue->setPercentage(CSSPrimitiveValue::create(fraction, CSSPrimitiveValue::CSS_NUMBER)); + return create(crossfadeValue); } bool AnimatableImage::equalTo(const AnimatableValue* value) const { - return StyleImage::imagesEquivalent(m_image.get(), toAnimatableImage(value)->m_image.get()); + return m_value->equals(*toAnimatableImage(value)->m_value.get()); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.h index 40152f95c5e..e76b6196e8c 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableImage.h @@ -37,30 +37,35 @@ namespace WebCore { -class AnimatableImage : public AnimatableValue { +class AnimatableImage FINAL : public AnimatableValue { public: virtual ~AnimatableImage() { } - static PassRefPtr<AnimatableImage> create(StyleImage* image) + static PassRefPtrWillBeRawPtr<AnimatableImage> create(PassRefPtrWillBeRawPtr<CSSValue> value) { - return adoptRef(new AnimatableImage(image)); + return adoptRefWillBeNoop(new AnimatableImage(value)); + } + CSSValue* toCSSValue() const { return m_value.get(); } + + virtual void trace(Visitor* visitor) OVERRIDE + { + visitor->trace(m_value); + AnimatableValue::trace(visitor); } - PassRefPtr<CSSValue> toCSSValue() const { return m_image->cssValue(); } - StyleImage* toStyleImage() const { return m_image.get(); } protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: - AnimatableImage(StyleImage* image) - : m_image(image) + AnimatableImage(PassRefPtrWillBeRawPtr<CSSValue> value) + : m_value(value) { - ASSERT(m_image); + ASSERT(m_value.get()); } virtual AnimatableType type() const OVERRIDE { return TypeImage; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - const RefPtr<StyleImage> m_image; + const RefPtrWillBeMember<CSSValue> m_value; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableImage, isImage()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.cpp index 38a46590dbb..c167c20a752 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.cpp @@ -31,211 +31,53 @@ #include "config.h" #include "core/animation/AnimatableLength.h" -#include "core/css/CSSPrimitiveValueMappings.h" #include "platform/CalculationValue.h" #include "platform/animation/AnimationUtilities.h" namespace WebCore { -PassRefPtr<AnimatableLength> AnimatableLength::create(CSSValue* value) -{ - ASSERT(canCreateFrom(value)); - if (value->isPrimitiveValue()) { - CSSPrimitiveValue* primitiveValue = WebCore::toCSSPrimitiveValue(value); - const CSSCalcValue* calcValue = primitiveValue->cssCalcValue(); - if (calcValue) - return create(calcValue->expressionNode(), primitiveValue); - NumberUnitType unitType; - bool isPrimitiveLength = primitiveUnitToNumberType(primitiveValue->primitiveType(), unitType); - ASSERT_UNUSED(isPrimitiveLength, isPrimitiveLength); - const double scale = CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(primitiveValue->primitiveType()); - return create(primitiveValue->getDoubleValue() * scale, unitType, primitiveValue); - } - - if (value->isCalcValue()) - return create(toCSSCalcValue(value)->expressionNode()); - - ASSERT_NOT_REACHED(); - return 0; -} +namespace { -bool AnimatableLength::canCreateFrom(const CSSValue* value) +double clampNumber(double value, ValueRange range) { - ASSERT(value); - if (value->isPrimitiveValue()) { - const CSSPrimitiveValue* primitiveValue = WebCore::toCSSPrimitiveValue(value); - if (primitiveValue->cssCalcValue()) - return true; - - NumberUnitType unitType; - // Only returns true if the type is a primitive length unit. - return primitiveUnitToNumberType(primitiveValue->primitiveType(), unitType); - } - return value->isCalcValue(); + if (range == ValueRangeNonNegative) + return std::max(value, 0.0); + ASSERT(range == ValueRangeAll); + return value; } -PassRefPtr<CSSValue> AnimatableLength::toCSSValue(NumberRange range) const -{ - return toCSSPrimitiveValue(range); -} +} // namespace -Length AnimatableLength::toLength(const CSSToLengthConversionData& conversionData, NumberRange range) const +AnimatableLength::AnimatableLength(const Length& length, float zoom) { - // Avoid creating a CSSValue in the common cases - if (m_unitType == UnitTypePixels) - return Length(clampedNumber(range) * conversionData.zoom(), Fixed); - if (m_unitType == UnitTypePercentage) - return Length(clampedNumber(range), Percent); - - return toCSSPrimitiveValue(range)->convertToLength<AnyConversion>(conversionData); + ASSERT(zoom); + PixelsAndPercent pixelsAndPercent = length.pixelsAndPercent(); + m_pixels = pixelsAndPercent.pixels / zoom; + m_percent = pixelsAndPercent.percent; + m_hasPixels = length.type() != Percent; + m_hasPercent = !length.isFixed(); } -PassRefPtr<AnimatableValue> AnimatableLength::interpolateTo(const AnimatableValue* value, double fraction) const +Length AnimatableLength::length(float zoom, ValueRange range) const { - const AnimatableLength* length = toAnimatableLength(value); - NumberUnitType type = commonUnitType(length); - if (type != UnitTypeCalc) - return AnimatableLength::create(blend(m_number, length->m_number, fraction), type); - - // FIXME(crbug.com/168840): Support for viewport units in calc needs to be added before we can blend them with other units. - if (isViewportUnit() || length->isViewportUnit()) - return defaultInterpolateTo(this, value, fraction); - - return AnimatableLength::create(scale(1 - fraction).get(), length->scale(fraction).get()); + if (!m_hasPercent) + return Length(clampNumber(m_pixels, range) * zoom, Fixed); + if (!m_hasPixels) + return Length(clampNumber(m_percent, range), Percent); + return Length(CalculationValue::create(PixelsAndPercent(m_pixels * zoom, m_percent), range)); } -PassRefPtr<AnimatableValue> AnimatableLength::addWith(const AnimatableValue* value) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableLength::interpolateTo(const AnimatableValue* value, double fraction) const { - // Optimization for adding with 0. - if (isUnitlessZero()) - return takeConstRef(value); - const AnimatableLength* length = toAnimatableLength(value); - if (length->isUnitlessZero()) - return takeConstRef(this); - - NumberUnitType type = commonUnitType(length); - if (type != UnitTypeCalc) - return AnimatableLength::create(m_number + length->m_number, type); - - return AnimatableLength::create(this, length); + return create(blend(m_pixels, length->m_pixels, fraction), blend(m_percent, length->m_percent, fraction), + m_hasPixels || length->m_hasPixels, m_hasPercent || length->m_hasPercent); } bool AnimatableLength::equalTo(const AnimatableValue* value) const { const AnimatableLength* length = toAnimatableLength(value); - if (m_unitType != length->m_unitType) - return false; - if (isCalc()) - return m_calcExpression == length->m_calcExpression || m_calcExpression->equals(*length->m_calcExpression); - return m_number == length->m_number; -} - -PassRefPtr<CSSCalcExpressionNode> AnimatableLength::toCSSCalcExpressionNode() const -{ - if (isCalc()) - return m_calcExpression; - return CSSCalcValue::createExpressionNode(toCSSPrimitiveValue(AllValues), m_number == trunc(m_number)); -} - -static bool isCompatibleWithRange(const CSSPrimitiveValue* primitiveValue, NumberRange range) -{ - ASSERT(primitiveValue); - if (range == AllValues) - return true; - if (primitiveValue->isCalculated()) - return primitiveValue->cssCalcValue()->permittedValueRange() == ValueRangeNonNegative; - return primitiveValue->getDoubleValue() >= 0; -} - -PassRefPtr<CSSPrimitiveValue> AnimatableLength::toCSSPrimitiveValue(NumberRange range) const -{ - if (!m_cachedCSSPrimitiveValue || !isCompatibleWithRange(m_cachedCSSPrimitiveValue.get(), range)) { - if (isCalc()) - m_cachedCSSPrimitiveValue = CSSPrimitiveValue::create(CSSCalcValue::create(m_calcExpression, range == AllValues ? ValueRangeAll : ValueRangeNonNegative)); - else - m_cachedCSSPrimitiveValue = CSSPrimitiveValue::create(clampedNumber(range), static_cast<CSSPrimitiveValue::UnitTypes>(numberTypeToPrimitiveUnit(m_unitType))); - } - return m_cachedCSSPrimitiveValue; -} - -PassRefPtr<AnimatableLength> AnimatableLength::scale(double factor) const -{ - if (isCalc()) { - return AnimatableLength::create(CSSCalcValue::createExpressionNode( - m_calcExpression, - CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(factor, CSSPrimitiveValue::CSS_NUMBER)), - CalcMultiply)); - } - return AnimatableLength::create(m_number * factor, m_unitType); -} - -bool AnimatableLength::primitiveUnitToNumberType(unsigned short primitiveUnit, NumberUnitType& numberType) -{ - switch (primitiveUnit) { - case CSSPrimitiveValue::CSS_PX: - case CSSPrimitiveValue::CSS_CM: - case CSSPrimitiveValue::CSS_MM: - case CSSPrimitiveValue::CSS_IN: - case CSSPrimitiveValue::CSS_PT: - case CSSPrimitiveValue::CSS_PC: - numberType = UnitTypePixels; - return true; - case CSSPrimitiveValue::CSS_EMS: - numberType = UnitTypeFontSize; - return true; - case CSSPrimitiveValue::CSS_EXS: - numberType = UnitTypeFontXSize; - return true; - case CSSPrimitiveValue::CSS_REMS: - numberType = UnitTypeRootFontSize; - return true; - case CSSPrimitiveValue::CSS_PERCENTAGE: - numberType = UnitTypePercentage; - return true; - case CSSPrimitiveValue::CSS_VW: - numberType = UnitTypeViewportWidth; - return true; - case CSSPrimitiveValue::CSS_VH: - numberType = UnitTypeViewportHeight; - return true; - case CSSPrimitiveValue::CSS_VMIN: - numberType = UnitTypeViewportMin; - return true; - case CSSPrimitiveValue::CSS_VMAX: - numberType = UnitTypeViewportMax; - return true; - default: - return false; - } -} - -unsigned short AnimatableLength::numberTypeToPrimitiveUnit(NumberUnitType numberType) -{ - switch (numberType) { - case UnitTypePixels: - return CSSPrimitiveValue::CSS_PX; - case UnitTypeFontSize: - return CSSPrimitiveValue::CSS_EMS; - case UnitTypeFontXSize: - return CSSPrimitiveValue::CSS_EXS; - case UnitTypeRootFontSize: - return CSSPrimitiveValue::CSS_REMS; - case UnitTypePercentage: - return CSSPrimitiveValue::CSS_PERCENTAGE; - case UnitTypeViewportWidth: - return CSSPrimitiveValue::CSS_VW; - case UnitTypeViewportHeight: - return CSSPrimitiveValue::CSS_VH; - case UnitTypeViewportMin: - return CSSPrimitiveValue::CSS_VMIN; - case UnitTypeViewportMax: - return CSSPrimitiveValue::CSS_VMAX; - case UnitTypeCalc: - return CSSPrimitiveValue::CSS_UNKNOWN; - } - ASSERT_NOT_REACHED(); - return CSSPrimitiveValue::CSS_UNKNOWN; + return m_pixels == length->m_pixels && m_percent == length->m_percent && m_hasPixels == length->m_hasPixels && m_hasPercent == length->m_hasPercent; } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.h index 14745761248..754c0436230 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLength.h @@ -32,131 +32,44 @@ #define AnimatableLength_h #include "core/animation/AnimatableValue.h" -#include "core/css/CSSCalculationValue.h" -#include "core/css/CSSPrimitiveValue.h" #include "platform/Length.h" namespace WebCore { -enum NumberRange { - AllValues, - NonNegativeValues, -}; - -// Handles animation of CSS length and percentage values including CSS calc. -// See primitiveUnitToNumberType() for the list of supported units. -// If created from a CSSPrimitiveValue this class will cache it to be returned in toCSSValue(). -class AnimatableLength : public AnimatableValue { +class AnimatableLength FINAL : public AnimatableValue { public: - enum NumberUnitType { - UnitTypeCalc, - UnitTypePixels, - UnitTypePercentage, - UnitTypeFontSize, - UnitTypeFontXSize, - UnitTypeRootFontSize, - UnitTypeViewportWidth, - UnitTypeViewportHeight, - UnitTypeViewportMin, - UnitTypeViewportMax, - }; - - virtual ~AnimatableLength() { } - static bool canCreateFrom(const CSSValue*); - static PassRefPtr<AnimatableLength> create(CSSValue*); - static PassRefPtr<AnimatableLength> create(double number, NumberUnitType unitType, CSSPrimitiveValue* cssPrimitiveValue = 0) - { - return adoptRef(new AnimatableLength(number, unitType, cssPrimitiveValue)); - } - static PassRefPtr<AnimatableLength> create(PassRefPtr<CSSCalcExpressionNode> calcExpression, CSSPrimitiveValue* cssPrimitiveValue = 0) + static PassRefPtrWillBeRawPtr<AnimatableLength> create(const Length& length, float zoom) { - return adoptRef(new AnimatableLength(calcExpression, cssPrimitiveValue)); + return adoptRefWillBeNoop(new AnimatableLength(length, zoom)); } - PassRefPtr<CSSValue> toCSSValue(NumberRange = AllValues) const; - Length toLength(const CSSToLengthConversionData&, NumberRange = AllValues) const; + Length length(float zoom, ValueRange) const; protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: - AnimatableLength(double number, NumberUnitType unitType, CSSPrimitiveValue* cssPrimitiveValue) - : m_number(number) - , m_unitType(unitType) - , m_cachedCSSPrimitiveValue(cssPrimitiveValue) + static PassRefPtrWillBeRawPtr<AnimatableLength> create(double pixels, double percent, bool hasPixels, bool hasPercent) { - ASSERT(m_unitType != UnitTypeCalc); + return adoptRefWillBeNoop(new AnimatableLength(pixels, percent, hasPixels, hasPercent)); } - AnimatableLength(PassRefPtr<CSSCalcExpressionNode> calcExpression, CSSPrimitiveValue* cssPrimitiveValue) - : m_unitType(UnitTypeCalc) - , m_calcExpression(calcExpression) - , m_cachedCSSPrimitiveValue(cssPrimitiveValue) + AnimatableLength(const Length&, float zoom); + AnimatableLength(double pixels, double percent, bool hasPixels, bool hasPercent) + : m_pixels(pixels) + , m_percent(percent) + , m_hasPixels(hasPixels) + , m_hasPercent(hasPercent) { - ASSERT(m_calcExpression); + ASSERT(m_hasPixels || m_hasPercent); } virtual AnimatableType type() const OVERRIDE { return TypeLength; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - bool isCalc() const - { - return m_unitType == UnitTypeCalc; - } - - bool isViewportUnit() const - { - return m_unitType == UnitTypeViewportWidth || m_unitType == UnitTypeViewportHeight || m_unitType == UnitTypeViewportMin || m_unitType == UnitTypeViewportMax; - } - - static PassRefPtr<AnimatableLength> create(const AnimatableLength* leftAddend, const AnimatableLength* rightAddend) - { - ASSERT(leftAddend && rightAddend); - return create(CSSCalcValue::createExpressionNode(leftAddend->toCSSCalcExpressionNode(), rightAddend->toCSSCalcExpressionNode(), CalcAdd)); - } - - PassRefPtr<CSSPrimitiveValue> toCSSPrimitiveValue(NumberRange) const; - PassRefPtr<CSSCalcExpressionNode> toCSSCalcExpressionNode() const; - - PassRefPtr<AnimatableLength> scale(double) const; - double clampedNumber(NumberRange range) const - { - ASSERT(!isCalc()); - return (range == NonNegativeValues && m_number <= 0) ? 0 : m_number; - } - - // Returns true and populates numberType, if primitiveUnit is a primitive length unit. Otherwise, returns false. - static bool primitiveUnitToNumberType(unsigned short primitiveUnit, NumberUnitType& numberType); - - static unsigned short numberTypeToPrimitiveUnit(NumberUnitType numberType); - - // Zero is effectively unitless, except in the case of percentage. - // http://www.w3.org/TR/css3-values/#calc-computed-value - // e.g. calc(100% - 100% + 1em) resolves to calc(0% + 1em), not to calc(1em) - bool isUnitlessZero() const - { - return !isCalc() && !m_number && m_unitType != UnitTypePercentage; - } - - NumberUnitType commonUnitType(const AnimatableLength* length) const - { - if (m_unitType == length->m_unitType) - return m_unitType; - - if (isUnitlessZero()) - return length->m_unitType; - if (length->isUnitlessZero()) - return m_unitType; - - return UnitTypeCalc; - } - - double m_number; - const NumberUnitType m_unitType; - - RefPtr<CSSCalcExpressionNode> m_calcExpression; - - mutable RefPtr<CSSPrimitiveValue> m_cachedCSSPrimitiveValue; + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } - friend class AnimationAnimatableLengthTest; + double m_pixels; + double m_percent; + bool m_hasPixels; + bool m_hasPercent; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableLength, isLength()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.cpp index 825a1351655..25896210c9e 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.cpp @@ -33,7 +33,7 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableLengthBox::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableLengthBox::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableLengthBox* lengthBox = toAnimatableLengthBox(value); return AnimatableLengthBox::create( @@ -43,16 +43,6 @@ PassRefPtr<AnimatableValue> AnimatableLengthBox::interpolateTo(const AnimatableV AnimatableValue::interpolate(this->bottom(), lengthBox->bottom(), fraction)); } -PassRefPtr<AnimatableValue> AnimatableLengthBox::addWith(const AnimatableValue* value) const -{ - const AnimatableLengthBox* lengthBox = toAnimatableLengthBox(value); - return AnimatableLengthBox::create( - AnimatableValue::add(this->left(), lengthBox->left()), - AnimatableValue::add(this->right(), lengthBox->right()), - AnimatableValue::add(this->top(), lengthBox->top()), - AnimatableValue::add(this->bottom(), lengthBox->bottom())); -} - bool AnimatableLengthBox::equalTo(const AnimatableValue* value) const { const AnimatableLengthBox* lengthBox = toAnimatableLengthBox(value); @@ -62,4 +52,13 @@ bool AnimatableLengthBox::equalTo(const AnimatableValue* value) const && bottom()->equals(lengthBox->bottom()); } +void AnimatableLengthBox::trace(Visitor* visitor) +{ + visitor->trace(m_left); + visitor->trace(m_right); + visitor->trace(m_top); + visitor->trace(m_bottom); + AnimatableValue::trace(visitor); +} + } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.h index b7ea5be5f6a..f796e753bc3 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBox.h @@ -35,24 +35,25 @@ namespace WebCore { -class AnimatableLengthBox : public AnimatableValue { +class AnimatableLengthBox FINAL : public AnimatableValue { public: virtual ~AnimatableLengthBox() { } - static PassRefPtr<AnimatableLengthBox> create(PassRefPtr<AnimatableValue> left, PassRefPtr<AnimatableValue> right, PassRefPtr<AnimatableValue> top, PassRefPtr<AnimatableValue> bottom) + static PassRefPtrWillBeRawPtr<AnimatableLengthBox> create(PassRefPtrWillBeRawPtr<AnimatableValue> left, PassRefPtrWillBeRawPtr<AnimatableValue> right, PassRefPtrWillBeRawPtr<AnimatableValue> top, PassRefPtrWillBeRawPtr<AnimatableValue> bottom) { - return adoptRef(new AnimatableLengthBox(left, right, top, bottom)); + return adoptRefWillBeNoop(new AnimatableLengthBox(left, right, top, bottom)); } const AnimatableValue* left() const { return m_left.get(); } const AnimatableValue* right() const { return m_right.get(); } const AnimatableValue* top() const { return m_top.get(); } const AnimatableValue* bottom() const { return m_bottom.get(); } + virtual void trace(Visitor*) OVERRIDE; + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: - AnimatableLengthBox(PassRefPtr<AnimatableValue> left, PassRefPtr<AnimatableValue> right, PassRefPtr<AnimatableValue> top, PassRefPtr<AnimatableValue> bottom) + AnimatableLengthBox(PassRefPtrWillBeRawPtr<AnimatableValue> left, PassRefPtrWillBeRawPtr<AnimatableValue> right, PassRefPtrWillBeRawPtr<AnimatableValue> top, PassRefPtrWillBeRawPtr<AnimatableValue> bottom) : m_left(left) , m_right(right) , m_top(top) @@ -62,10 +63,10 @@ private: virtual AnimatableType type() const OVERRIDE { return TypeLengthBox; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - RefPtr<AnimatableValue> m_left; - RefPtr<AnimatableValue> m_right; - RefPtr<AnimatableValue> m_top; - RefPtr<AnimatableValue> m_bottom; + RefPtrWillBeMember<AnimatableValue> m_left; + RefPtrWillBeMember<AnimatableValue> m_right; + RefPtrWillBeMember<AnimatableValue> m_top; + RefPtrWillBeMember<AnimatableValue> m_bottom; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableLengthBox, isLengthBox()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.cpp index da92ac24181..c598ec435a9 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.cpp @@ -33,26 +33,23 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableLengthBoxAndBool::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableLengthBoxAndBool::usesDefaultInterpolationWith(const AnimatableValue* value) const { const AnimatableLengthBoxAndBool* lengthBox = toAnimatableLengthBoxAndBool(value); - if (lengthBox->flag() == flag()) { - return AnimatableLengthBoxAndBool::create( - AnimatableValue::interpolate(box(), lengthBox->box(), fraction), - flag()); - } - return defaultInterpolateTo(this, value, fraction); + if (lengthBox->flag() != flag()) + return true; + return AnimatableValue::usesDefaultInterpolation(lengthBox->box(), box()); } -PassRefPtr<AnimatableValue> AnimatableLengthBoxAndBool::addWith(const AnimatableValue* value) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableLengthBoxAndBool::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableLengthBoxAndBool* lengthBox = toAnimatableLengthBoxAndBool(value); if (lengthBox->flag() == flag()) { return AnimatableLengthBoxAndBool::create( - AnimatableValue::add(box(), lengthBox->box()), + AnimatableValue::interpolate(box(), lengthBox->box(), fraction), flag()); } - return defaultAddWith(this, value); + return defaultInterpolateTo(this, value, fraction); } bool AnimatableLengthBoxAndBool::equalTo(const AnimatableValue* value) const @@ -61,4 +58,10 @@ bool AnimatableLengthBoxAndBool::equalTo(const AnimatableValue* value) const return box()->equals(lengthBox->box()) && flag() == lengthBox->flag(); } +void AnimatableLengthBoxAndBool::trace(Visitor* visitor) +{ + visitor->trace(m_box); + AnimatableValue::trace(visitor); +} + } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.h index 9e3379791fd..76bd94c097e 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthBoxAndBool.h @@ -35,22 +35,24 @@ namespace WebCore { -class AnimatableLengthBoxAndBool : public AnimatableValue { +class AnimatableLengthBoxAndBool FINAL : public AnimatableValue { public: virtual ~AnimatableLengthBoxAndBool() { } - static PassRefPtr<AnimatableLengthBoxAndBool> create(PassRefPtr<AnimatableValue> box, bool flag) + static PassRefPtrWillBeRawPtr<AnimatableLengthBoxAndBool> create(PassRefPtrWillBeRawPtr<AnimatableValue> box, bool flag) { - return adoptRef(new AnimatableLengthBoxAndBool(box, flag)); + return adoptRefWillBeNoop(new AnimatableLengthBoxAndBool(box, flag)); } const AnimatableValue* box() const { return m_box.get(); } bool flag() const { return m_flag; } + virtual void trace(Visitor*) OVERRIDE; + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: - AnimatableLengthBoxAndBool(PassRefPtr<AnimatableValue> box, bool flag) + AnimatableLengthBoxAndBool(PassRefPtrWillBeRawPtr<AnimatableValue> box, bool flag) : m_box(box) , m_flag(flag) { @@ -58,7 +60,7 @@ private: virtual AnimatableType type() const OVERRIDE { return TypeLengthBoxAndBool; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - RefPtr<AnimatableValue> m_box; + RefPtrWillBeMember<AnimatableValue> m_box; bool m_flag; }; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.cpp index e779699d93d..b8805d05685 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.cpp @@ -33,7 +33,7 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableLengthPoint::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableLengthPoint::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableLengthPoint* lengthPoint = toAnimatableLengthPoint(value); return AnimatableLengthPoint::create( @@ -41,18 +41,17 @@ PassRefPtr<AnimatableValue> AnimatableLengthPoint::interpolateTo(const Animatabl AnimatableValue::interpolate(this->y(), lengthPoint->y(), fraction)); } -PassRefPtr<AnimatableValue> AnimatableLengthPoint::addWith(const AnimatableValue* value) const +bool AnimatableLengthPoint::equalTo(const AnimatableValue* value) const { const AnimatableLengthPoint* lengthPoint = toAnimatableLengthPoint(value); - return AnimatableLengthPoint::create( - AnimatableValue::add(this->x(), lengthPoint->x()), - AnimatableValue::add(this->y(), lengthPoint->y())); + return x()->equals(lengthPoint->x()) && y()->equals(lengthPoint->y()); } -bool AnimatableLengthPoint::equalTo(const AnimatableValue* value) const +void AnimatableLengthPoint::trace(Visitor* visitor) { - const AnimatableLengthPoint* lengthPoint = toAnimatableLengthPoint(value); - return x()->equals(lengthPoint->x()) && y()->equals(lengthPoint->y()); + visitor->trace(m_x); + visitor->trace(m_y); + AnimatableValue::trace(visitor); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.h index 60b3e03f152..f2f479b11ef 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint.h @@ -35,22 +35,23 @@ namespace WebCore { -class AnimatableLengthPoint : public AnimatableValue { +class AnimatableLengthPoint FINAL : public AnimatableValue { public: virtual ~AnimatableLengthPoint() { } - static PassRefPtr<AnimatableLengthPoint> create(PassRefPtr<AnimatableValue> x, PassRefPtr<AnimatableValue> y) + static PassRefPtrWillBeRawPtr<AnimatableLengthPoint> create(PassRefPtrWillBeRawPtr<AnimatableValue> x, PassRefPtrWillBeRawPtr<AnimatableValue> y) { - return adoptRef(new AnimatableLengthPoint(x, y)); + return adoptRefWillBeNoop(new AnimatableLengthPoint(x, y)); } const AnimatableValue* x() const { return m_x.get(); } const AnimatableValue* y() const { return m_y.get(); } + virtual void trace(Visitor*) OVERRIDE; + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: - AnimatableLengthPoint(PassRefPtr<AnimatableValue> x, PassRefPtr<AnimatableValue> y) + AnimatableLengthPoint(PassRefPtrWillBeRawPtr<AnimatableValue> x, PassRefPtrWillBeRawPtr<AnimatableValue> y) : m_x(x) , m_y(y) { @@ -58,8 +59,8 @@ private: virtual AnimatableType type() const OVERRIDE { return TypeLengthPoint; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - RefPtr<AnimatableValue> m_x; - RefPtr<AnimatableValue> m_y; + RefPtrWillBeMember<AnimatableValue> m_x; + RefPtrWillBeMember<AnimatableValue> m_y; }; inline const AnimatableLengthPoint* toAnimatableLengthPoint(const AnimatableValue* value) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint3D.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint3D.cpp new file mode 100644 index 00000000000..402f0a8a707 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint3D.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/AnimatableLengthPoint3D.h" + +namespace WebCore { + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableLengthPoint3D::interpolateTo(const AnimatableValue* value, double fraction) const +{ + const AnimatableLengthPoint3D* lengthPoint = toAnimatableLengthPoint3D(value); + return AnimatableLengthPoint3D::create( + AnimatableValue::interpolate(this->x(), lengthPoint->x(), fraction), + AnimatableValue::interpolate(this->y(), lengthPoint->y(), fraction), + AnimatableValue::interpolate(this->z(), lengthPoint->z(), fraction)); +} + +bool AnimatableLengthPoint3D::equalTo(const AnimatableValue* value) const +{ + const AnimatableLengthPoint3D* lengthPoint = toAnimatableLengthPoint3D(value); + return x()->equals(lengthPoint->x()) && y()->equals(lengthPoint->y()) && z()->equals(lengthPoint->z()); +} + +void AnimatableLengthPoint3D::trace(Visitor* visitor) +{ + visitor->trace(m_x); + visitor->trace(m_y); + visitor->trace(m_z); + AnimatableValue::trace(visitor); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint3D.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint3D.h new file mode 100644 index 00000000000..0e3650ffae5 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthPoint3D.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AnimatableLengthPoint3D_h +#define AnimatableLengthPoint3D_h + +#include "core/animation/AnimatableValue.h" + +namespace WebCore { + +class AnimatableLengthPoint3D FINAL : public AnimatableValue { +public: + virtual ~AnimatableLengthPoint3D() { } + static PassRefPtrWillBeRawPtr<AnimatableLengthPoint3D> create(PassRefPtrWillBeRawPtr<AnimatableValue> x, PassRefPtrWillBeRawPtr<AnimatableValue> y, PassRefPtrWillBeRawPtr<AnimatableValue> z) + { + return adoptRefWillBeNoop(new AnimatableLengthPoint3D(x, y, z)); + } + const AnimatableValue* x() const { return m_x.get(); } + const AnimatableValue* y() const { return m_y.get(); } + const AnimatableValue* z() const { return m_z.get(); } + + virtual void trace(Visitor*) OVERRIDE; + +protected: + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + +private: + AnimatableLengthPoint3D(PassRefPtrWillBeRawPtr<AnimatableValue> x, PassRefPtrWillBeRawPtr<AnimatableValue> y, PassRefPtrWillBeRawPtr<AnimatableValue> z) + : m_x(x) + , m_y(y) + , m_z(z) + { + } + virtual AnimatableType type() const OVERRIDE { return TypeLengthPoint3D; } + virtual bool equalTo(const AnimatableValue*) const OVERRIDE; + + RefPtrWillBeMember<AnimatableValue> m_x; + RefPtrWillBeMember<AnimatableValue> m_y; + RefPtrWillBeMember<AnimatableValue> m_z; +}; + +inline const AnimatableLengthPoint3D* toAnimatableLengthPoint3D(const AnimatableValue* value) +{ + ASSERT_WITH_SECURITY_IMPLICATION(value && value->isLengthPoint3D()); + return static_cast<const AnimatableLengthPoint3D*>(value); +} + +} // namespace WebCore + +#endif // AnimatableLengthPoint3D_h diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.cpp index b422f39a931..c63239536c0 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.cpp @@ -33,7 +33,7 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableLengthSize::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableLengthSize::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableLengthSize* lengthSize = toAnimatableLengthSize(value); return AnimatableLengthSize::create( @@ -41,18 +41,17 @@ PassRefPtr<AnimatableValue> AnimatableLengthSize::interpolateTo(const Animatable AnimatableValue::interpolate(this->height(), lengthSize->height(), fraction)); } -PassRefPtr<AnimatableValue> AnimatableLengthSize::addWith(const AnimatableValue* value) const +bool AnimatableLengthSize::equalTo(const AnimatableValue* value) const { const AnimatableLengthSize* lengthSize = toAnimatableLengthSize(value); - return AnimatableLengthSize::create( - AnimatableValue::add(this->width(), lengthSize->width()), - AnimatableValue::add(this->height(), lengthSize->height())); + return width()->equals(lengthSize->width()) && height()->equals(lengthSize->height()); } -bool AnimatableLengthSize::equalTo(const AnimatableValue* value) const +void AnimatableLengthSize::trace(Visitor* visitor) { - const AnimatableLengthSize* lengthSize = toAnimatableLengthSize(value); - return width()->equals(lengthSize->width()) && height()->equals(lengthSize->height()); + visitor->trace(m_width); + visitor->trace(m_height); + AnimatableValue::trace(visitor); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.h index 55dcc4090d3..a572468e18e 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthSize.h @@ -35,22 +35,23 @@ namespace WebCore { -class AnimatableLengthSize : public AnimatableValue { +class AnimatableLengthSize FINAL : public AnimatableValue { public: virtual ~AnimatableLengthSize() { } - static PassRefPtr<AnimatableLengthSize> create(PassRefPtr<AnimatableValue> width, PassRefPtr<AnimatableValue> height) + static PassRefPtrWillBeRawPtr<AnimatableLengthSize> create(PassRefPtrWillBeRawPtr<AnimatableValue> width, PassRefPtrWillBeRawPtr<AnimatableValue> height) { - return adoptRef(new AnimatableLengthSize(width, height)); + return adoptRefWillBeNoop(new AnimatableLengthSize(width, height)); } const AnimatableValue* width() const { return m_width.get(); } const AnimatableValue* height() const { return m_height.get(); } + virtual void trace(Visitor*) OVERRIDE; + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: - AnimatableLengthSize(PassRefPtr<AnimatableValue> width, PassRefPtr<AnimatableValue> height) + AnimatableLengthSize(PassRefPtrWillBeRawPtr<AnimatableValue> width, PassRefPtrWillBeRawPtr<AnimatableValue> height) : m_width(width) , m_height(height) { @@ -58,8 +59,8 @@ private: virtual AnimatableType type() const OVERRIDE { return TypeLengthSize; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - RefPtr<AnimatableValue> m_width; - RefPtr<AnimatableValue> m_height; + RefPtrWillBeMember<AnimatableValue> m_width; + RefPtrWillBeMember<AnimatableValue> m_height; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableLengthSize, isLengthSize()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthTest.cpp index 3574a66279b..c5887e239c8 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableLengthTest.cpp @@ -1,367 +1,73 @@ -/* - * Copyright (c) 2013, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "config.h" #include "core/animation/AnimatableLength.h" - -#include "core/animation/AnimatableValueTestHelper.h" -#include "core/css/CSSCalculationValue.h" -#include "core/css/CSSPrimitiveValue.h" -#include "core/css/CSSToLengthConversionData.h" -#include "core/rendering/style/RenderStyle.h" -#include "core/rendering/style/StyleInheritedData.h" #include "platform/CalculationValue.h" -#include "wtf/MathExtras.h" #include <gtest/gtest.h> -#define EXPECT_ROUNDTRIP(a, f) EXPECT_REFV_EQ(a, f(a.get())) - namespace WebCore { -class AnimationAnimatableLengthTest : public ::testing::Test { -protected: - AnimationAnimatableLengthTest() - : style(RenderStyle::createDefaultStyle()) - , conversionDataZoom1(style.get(), style.get(), 1.0f) - , conversionDataZoom3(style.get(), style.get(), 3.0f) - { - } - - PassRefPtr<AnimatableLength> create(double value, CSSPrimitiveValue::UnitTypes type) - { - return AnimatableLength::create(CSSPrimitiveValue::create(value, type).get()); - } - - PassRefPtr<AnimatableLength> create(double valueLeft, CSSPrimitiveValue::UnitTypes typeLeft, double valueRight, CSSPrimitiveValue::UnitTypes typeRight) - { - return AnimatableLength::create(createCalc(valueLeft, typeLeft, valueRight, typeRight).get()); - } - - PassRefPtr<CSSCalcValue> createCalc(double valueLeft, CSSPrimitiveValue::UnitTypes typeLeft, double valueRight, CSSPrimitiveValue::UnitTypes typeRight) - { - return CSSCalcValue::create(CSSCalcValue::createExpressionNode( - CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(valueLeft, typeLeft), valueLeft == trunc(valueLeft)), - CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(valueRight, typeRight), valueRight == trunc(valueRight)), - CalcAdd - )); - } - - PassRefPtr<CSSValue> toCSSValue(CSSValue* cssValue) - { - return AnimatableLength::create(cssValue)->toCSSValue(); - } - - AnimatableLength::NumberUnitType commonUnitType(PassRefPtr<AnimatableLength> a, PassRefPtr<AnimatableLength> b) - { - return a->commonUnitType(b.get()); - } +namespace { - bool isUnitlessZero(PassRefPtr<AnimatableLength> a) + PassRefPtrWillBeRawPtr<AnimatableLength> create(const Length& length, double zoom = 1) { - return a->isUnitlessZero(); + return AnimatableLength::create(length, zoom); } - RefPtr<RenderStyle> style; - CSSToLengthConversionData conversionDataZoom1; - CSSToLengthConversionData conversionDataZoom3; -}; - -TEST_F(AnimationAnimatableLengthTest, CanCreateFrom) -{ - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_PX).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_MM).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_IN).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_PT).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_PC).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_EMS).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_EXS).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_REMS).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_VW).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_VH).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_VMIN).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_VMAX).get())); - - EXPECT_TRUE(AnimatableLength::canCreateFrom(createCalc(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_TRUE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create(createCalc(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM)).get())); - - EXPECT_FALSE(AnimatableLength::canCreateFrom(CSSPrimitiveValue::create("NaN", CSSPrimitiveValue::CSS_STRING).get())); -} - -TEST_F(AnimationAnimatableLengthTest, Create) -{ - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_PX).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_MM).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_IN).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_PT).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_PC).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_EMS).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_EXS).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_REMS).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_VW).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_VH).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_VMIN).get())); - EXPECT_TRUE(static_cast<bool>(create(5, CSSPrimitiveValue::CSS_VMAX).get())); - - EXPECT_TRUE(static_cast<bool>( - AnimatableLength::create(createCalc(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM).get()).get() - )); - EXPECT_TRUE(static_cast<bool>( - AnimatableLength::create(CSSPrimitiveValue::create(createCalc(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM)).get()).get() - )); -} - - -TEST_F(AnimationAnimatableLengthTest, ToCSSValue) -{ - - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_PX), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_CM), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_MM), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_IN), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_PT), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_PC), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_EMS), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_EXS), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_REMS), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_PERCENTAGE), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_VW), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_VH), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_VMIN), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(-5, CSSPrimitiveValue::CSS_VMAX), toCSSValue); - - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(createCalc(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_IN)), toCSSValue); - EXPECT_ROUNDTRIP(CSSPrimitiveValue::create(createCalc(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_IN)), toCSSValue); -} - +} // namespace -TEST_F(AnimationAnimatableLengthTest, ToLength) +TEST(AnimationAnimatableLengthTest, RoundTripConversion) { - EXPECT_EQ(Length(-5, WebCore::Fixed), create(-5, CSSPrimitiveValue::CSS_PX)->toLength(conversionDataZoom1)); - EXPECT_EQ(Length(-15, WebCore::Fixed), create(-5, CSSPrimitiveValue::CSS_PX)->toLength(conversionDataZoom3)); - EXPECT_EQ(Length(0, WebCore::Fixed), create(-5, CSSPrimitiveValue::CSS_PX)->toLength(conversionDataZoom1, NonNegativeValues)); - EXPECT_EQ(Length(0, WebCore::Fixed), create(-5, CSSPrimitiveValue::CSS_PX)->toLength(conversionDataZoom3, NonNegativeValues)); - - EXPECT_EQ(Length(-5, Percent), create(-5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom1)); - EXPECT_EQ(Length(-5, Percent), create(-5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom3)); - EXPECT_EQ(Length(0, Percent), create(-5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom1, NonNegativeValues)); - EXPECT_EQ(Length(0, Percent), create(-5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom3, NonNegativeValues)); - - EXPECT_EQ( - Length(CalculationValue::create( - adoptPtr(new CalcExpressionBinaryOperation( - adoptPtr(new CalcExpressionLength(Length(-5, WebCore::Fixed))), - adoptPtr(new CalcExpressionLength(Length(-5, Percent))), - CalcAdd)), - ValueRangeAll)), - create(-5, CSSPrimitiveValue::CSS_PX, -5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom1)); - EXPECT_EQ( - Length(CalculationValue::create( - adoptPtr(new CalcExpressionBinaryOperation( - adoptPtr(new CalcExpressionLength(Length(-15, WebCore::Fixed))), - adoptPtr(new CalcExpressionLength(Length(-5, Percent))), - CalcAdd)), - ValueRangeAll)), - create(-5, CSSPrimitiveValue::CSS_PX, -5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom3)); - EXPECT_EQ( - Length(CalculationValue::create( - adoptPtr(new CalcExpressionBinaryOperation( - adoptPtr(new CalcExpressionLength(Length(-5, WebCore::Fixed))), - adoptPtr(new CalcExpressionLength(Length(-5, Percent))), - CalcAdd)), - ValueRangeNonNegative)), - create(-5, CSSPrimitiveValue::CSS_PX, -5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom1, NonNegativeValues)); - EXPECT_EQ( - Length(CalculationValue::create( - adoptPtr(new CalcExpressionBinaryOperation( - adoptPtr(new CalcExpressionLength(Length(-15, WebCore::Fixed))), - adoptPtr(new CalcExpressionLength(Length(-5, Percent))), - CalcAdd)), - ValueRangeNonNegative)), - create(-5, CSSPrimitiveValue::CSS_PX, -5, CSSPrimitiveValue::CSS_PERCENTAGE)->toLength(conversionDataZoom3, NonNegativeValues)); + EXPECT_EQ(Length(0, Fixed), create(Length(0, Fixed))->length(1, ValueRangeAll)); + EXPECT_EQ(Length(0, Percent), create(Length(0, Percent))->length(1, ValueRangeAll)); + EXPECT_EQ(Length(10, Fixed), create(Length(10, Fixed))->length(1, ValueRangeAll)); + EXPECT_EQ(Length(10, Percent), create(Length(10, Percent))->length(1, ValueRangeAll)); + EXPECT_EQ(Length(-10, Fixed), create(Length(-10, Fixed))->length(1, ValueRangeAll)); + EXPECT_EQ(Length(-10, Percent), create(Length(-10, Percent))->length(1, ValueRangeAll)); + Length calc = Length(CalculationValue::create(PixelsAndPercent(5, 10), ValueRangeAll)); + EXPECT_EQ(calc, create(calc)->length(1, ValueRangeAll)); } -TEST_F(AnimationAnimatableLengthTest, Interpolate) +TEST(AnimationAnimatableLengthTest, ValueRangeNonNegative) { - RefPtr<AnimatableLength> from10px = create(10, CSSPrimitiveValue::CSS_PX); - RefPtr<AnimatableLength> to20pxAsInches = create(20.0 / 96, CSSPrimitiveValue::CSS_IN); - - EXPECT_REFV_EQ(create(5, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), -0.5)); - - EXPECT_REFV_EQ(create(10, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), 0)); - EXPECT_REFV_EQ(create(14, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), 0.4)); - EXPECT_REFV_EQ(create(15, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), 0.5)); - EXPECT_REFV_EQ(create(16, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), 0.6)); - EXPECT_REFV_EQ(create(20.0 / 96, CSSPrimitiveValue::CSS_IN), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), 1)); - EXPECT_REFV_EQ(create(25, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from10px.get(), to20pxAsInches.get(), 1.5)); - - RefPtr<AnimatableLength> from10em = create(10, CSSPrimitiveValue::CSS_EMS); - RefPtr<AnimatableLength> to20rem = create(20, CSSPrimitiveValue::CSS_REMS); - EXPECT_REFV_EQ(create(15, CSSPrimitiveValue::CSS_EMS, -10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), -0.5)); - EXPECT_REFV_EQ(create(10, CSSPrimitiveValue::CSS_EMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), 0)); - EXPECT_REFV_EQ(create(6, CSSPrimitiveValue::CSS_EMS, 8, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), 0.4)); - EXPECT_REFV_EQ(create(5, CSSPrimitiveValue::CSS_EMS, 10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), 0.5)); - EXPECT_REFV_EQ(create(4, CSSPrimitiveValue::CSS_EMS, 12, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), 0.6)); - EXPECT_REFV_EQ(create(20, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), 1)); - EXPECT_REFV_EQ(create(-5, CSSPrimitiveValue::CSS_EMS, 30, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from10em.get(), to20rem.get(), 1.5)); - - // Zero values are typeless and hence we can don't get a calc - RefPtr<AnimatableLength> from0px = create(0, CSSPrimitiveValue::CSS_PX); - EXPECT_REFV_EQ(create(-10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0px.get(), to20rem.get(), -0.5)); - // At t=0, interpolate always returns the "from" value. - EXPECT_REFV_EQ(create(0, CSSPrimitiveValue::CSS_PX), - AnimatableValue::interpolate(from0px.get(), to20rem.get(), 0)); - EXPECT_REFV_EQ(create(10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0px.get(), to20rem.get(), 0.5)); - EXPECT_REFV_EQ(create(20, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0px.get(), to20rem.get(), 1.0)); - EXPECT_REFV_EQ(create(30, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0px.get(), to20rem.get(), 1.5)); - - // Except 0% which is special - RefPtr<AnimatableLength> from0percent = create(0, CSSPrimitiveValue::CSS_PERCENTAGE); - EXPECT_REFV_EQ(create(0, CSSPrimitiveValue::CSS_PERCENTAGE, -10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0percent.get(), to20rem.get(), -0.5)); - // At t=0, interpolate always returns the "from" value. - EXPECT_REFV_EQ(create(0, CSSPrimitiveValue::CSS_PERCENTAGE), - AnimatableValue::interpolate(from0percent.get(), to20rem.get(), 0)); - EXPECT_REFV_EQ(create(0, CSSPrimitiveValue::CSS_PERCENTAGE, 10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0percent.get(), to20rem.get(), 0.5)); - // At t=1, interpolate always returns the "to" value. - EXPECT_REFV_EQ(create(20, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0percent.get(), to20rem.get(), 1.0)); - EXPECT_REFV_EQ(create(0, CSSPrimitiveValue::CSS_PERCENTAGE, 30, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::interpolate(from0percent.get(), to20rem.get(), 1.5)); + EXPECT_EQ(Length(10, Fixed), create(Length(10, Fixed))->length(1, ValueRangeNonNegative)); + EXPECT_EQ(Length(10, Percent), create(Length(10, Percent))->length(1, ValueRangeNonNegative)); + EXPECT_EQ(Length(0, Fixed), create(Length(-10, Fixed))->length(1, ValueRangeNonNegative)); + EXPECT_EQ(Length(0, Percent), create(Length(-10, Percent))->length(1, ValueRangeNonNegative)); + Length calc = Length(CalculationValue::create(PixelsAndPercent(-5, -10), ValueRangeNonNegative)); + EXPECT_TRUE(calc == create(calc)->length(1, ValueRangeNonNegative)); } -TEST_F(AnimationAnimatableLengthTest, Add) +TEST(AnimationAnimatableLengthTest, Zoom) { - EXPECT_REFV_EQ(create(10, CSSPrimitiveValue::CSS_PX), - AnimatableValue::add(create(10, CSSPrimitiveValue::CSS_PX).get(), create(0, CSSPrimitiveValue::CSS_MM).get())); - EXPECT_REFV_EQ(create(100, CSSPrimitiveValue::CSS_PX), - AnimatableValue::add(create(4, CSSPrimitiveValue::CSS_PX).get(), create(1, CSSPrimitiveValue::CSS_IN).get())); - EXPECT_REFV_EQ( - create(10, CSSPrimitiveValue::CSS_EMS, 20, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::add(create(10, CSSPrimitiveValue::CSS_EMS).get(), create(20, CSSPrimitiveValue::CSS_REMS).get())); - EXPECT_REFV_EQ( - create(10, CSSPrimitiveValue::CSS_EMS), - AnimatableValue::add(create(10, CSSPrimitiveValue::CSS_EMS).get(), create(0, CSSPrimitiveValue::CSS_REMS).get())); - EXPECT_REFV_EQ( - create(20, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::add(create(0, CSSPrimitiveValue::CSS_EMS).get(), create(20, CSSPrimitiveValue::CSS_REMS).get())); - - // Check you actually get the reference back for zero optimization - RefPtr<AnimatableLength> rems20 = create(20, CSSPrimitiveValue::CSS_REMS); - EXPECT_EQ(rems20.get(), AnimatableValue::add(create(0, CSSPrimitiveValue::CSS_EMS).get(), rems20.get()).get()); - EXPECT_EQ(rems20.get(), AnimatableValue::add(rems20.get(), create(0, CSSPrimitiveValue::CSS_EMS).get()).get()); - - // Except 0% which is special - RefPtr<AnimatableLength> zeropercent = create(0, CSSPrimitiveValue::CSS_PERCENTAGE); - EXPECT_REFV_EQ(create(0, CSSPrimitiveValue::CSS_PERCENTAGE, -10, CSSPrimitiveValue::CSS_REMS), - AnimatableValue::add(zeropercent.get(), create(-10, CSSPrimitiveValue::CSS_REMS).get())); - EXPECT_REFV_EQ(create(-10, CSSPrimitiveValue::CSS_REMS, 0, CSSPrimitiveValue::CSS_PERCENTAGE), - AnimatableValue::add(create(-10, CSSPrimitiveValue::CSS_REMS).get(), zeropercent.get())); + EXPECT_EQ(Length(4, Fixed), create(Length(10, Fixed), 5)->length(2, ValueRangeAll)); + EXPECT_EQ(Length(10, Percent), create(Length(10, Percent), 5)->length(2, ValueRangeAll)); + Length calc = Length(CalculationValue::create(PixelsAndPercent(5, 10), ValueRangeAll)); + Length result = Length(CalculationValue::create(PixelsAndPercent(2, 10), ValueRangeAll)); + EXPECT_TRUE(result == create(calc, 5)->length(2, ValueRangeAll)); } -TEST_F(AnimationAnimatableLengthTest, IsUnitless) +TEST(AnimationAnimatableLengthTest, Equals) { - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_PX))); - EXPECT_FALSE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_PERCENTAGE))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_EMS))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_EXS))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_REMS))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_VW))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_VH))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_VMIN))); - EXPECT_TRUE(isUnitlessZero(create(0, CSSPrimitiveValue::CSS_VMAX))); - - EXPECT_FALSE(isUnitlessZero(create(1, CSSPrimitiveValue::CSS_PX))); - EXPECT_FALSE(isUnitlessZero(create(2, CSSPrimitiveValue::CSS_PERCENTAGE))); - EXPECT_FALSE(isUnitlessZero(create(3, CSSPrimitiveValue::CSS_EMS))); - EXPECT_FALSE(isUnitlessZero(create(4, CSSPrimitiveValue::CSS_EXS))); - EXPECT_FALSE(isUnitlessZero(create(5, CSSPrimitiveValue::CSS_REMS))); - EXPECT_FALSE(isUnitlessZero(create(6, CSSPrimitiveValue::CSS_VW))); - EXPECT_FALSE(isUnitlessZero(create(7, CSSPrimitiveValue::CSS_VH))); - EXPECT_FALSE(isUnitlessZero(create(8, CSSPrimitiveValue::CSS_VMIN))); - EXPECT_FALSE(isUnitlessZero(create(9, CSSPrimitiveValue::CSS_VMAX))); + EXPECT_TRUE(create(Length(10, Fixed))->equals(create(Length(10, Fixed)).get())); + EXPECT_TRUE(create(Length(20, Percent))->equals(create(Length(20, Percent)).get())); + EXPECT_FALSE(create(Length(10, Fixed))->equals(create(Length(10, Percent)).get())); + EXPECT_FALSE(create(Length(0, Percent))->equals(create(Length(0, Fixed)).get())); + Length calc = Length(CalculationValue::create(PixelsAndPercent(5, 10), ValueRangeAll)); + EXPECT_TRUE(create(calc)->equals(create(calc).get())); + EXPECT_FALSE(create(calc)->equals(create(Length(10, Percent)).get())); } -TEST_F(AnimationAnimatableLengthTest, CommonUnitType) +TEST(AnimationAnimatableLengthTest, Interpolate) { - RefPtr<AnimatableLength> length10px = create(10, CSSPrimitiveValue::CSS_PX); - EXPECT_EQ(AnimatableLength::UnitTypePixels, commonUnitType(length10px, create(1, CSSPrimitiveValue::CSS_PX).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length10px, create(2, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length10px, create(3, CSSPrimitiveValue::CSS_EMS).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length10px, create(4, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length10px, create(0, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - - RefPtr<AnimatableLength> length0px = create(0, CSSPrimitiveValue::CSS_PX); - EXPECT_EQ(AnimatableLength::UnitTypePixels, commonUnitType(length0px, create(1, CSSPrimitiveValue::CSS_PX).get())); - EXPECT_EQ(AnimatableLength::UnitTypePercentage, commonUnitType(length0px, create(2, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - EXPECT_EQ(AnimatableLength::UnitTypeFontSize, commonUnitType(length0px, create(3, CSSPrimitiveValue::CSS_EMS).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length0px, create(4, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_EQ(AnimatableLength::UnitTypePercentage, commonUnitType(length0px, create(0, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - - RefPtr<AnimatableLength> length0percent = create(0, CSSPrimitiveValue::CSS_PERCENTAGE); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length0percent, create(1, CSSPrimitiveValue::CSS_PX).get())); - EXPECT_EQ(AnimatableLength::UnitTypePercentage, commonUnitType(length0percent, create(2, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length0percent, create(3, CSSPrimitiveValue::CSS_EMS).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(length0percent, create(4, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_EQ(AnimatableLength::UnitTypePercentage, commonUnitType(length0percent, create(0, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - - RefPtr<AnimatableLength> lengthCalc = create(3, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(lengthCalc, create(1, CSSPrimitiveValue::CSS_PX).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(lengthCalc, create(2, CSSPrimitiveValue::CSS_PERCENTAGE).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(lengthCalc, create(3, CSSPrimitiveValue::CSS_EMS).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(lengthCalc, create(4, CSSPrimitiveValue::CSS_PX, 5, CSSPrimitiveValue::CSS_CM).get())); - EXPECT_EQ(AnimatableLength::UnitTypeCalc, commonUnitType(lengthCalc, create(0, CSSPrimitiveValue::CSS_PERCENTAGE).get())); + EXPECT_TRUE(AnimatableValue::interpolate(create(Length(10, Fixed)).get(), create(Length(0, Fixed)).get(), 0.2)->equals(create(Length(8, Fixed)).get())); + EXPECT_TRUE(AnimatableValue::interpolate(create(Length(4, Percent)).get(), create(Length(12, Percent)).get(), 0.25)->equals(create(Length(6, Percent)).get())); + Length calc = Length(CalculationValue::create(PixelsAndPercent(12, 4), ValueRangeAll)); + EXPECT_TRUE(AnimatableValue::interpolate(create(Length(20, Fixed)).get(), create(Length(10, Percent)).get(), 0.4)->equals(create(calc).get())); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutral.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutral.h index 5b8759ce45b..16f8595b29f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutral.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutral.h @@ -35,16 +35,18 @@ namespace WebCore { -class AnimatableNeutral : public AnimatableValue { +class AnimatableNeutral FINAL : public AnimatableValue { public: virtual ~AnimatableNeutral() { } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - static PassRefPtr<AnimatableNeutral> create() { return adoptRef(new AnimatableNeutral()); } - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue* value, double fraction) const OVERRIDE + static PassRefPtrWillBeRawPtr<AnimatableNeutral> create() { return adoptRefWillBeNoop(new AnimatableNeutral()); } + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue* value, double fraction) const OVERRIDE { ASSERT_NOT_REACHED(); - return 0; + return nullptr; } private: diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutralTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutralTest.cpp index dbf685ab8fd..755aa77e514 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutralTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableNeutralTest.cpp @@ -45,13 +45,4 @@ TEST(AnimationAnimatableNeutralTest, Create) EXPECT_TRUE(AnimatableValue::neutralValue()); } -TEST(AnimationAnimatableNeutralTest, Add) -{ - RefPtr<CSSValue> cssValue = CSSArrayFunctionValue::create(); - RefPtr<AnimatableValue> animatableUnknown = AnimatableUnknown::create(cssValue); - - EXPECT_EQ(cssValue, toAnimatableUnknown(AnimatableValue::add(animatableUnknown.get(), AnimatableValue::neutralValue()).get())->toCSSValue()); - EXPECT_EQ(cssValue, toAnimatableUnknown(AnimatableValue::add(AnimatableValue::neutralValue(), animatableUnknown.get()).get())->toCSSValue()); -} - } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.cpp index a9f1ae6d982..2c2f63e4e8b 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.cpp @@ -31,63 +31,57 @@ #include "config.h" #include "core/animation/AnimatableRepeatable.h" -namespace { +#include "wtf/MathExtras.h" -size_t greatestCommonDivisor(size_t a, size_t b) -{ - return b ? greatestCommonDivisor(b, a % b) : a; -} +namespace WebCore { -size_t lowestCommonMultiple(size_t a, size_t b) +bool AnimatableRepeatable::usesDefaultInterpolationWith(const AnimatableValue* value) const { - ASSERT(a && b); - return a / greatestCommonDivisor(a, b) * b; + const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& fromValues = m_values; + const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& toValues = toAnimatableRepeatable(value)->m_values; + ASSERT(!fromValues.isEmpty() && !toValues.isEmpty()); + size_t size = lowestCommonMultiple(fromValues.size(), toValues.size()); + ASSERT(size > 0); + for (size_t i = 0; i < size; ++i) { + const AnimatableValue* from = fromValues[i % fromValues.size()].get(); + const AnimatableValue* to = toValues[i % toValues.size()].get(); + // Spec: If a pair of values cannot be interpolated, then the lists are not interpolable. + if (AnimatableValue::usesDefaultInterpolation(from, to)) + return true; + } + return false; } -} // namespace - -namespace WebCore { - -bool AnimatableRepeatable::interpolateLists(const Vector<RefPtr<AnimatableValue> >& fromValues, const Vector<RefPtr<AnimatableValue> >& toValues, double fraction, Vector<RefPtr<AnimatableValue> >& interpolatedValues) +bool AnimatableRepeatable::interpolateLists(const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& fromValues, const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& toValues, double fraction, WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& interpolatedValues) { // Interpolation behaviour spec: http://www.w3.org/TR/css3-transitions/#animtype-repeatable-list ASSERT(interpolatedValues.isEmpty()); ASSERT(!fromValues.isEmpty() && !toValues.isEmpty()); size_t size = lowestCommonMultiple(fromValues.size(), toValues.size()); + ASSERT(size > 0); for (size_t i = 0; i < size; ++i) { const AnimatableValue* from = fromValues[i % fromValues.size()].get(); const AnimatableValue* to = toValues[i % toValues.size()].get(); // Spec: If a pair of values cannot be interpolated, then the lists are not interpolable. - if (!from->usesNonDefaultInterpolationWith(to)) + if (AnimatableValue::usesDefaultInterpolation(from, to)) return false; interpolatedValues.append(interpolate(from, to, fraction)); } return true; } -PassRefPtr<AnimatableValue> AnimatableRepeatable::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableRepeatable::interpolateTo(const AnimatableValue* value, double fraction) const { - Vector<RefPtr<AnimatableValue> > interpolatedValues; + WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > interpolatedValues; bool success = interpolateLists(m_values, toAnimatableRepeatable(value)->m_values, fraction, interpolatedValues); - return success ? create(interpolatedValues) : defaultInterpolateTo(this, value, fraction); -} - -PassRefPtr<AnimatableValue> AnimatableRepeatable::addWith(const AnimatableValue* value) const -{ - const Vector<RefPtr<AnimatableValue> >& otherValues = toAnimatableRepeatable(value)->m_values; - ASSERT(!m_values.isEmpty() && !otherValues.isEmpty()); - Vector<RefPtr<AnimatableValue> > addedValues(lowestCommonMultiple(m_values.size(), otherValues.size())); - for (size_t i = 0; i < addedValues.size(); ++i) { - const AnimatableValue* left = m_values[i % m_values.size()].get(); - const AnimatableValue* right = otherValues[i % otherValues.size()].get(); - addedValues[i] = add(left, right); - } - return create(addedValues); + if (success) + return create(interpolatedValues); + return defaultInterpolateTo(this, value, fraction); } bool AnimatableRepeatable::equalTo(const AnimatableValue* value) const { - const Vector<RefPtr<AnimatableValue> >& otherValues = toAnimatableRepeatable(value)->m_values; + const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& otherValues = toAnimatableRepeatable(value)->m_values; if (m_values.size() != otherValues.size()) return false; for (size_t i = 0; i < m_values.size(); ++i) { @@ -97,4 +91,10 @@ bool AnimatableRepeatable::equalTo(const AnimatableValue* value) const return true; } +void AnimatableRepeatable::trace(Visitor* visitor) +{ + visitor->trace(m_values); + AnimatableValue::trace(visitor); +} + } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.h index b1c74949c11..317fbfc113f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableRepeatable.h @@ -43,33 +43,36 @@ public: virtual ~AnimatableRepeatable() { } // This will consume the vector passed into it. - static PassRefPtr<AnimatableRepeatable> create(Vector<RefPtr<AnimatableValue> >& values) + static PassRefPtrWillBeRawPtr<AnimatableRepeatable> create(WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& values) { - return adoptRef(new AnimatableRepeatable(values)); + return adoptRefWillBeNoop(new AnimatableRepeatable(values)); } - const Vector<RefPtr<AnimatableValue> >& values() const { return m_values; } + const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& values() const { return m_values; } + + virtual void trace(Visitor*) OVERRIDE; protected: AnimatableRepeatable() { } - AnimatableRepeatable(Vector<RefPtr<AnimatableValue> >& values) + AnimatableRepeatable(WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& values) { ASSERT(!values.isEmpty()); m_values.swap(values); } - static bool interpolateLists(const Vector<RefPtr<AnimatableValue> >& fromValues, const Vector<RefPtr<AnimatableValue> >& toValues, double fraction, Vector<RefPtr<AnimatableValue> >& interpolatedValues); + static bool interpolateLists(const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& fromValues, const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& toValues, double fraction, WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& interpolatedValues); + + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; - Vector<RefPtr<AnimatableValue> > m_values; + WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > m_values; private: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; virtual AnimatableType type() const OVERRIDE { return TypeRepeatable; } - virtual bool equalTo(const AnimatableValue*) const OVERRIDE; + virtual bool equalTo(const AnimatableValue*) const OVERRIDE FINAL; }; DEFINE_TYPE_CASTS(AnimatableRepeatable, AnimatableValue, value, (value->isRepeatable() || value->isStrokeDasharrayList()), (value.isRepeatable() || value.isStrokeDasharrayList())); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.cpp index 97f4158d0b3..829140e7b7c 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.cpp @@ -35,20 +35,14 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableSVGLength::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableSVGLength::interpolateTo(const AnimatableValue* value, double fraction) const { - return create(toAnimatableSVGLength(value)->toSVGLength().blend(m_length, narrowPrecisionToFloat(fraction))); -} - -PassRefPtr<AnimatableValue> AnimatableSVGLength::addWith(const AnimatableValue* value) const -{ - ASSERT_WITH_MESSAGE(false, "Web Animations not yet implemented: AnimatableSVGLength::addWith()"); - return defaultAddWith(this, value); + return create(toAnimatableSVGLength(value)->toSVGLength()->blend(m_length.get(), narrowPrecisionToFloat(fraction))); } bool AnimatableSVGLength::equalTo(const AnimatableValue* value) const { - return m_length == toAnimatableSVGLength(value)->m_length; + return *m_length == *toAnimatableSVGLength(value)->m_length; } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.h index 27dba9a48c1..5b7f7912bb3 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGLength.h @@ -36,34 +36,35 @@ namespace WebCore { -class AnimatableSVGLength: public AnimatableValue { +class AnimatableSVGLength FINAL : public AnimatableValue { public: virtual ~AnimatableSVGLength() { } - static PassRefPtr<AnimatableSVGLength> create(const SVGLength& length) + static PassRefPtrWillBeRawPtr<AnimatableSVGLength> create(PassRefPtr<SVGLength> length) { - return adoptRef(new AnimatableSVGLength(length)); + return adoptRefWillBeNoop(new AnimatableSVGLength(length)); } - const SVGLength& toSVGLength() const + SVGLength* toSVGLength() const { - return m_length; + return m_length.get(); } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: - AnimatableSVGLength(const SVGLength& length) + AnimatableSVGLength(PassRefPtr<SVGLength> length) : m_length(length) { } - virtual AnimatableType type() const { return TypeSVGLength; } + virtual AnimatableType type() const OVERRIDE { return TypeSVGLength; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - SVGLength m_length; + RefPtr<SVGLength> m_length; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableSVGLength, isSVGLength()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.cpp index 784c9d32558..94eca3cd587 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.cpp @@ -33,32 +33,33 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableSVGPaint::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableSVGPaint::usesDefaultInterpolationWith(const AnimatableValue* value) const { const AnimatableSVGPaint* svgPaint = toAnimatableSVGPaint(value); - if (paintType() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR && svgPaint->paintType() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR) { - ASSERT(uri().isNull()); - return AnimatableSVGPaint::create(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, m_color.interpolateTo(svgPaint->m_color, fraction), String()); - } - return defaultInterpolateTo(this, value, fraction); + return (paintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || svgPaint->paintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR) + && (visitedLinkPaintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || svgPaint->visitedLinkPaintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR); } -PassRefPtr<AnimatableValue> AnimatableSVGPaint::addWith(const AnimatableValue* value) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableSVGPaint::interpolateTo(const AnimatableValue* value, double fraction) const { + if (usesDefaultInterpolationWith(value)) + return defaultInterpolateTo(this, value, fraction); + const AnimatableSVGPaint* svgPaint = toAnimatableSVGPaint(value); - if (paintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || svgPaint->paintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR) { - ASSERT(uri().isNull()); - return AnimatableSVGPaint::create(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, m_color.addWith(svgPaint->m_color), String()); - } - return defaultAddWith(this, value); + RefPtrWillBeRawPtr<AnimatableColor> color = toAnimatableColor(AnimatableValue::interpolate(m_color.get(), svgPaint->m_color.get(), fraction).get()); + if (fraction < 0.5) + return create(paintType(), visitedLinkPaintType(), color, uri(), visitedLinkURI()); + return create(svgPaint->paintType(), svgPaint->visitedLinkPaintType(), color, svgPaint->uri(), svgPaint->visitedLinkURI()); } bool AnimatableSVGPaint::equalTo(const AnimatableValue* value) const { const AnimatableSVGPaint* svgPaint = toAnimatableSVGPaint(value); return paintType() == svgPaint->paintType() + && visitedLinkPaintType() == svgPaint->visitedLinkPaintType() && color() == svgPaint->color() - && uri() == svgPaint->uri(); + && uri() == svgPaint->uri() + && visitedLinkURI() == svgPaint->visitedLinkURI(); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.h index 93ebd2e8ca6..ff54142b43c 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableSVGPaint.h @@ -37,38 +37,58 @@ namespace WebCore { -class AnimatableSVGPaint : public AnimatableValue { +class AnimatableSVGPaint FINAL : public AnimatableValue { public: virtual ~AnimatableSVGPaint() { } - static PassRefPtr<AnimatableSVGPaint> create(SVGPaint::SVGPaintType type, const Color& color, const String& uri) + static PassRefPtrWillBeRawPtr<AnimatableSVGPaint> create( + SVGPaint::SVGPaintType type, SVGPaint::SVGPaintType visitedLinkType, + const Color& color, const Color& visitedLinkColor, + const String& uri, const String& visitedLinkURI) { - return create(type, AnimatableColorImpl(color), uri); + return create(type, visitedLinkType, AnimatableColor::create(color, visitedLinkColor), uri, visitedLinkURI); } - static PassRefPtr<AnimatableSVGPaint> create(SVGPaint::SVGPaintType type, const AnimatableColorImpl& color, const String& uri) + static PassRefPtrWillBeRawPtr<AnimatableSVGPaint> create( + SVGPaint::SVGPaintType type, SVGPaint::SVGPaintType visitedLinkType, + PassRefPtrWillBeRawPtr<AnimatableColor> color, + const String& uri, const String& visitedLinkURI) { - return adoptRef(new AnimatableSVGPaint(type, color, uri)); + return adoptRefWillBeNoop(new AnimatableSVGPaint(type, visitedLinkType, color, uri, visitedLinkURI)); } SVGPaint::SVGPaintType paintType() const { return m_type; }; - Color color() const { return m_color.toColor(); }; + SVGPaint::SVGPaintType visitedLinkPaintType() const { return m_visitedLinkType; }; + Color color() const { return m_color->color(); }; + Color visitedLinkColor() const { return m_color->visitedLinkColor(); }; const String& uri() const { return m_uri; }; + const String& visitedLinkURI() const { return m_visitedLinkURI; }; + + virtual void trace(Visitor* visitor) OVERRIDE + { + visitor->trace(m_color); + AnimatableValue::trace(visitor); + } protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: - AnimatableSVGPaint(SVGPaint::SVGPaintType type, const AnimatableColorImpl& color, const String& uri) + AnimatableSVGPaint(SVGPaint::SVGPaintType type, SVGPaint::SVGPaintType visitedLinkType, PassRefPtrWillBeRawPtr<AnimatableColor> color, const String& uri, const String& visitedLinkURI) : m_type(type) + , m_visitedLinkType(visitedLinkType) , m_color(color) , m_uri(uri) + , m_visitedLinkURI(visitedLinkURI) { } virtual AnimatableType type() const OVERRIDE { return TypeSVGPaint; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; SVGPaint::SVGPaintType m_type; - AnimatableColorImpl m_color; + SVGPaint::SVGPaintType m_visitedLinkType; + // AnimatableColor includes a visited link color. + RefPtrWillBeMember<AnimatableColor> m_color; String m_uri; + String m_visitedLinkURI; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableSVGPaint, isSVGPaint()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.cpp index 4d1a847f319..63719bbe99a 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.cpp @@ -33,19 +33,12 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableShadow::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableShadow::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableShadow* shadowList = toAnimatableShadow(value); return AnimatableShadow::create(ShadowList::blend(m_shadowList.get(), shadowList->m_shadowList.get(), fraction)); } -PassRefPtr<AnimatableValue> AnimatableShadow::addWith(const AnimatableValue* value) const -{ - // FIXME: The spec doesn't specify anything for shadow in particular, but - // the default behaviour is probably not what one would expect. - return AnimatableValue::defaultAddWith(this, value); -} - bool AnimatableShadow::equalTo(const AnimatableValue* value) const { const ShadowList* shadowList = toAnimatableShadow(value)->m_shadowList.get(); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.h index 23fb59b7a08..80a7cf51886 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableShadow.h @@ -36,18 +36,19 @@ namespace WebCore { -class AnimatableShadow : public AnimatableValue { +class AnimatableShadow FINAL : public AnimatableValue { public: virtual ~AnimatableShadow() { } - static PassRefPtr<AnimatableShadow> create(PassRefPtr<ShadowList> shadowList) + static PassRefPtrWillBeRawPtr<AnimatableShadow> create(PassRefPtr<ShadowList> shadowList) { - return adoptRef(new AnimatableShadow(shadowList)); + return adoptRefWillBeNoop(new AnimatableShadow(shadowList)); } ShadowList* shadowList() const { return m_shadowList.get(); } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: explicit AnimatableShadow(PassRefPtr<ShadowList> shadowList) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.cpp index 9b17295c273..2e71e0dc88a 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.cpp @@ -33,20 +33,30 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableShapeValue::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableShapeValue::usesDefaultInterpolationWith(const AnimatableValue* value) const { const AnimatableShapeValue* shapeValue = toAnimatableShapeValue(value); - if (m_shape->type() != ShapeValue::Shape || shapeValue->m_shape->type() != ShapeValue::Shape) - return defaultInterpolateTo(this, value, fraction); + if (m_shape->type() != ShapeValue::Shape + || shapeValue->m_shape->type() != ShapeValue::Shape + || m_shape->cssBox() != shapeValue->m_shape->cssBox()) + return true; const BasicShape* fromShape = this->m_shape->shape(); const BasicShape* toShape = shapeValue->m_shape->shape(); - if (!fromShape->canBlend(toShape)) + return !fromShape->canBlend(toShape); +} + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableShapeValue::interpolateTo(const AnimatableValue* value, double fraction) const +{ + if (usesDefaultInterpolationWith(value)) return defaultInterpolateTo(this, value, fraction); - return AnimatableShapeValue::create(ShapeValue::createShapeValue(toShape->blend(fromShape, fraction)).get()); + const AnimatableShapeValue* shapeValue = toAnimatableShapeValue(value); + const BasicShape* fromShape = this->m_shape->shape(); + const BasicShape* toShape = shapeValue->m_shape->shape(); + return AnimatableShapeValue::create(ShapeValue::createShapeValue(toShape->blend(fromShape, fraction), shapeValue->m_shape->cssBox()).get()); } bool AnimatableShapeValue::equalTo(const AnimatableValue* value) const diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.h index 1d5382b0548..decc6f33f03 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableShapeValue.h @@ -36,17 +36,20 @@ namespace WebCore { -class AnimatableShapeValue : public AnimatableValue { +class AnimatableShapeValue FINAL : public AnimatableValue { public: virtual ~AnimatableShapeValue() { } - static PassRefPtr<AnimatableShapeValue> create(ShapeValue* shape) + static PassRefPtrWillBeRawPtr<AnimatableShapeValue> create(ShapeValue* shape) { - return adoptRef(new AnimatableShapeValue(shape)); + return adoptRefWillBeNoop(new AnimatableShapeValue(shape)); } ShapeValue* shapeValue() const { return m_shape.get(); } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: AnimatableShapeValue(ShapeValue* shape) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.cpp index b8951cc4cad..f81e3513a1e 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.cpp @@ -36,27 +36,36 @@ namespace WebCore { -AnimatableStrokeDasharrayList::AnimatableStrokeDasharrayList(const Vector<SVGLength>& lengths) +AnimatableStrokeDasharrayList::AnimatableStrokeDasharrayList(PassRefPtr<SVGLengthList> passLengths) { - for (size_t i = 0; i < lengths.size(); ++i) - m_values.append(AnimatableSVGLength::create(lengths[i])); + RefPtr<SVGLengthList> lengths = passLengths; + SVGLengthList::ConstIterator it = lengths->begin(); + SVGLengthList::ConstIterator itEnd = lengths->end(); + for (; it != itEnd; ++it) + m_values.append(AnimatableSVGLength::create(*it)); } -Vector<SVGLength> AnimatableStrokeDasharrayList::toSVGLengthVector() const +PassRefPtr<SVGLengthList> AnimatableStrokeDasharrayList::toSVGLengthList() const { - Vector<SVGLength> lengths(m_values.size()); + RefPtr<SVGLengthList> lengths = SVGLengthList::create(); for (size_t i = 0; i < m_values.size(); ++i) { - lengths[i] = toAnimatableSVGLength(m_values[i].get())->toSVGLength(); - if (lengths[i].valueInSpecifiedUnits() < 0) - lengths[i].setValueInSpecifiedUnits(0); + RefPtr<SVGLength> length = toAnimatableSVGLength(m_values[i].get())->toSVGLength()->clone(); + if (length->valueInSpecifiedUnits() < 0) + length->setValueInSpecifiedUnits(0); + lengths->append(length); } - return lengths; + return lengths.release(); } -PassRefPtr<AnimatableValue> AnimatableStrokeDasharrayList::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableStrokeDasharrayList::usesDefaultInterpolationWith(const AnimatableValue* value) const { - Vector<RefPtr<AnimatableValue> > from = m_values; - Vector<RefPtr<AnimatableValue> > to = toAnimatableStrokeDasharrayList(value)->m_values; + return false; +} + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableStrokeDasharrayList::interpolateTo(const AnimatableValue* value, double fraction) const +{ + WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > from = m_values; + WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > to = toAnimatableStrokeDasharrayList(value)->m_values; // The spec states that if the sum of all values is zero, this should be // treated like a value of 'none', which means that a solid line is drawn. @@ -66,12 +75,7 @@ PassRefPtr<AnimatableValue> AnimatableStrokeDasharrayList::interpolateTo(const A if (from.isEmpty() && to.isEmpty()) return takeConstRef(this); if (from.isEmpty() || to.isEmpty()) { - DEFINE_STATIC_REF(AnimatableSVGLength, zeroPixels, 0); - if (!zeroPixels) { - SVGLength length; - length.newValueSpecifiedUnits(LengthTypePX, 0, IGNORE_EXCEPTION); - zeroPixels = AnimatableSVGLength::create(length).leakRef(); - } + DEFINE_STATIC_REF_WILL_BE_PERSISTENT(AnimatableSVGLength, zeroPixels, (AnimatableSVGLength::create(SVGLength::create()))); if (from.isEmpty()) { from.append(zeroPixels); from.append(zeroPixels); @@ -82,10 +86,15 @@ PassRefPtr<AnimatableValue> AnimatableStrokeDasharrayList::interpolateTo(const A } } - Vector<RefPtr<AnimatableValue> > interpolatedValues; + WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > interpolatedValues; bool success = interpolateLists(from, to, fraction, interpolatedValues); ASSERT_UNUSED(success, success); - return adoptRef(new AnimatableStrokeDasharrayList(interpolatedValues)); + return adoptRefWillBeNoop(new AnimatableStrokeDasharrayList(interpolatedValues)); +} + +void AnimatableStrokeDasharrayList::trace(Visitor* visitor) +{ + AnimatableRepeatable::trace(visitor); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.h index d19b2e017ac..c7f5d8fafd7 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayList.h @@ -32,33 +32,36 @@ #define AnimatableStrokeDasharrayList_h #include "core/animation/AnimatableRepeatable.h" -#include "core/svg/SVGLength.h" +#include "core/svg/SVGLengthList.h" namespace WebCore { -class AnimatableStrokeDasharrayList: public AnimatableRepeatable { +class AnimatableStrokeDasharrayList FINAL : public AnimatableRepeatable { public: virtual ~AnimatableStrokeDasharrayList() { } - static PassRefPtr<AnimatableStrokeDasharrayList> create(const Vector<SVGLength>& lengths) + static PassRefPtrWillBeRawPtr<AnimatableStrokeDasharrayList> create(PassRefPtr<SVGLengthList> lengths) { - return adoptRef(new AnimatableStrokeDasharrayList(lengths)); + return adoptRefWillBeNoop(new AnimatableStrokeDasharrayList(lengths)); } - Vector<SVGLength> toSVGLengthVector() const; + PassRefPtr<SVGLengthList> toSVGLengthList() const; + + virtual void trace(Visitor*) OVERRIDE; protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: - AnimatableStrokeDasharrayList(const Vector<SVGLength>&); + AnimatableStrokeDasharrayList(PassRefPtr<SVGLengthList>); // This will consume the vector passed into it. - AnimatableStrokeDasharrayList(Vector<RefPtr<AnimatableValue> >& values) + AnimatableStrokeDasharrayList(WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& values) : AnimatableRepeatable(values) { } - virtual AnimatableType type() const { return TypeStrokeDasharrayList; } + virtual AnimatableType type() const OVERRIDE { return TypeStrokeDasharrayList; } }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableStrokeDasharrayList, isStrokeDasharrayList()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayListTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayListTest.cpp index 6a2756d2504..97f3330fcce 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayListTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableStrokeDasharrayListTest.cpp @@ -39,21 +39,29 @@ using namespace WebCore; namespace { +PassRefPtr<SVGLengthList> createSVGLengthList(size_t length) +{ + RefPtr<SVGLengthList> list = SVGLengthList::create(); + for (size_t i = 0; i < length; ++i) + list->append(SVGLength::create()); + return list.release(); +} + TEST(AnimationAnimatableStrokeDasharrayListTest, EqualTo) { - Vector<SVGLength> vectorA(4); - Vector<SVGLength> vectorB(4); - RefPtr<AnimatableStrokeDasharrayList> listA = AnimatableStrokeDasharrayList::create(vectorA); - RefPtr<AnimatableStrokeDasharrayList> listB = AnimatableStrokeDasharrayList::create(vectorB); + RefPtr<SVGLengthList> svgListA = createSVGLengthList(4); + RefPtr<SVGLengthList> svgListB = createSVGLengthList(4); + RefPtrWillBeRawPtr<AnimatableStrokeDasharrayList> listA = AnimatableStrokeDasharrayList::create(svgListA); + RefPtrWillBeRawPtr<AnimatableStrokeDasharrayList> listB = AnimatableStrokeDasharrayList::create(svgListB); EXPECT_TRUE(listA->equals(listB.get())); TrackExceptionState exceptionState; - vectorB[3].newValueSpecifiedUnits(LengthTypePX, 50, exceptionState); - listB = AnimatableStrokeDasharrayList::create(vectorB); + svgListB->at(3)->newValueSpecifiedUnits(LengthTypePX, 50); + listB = AnimatableStrokeDasharrayList::create(svgListB); EXPECT_FALSE(listA->equals(listB.get())); - vectorB = Vector<SVGLength>(5); - listB = AnimatableStrokeDasharrayList::create(vectorB); + svgListB = createSVGLengthList(5); + listB = AnimatableStrokeDasharrayList::create(svgListB); EXPECT_FALSE(listA->equals(listB.get())); } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.cpp index 542d8c315b0..98ea3e19930 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.cpp @@ -33,23 +33,17 @@ namespace WebCore { -PassRefPtr<AnimatableTransform> AnimatableTransform::create(const TransformOperations& transform) +PassRefPtrWillBeRawPtr<AnimatableTransform> AnimatableTransform::create(const TransformOperations& transform) { - return adoptRef(new AnimatableTransform(transform)); + return adoptRefWillBeNoop(new AnimatableTransform(transform)); } -PassRefPtr<AnimatableValue> AnimatableTransform::interpolateTo(const AnimatableValue* value, double fraction) const +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableTransform::interpolateTo(const AnimatableValue* value, double fraction) const { const AnimatableTransform* transform = toAnimatableTransform(value); return AnimatableTransform::create(transform->m_transform.blend(m_transform, fraction)); } -PassRefPtr<AnimatableValue> AnimatableTransform::addWith(const AnimatableValue* value) const -{ - const AnimatableTransform* transform = toAnimatableTransform(value); - return AnimatableTransform::create(m_transform.add(transform->m_transform)); -} - bool AnimatableTransform::equalTo(const AnimatableValue* value) const { return m_transform == toAnimatableTransform(value)->m_transform; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.h index 7fa85323fbc..51896af426b 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableTransform.h @@ -36,18 +36,19 @@ namespace WebCore { -class AnimatableTransform : public AnimatableValue { +class AnimatableTransform FINAL : public AnimatableValue { public: virtual ~AnimatableTransform() { } - static PassRefPtr<AnimatableTransform> create(const TransformOperations&); + static PassRefPtrWillBeRawPtr<AnimatableTransform> create(const TransformOperations&); const TransformOperations& transformOperations() const { return m_transform; } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; private: explicit AnimatableTransform(const TransformOperations& transform) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknown.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknown.h index 43be1c8568c..820c656ef6d 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknown.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknown.h @@ -31,36 +31,44 @@ #ifndef AnimatableUnknown_h #define AnimatableUnknown_h -#include "CSSValueKeywords.h" +#include "core/CSSValueKeywords.h" #include "core/animation/AnimatableValue.h" #include "core/css/CSSValuePool.h" namespace WebCore { -class AnimatableUnknown : public AnimatableValue { +class AnimatableUnknown FINAL : public AnimatableValue { public: virtual ~AnimatableUnknown() { } - static PassRefPtr<AnimatableUnknown> create(PassRefPtr<CSSValue> value) + static PassRefPtrWillBeRawPtr<AnimatableUnknown> create(PassRefPtrWillBeRawPtr<CSSValue> value) { - return adoptRef(new AnimatableUnknown(value)); + return adoptRefWillBeNoop(new AnimatableUnknown(value)); } - static PassRefPtr<AnimatableUnknown> create(CSSValueID value) + static PassRefPtrWillBeRawPtr<AnimatableUnknown> create(CSSValueID value) { - return adoptRef(new AnimatableUnknown(cssValuePool().createIdentifierValue(value))); + return adoptRefWillBeNoop(new AnimatableUnknown(cssValuePool().createIdentifierValue(value))); } - PassRefPtr<CSSValue> toCSSValue() const { return m_value; } + PassRefPtrWillBeRawPtr<CSSValue> toCSSValue() const { return m_value; } CSSValueID toCSSValueID() const { return toCSSPrimitiveValue(m_value.get())->getValueID(); } + virtual void trace(Visitor* visitor) OVERRIDE + { + visitor->trace(m_value); + AnimatableValue::trace(visitor); + } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue* value, double fraction) const OVERRIDE + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue* value, double fraction) const OVERRIDE { return defaultInterpolateTo(this, value, fraction); } + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE { return true; } + private: - explicit AnimatableUnknown(PassRefPtr<CSSValue> value) + explicit AnimatableUnknown(PassRefPtrWillBeRawPtr<CSSValue> value) : m_value(value) { ASSERT(m_value); @@ -68,7 +76,7 @@ private: virtual AnimatableType type() const OVERRIDE { return TypeUnknown; } virtual bool equalTo(const AnimatableValue*) const OVERRIDE; - const RefPtr<CSSValue> m_value; + const RefPtrWillBeMember<CSSValue> m_value; }; DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(AnimatableUnknown, isUnknown()); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknownTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknownTest.cpp index 5ca17607108..ad40dd76e57 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknownTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableUnknownTest.cpp @@ -51,11 +51,10 @@ protected: otherAnimatableUnknown = AnimatableUnknown::create(otherCSSValue); } - RefPtr<CSSValue> cssValue; - RefPtr<AnimatableValue> animatableUnknown; - - RefPtr<CSSValue> otherCSSValue; - RefPtr<AnimatableValue> otherAnimatableUnknown; + RefPtrWillBePersistent<CSSValue> cssValue; + RefPtrWillBePersistent<AnimatableValue> animatableUnknown; + RefPtrWillBePersistent<CSSValue> otherCSSValue; + RefPtrWillBePersistent<AnimatableValue> otherAnimatableUnknown; }; TEST_F(AnimationAnimatableUnknownTest, Create) @@ -83,10 +82,4 @@ TEST_F(AnimationAnimatableUnknownTest, Interpolate) EXPECT_EQ(cssValue, toAnimatableUnknown(AnimatableValue::interpolate(otherAnimatableUnknown.get(), animatableUnknown.get(), 1).get())->toCSSValue()); } -TEST_F(AnimationAnimatableUnknownTest, Add) -{ - EXPECT_EQ(otherCSSValue, toAnimatableUnknown(AnimatableValue::add(animatableUnknown.get(), otherAnimatableUnknown.get()).get())->toCSSValue()); - EXPECT_EQ(cssValue, toAnimatableUnknown(AnimatableValue::add(otherAnimatableUnknown.get(), animatableUnknown.get()).get())->toCSSValue()); -} - } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.cpp index 1c9d1751ca0..af0aec9ac4a 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.cpp @@ -34,15 +34,21 @@ #include "wtf/StdLibExtras.h" #include <algorithm> +namespace { + +const double defaultDistance = 1; + +} // namespace + namespace WebCore { const AnimatableValue* AnimatableValue::neutralValue() { - DEFINE_STATIC_REF(AnimatableNeutral, neutralSentinelValue, (AnimatableNeutral::create())); + DEFINE_STATIC_REF_WILL_BE_PERSISTENT(AnimatableNeutral, neutralSentinelValue, (AnimatableNeutral::create())); return neutralSentinelValue; } -PassRefPtr<AnimatableValue> AnimatableValue::interpolate(const AnimatableValue* left, const AnimatableValue* right, double fraction) +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableValue::interpolate(const AnimatableValue* left, const AnimatableValue* right, double fraction) { ASSERT(left); ASSERT(right); @@ -55,25 +61,20 @@ PassRefPtr<AnimatableValue> AnimatableValue::interpolate(const AnimatableValue* return defaultInterpolateTo(left, right, fraction); } -PassRefPtr<AnimatableValue> AnimatableValue::add(const AnimatableValue* left, const AnimatableValue* right) +double AnimatableValue::distance(const AnimatableValue* left, const AnimatableValue* right) { ASSERT(left); ASSERT(right); - if (left->isNeutral()) - return takeConstRef(right); - if (right->isNeutral()) - return takeConstRef(left); - if (left->isSameType(right)) - return left->addWith(right); + return left->distanceTo(right); - return defaultAddWith(left, right); + return defaultDistance; } -PassRefPtr<AnimatableValue> AnimatableValue::addWith(const AnimatableValue* value) const +double AnimatableValue::distanceTo(const AnimatableValue*) const { - return defaultAddWith(this, value); + return defaultDistance; } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.h index 2e52c321e95..2306142cc1c 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValue.h @@ -32,19 +32,23 @@ #define AnimatableValue_h #include "core/css/CSSValue.h" +#include "platform/heap/Handle.h" #include "wtf/RefCounted.h" namespace WebCore { -class AnimatableValue : public RefCounted<AnimatableValue> { +class AnimatableValue : public RefCountedWillBeGarbageCollectedFinalized<AnimatableValue> { public: virtual ~AnimatableValue() { } static const AnimatableValue* neutralValue(); - static PassRefPtr<AnimatableValue> interpolate(const AnimatableValue*, const AnimatableValue*, double fraction); - // For noncommutative values read add(A, B) to mean the value A with B composed onto it. - static PassRefPtr<AnimatableValue> add(const AnimatableValue*, const AnimatableValue*); + static PassRefPtrWillBeRawPtr<AnimatableValue> interpolate(const AnimatableValue*, const AnimatableValue*, double fraction); + static double distance(const AnimatableValue* from, const AnimatableValue* to); + static bool usesDefaultInterpolation(const AnimatableValue* from, const AnimatableValue* to) + { + return !from->isSameType(to) || from->usesDefaultInterpolationWith(to); + } bool equals(const AnimatableValue* value) const { @@ -64,6 +68,7 @@ public: bool isLengthBox() const { return type() == TypeLengthBox; } bool isLengthBoxAndBool() const { return type() == TypeLengthBoxAndBool; } bool isLengthPoint() const { return type() == TypeLengthPoint; } + bool isLengthPoint3D() const { return type() == TypeLengthPoint3D; } bool isLengthSize() const { return type() == TypeLengthSize; } bool isNeutral() const { return type() == TypeNeutral; } bool isRepeatable() const { return type() == TypeRepeatable; } @@ -82,10 +87,7 @@ public: return value->type() == type(); } - bool usesNonDefaultInterpolationWith(const AnimatableValue* value) const - { - return isSameType(value) && !isUnknown(); - } + virtual void trace(Visitor*) { } protected: enum AnimatableType { @@ -98,6 +100,7 @@ protected: TypeLengthBox, TypeLengthBoxAndBool, TypeLengthPoint, + TypeLengthPoint3D, TypeLengthSize, TypeNeutral, TypeRepeatable, @@ -111,20 +114,21 @@ protected: TypeVisibility, }; - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const = 0; - static PassRefPtr<AnimatableValue> defaultInterpolateTo(const AnimatableValue* left, const AnimatableValue* right, double fraction) { return takeConstRef((fraction < 0.5) ? left : right); } - - // For noncommutative values read A->addWith(B) to mean the value A with B composed onto it. - virtual PassRefPtr<AnimatableValue> addWith(const AnimatableValue*) const; - static PassRefPtr<AnimatableValue> defaultAddWith(const AnimatableValue* left, const AnimatableValue* right) { return takeConstRef(right); } + virtual bool usesDefaultInterpolationWith(const AnimatableValue* value) const { return false; } + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const = 0; + static PassRefPtrWillBeRawPtr<AnimatableValue> defaultInterpolateTo(const AnimatableValue* left, const AnimatableValue* right, double fraction) { return takeConstRef((fraction < 0.5) ? left : right); } template <class T> - static PassRefPtr<T> takeConstRef(const T* value) { return PassRefPtr<T>(const_cast<T*>(value)); } + static PassRefPtrWillBeRawPtr<T> takeConstRef(const T* value) { return PassRefPtrWillBeRawPtr<T>(const_cast<T*>(value)); } private: virtual AnimatableType type() const = 0; // Implementations can assume that the object being compared has the same type as the object this is called on virtual bool equalTo(const AnimatableValue*) const = 0; + + virtual double distanceTo(const AnimatableValue*) const; + + template <class Keyframe> friend class KeyframeEffectModel; }; #define DEFINE_ANIMATABLE_VALUE_TYPE_CASTS(thisType, predicate) \ diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueKeyframe.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueKeyframe.cpp new file mode 100644 index 00000000000..43e9bfdee32 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueKeyframe.cpp @@ -0,0 +1,80 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/AnimatableValueKeyframe.h" + +#include "core/animation/interpolation/LegacyStyleInterpolation.h" + +namespace WebCore { + +AnimatableValueKeyframe::AnimatableValueKeyframe(const AnimatableValueKeyframe& copyFrom) + : Keyframe(copyFrom.m_offset, copyFrom.m_composite, copyFrom.m_easing) +{ + for (PropertyValueMap::const_iterator iter = copyFrom.m_propertyValues.begin(); iter != copyFrom.m_propertyValues.end(); ++iter) + setPropertyValue(iter->key, iter->value.get()); +} + +PropertySet AnimatableValueKeyframe::properties() const +{ + // This is not used in time-critical code, so we probably don't need to + // worry about caching this result. + PropertySet properties; + for (PropertyValueMap::const_iterator iter = m_propertyValues.begin(); iter != m_propertyValues.end(); ++iter) + properties.add(*iter.keys()); + return properties; +} + +PassRefPtrWillBeRawPtr<Keyframe> AnimatableValueKeyframe::clone() const +{ + return adoptRefWillBeNoop(new AnimatableValueKeyframe(*this)); +} + +PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> AnimatableValueKeyframe::createPropertySpecificKeyframe(CSSPropertyID property) const +{ + return adoptPtrWillBeNoop(new PropertySpecificKeyframe(offset(), easing(), propertyValue(property), composite())); +} + +void AnimatableValueKeyframe::trace(Visitor* visitor) +{ + visitor->trace(m_propertyValues); + Keyframe::trace(visitor); +} + +AnimatableValueKeyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, const AnimatableValue* value, AnimationEffect::CompositeOperation op) + : Keyframe::PropertySpecificKeyframe(offset, easing, op) + , m_value(const_cast<AnimatableValue*>(value)) +{ } + +AnimatableValueKeyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, PassRefPtrWillBeRawPtr<AnimatableValue> value) + : Keyframe::PropertySpecificKeyframe(offset, easing, AnimationEffect::CompositeReplace) + , m_value(value) +{ + ASSERT(!isNull(m_offset)); +} + +PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> AnimatableValueKeyframe::PropertySpecificKeyframe::cloneWithOffset(double offset) const +{ + Keyframe::PropertySpecificKeyframe* theClone = new PropertySpecificKeyframe(offset, m_easing, m_value); + return adoptPtrWillBeNoop(theClone); +} + +PassRefPtrWillBeRawPtr<Interpolation> AnimatableValueKeyframe::PropertySpecificKeyframe::createInterpolation(CSSPropertyID property, Keyframe::PropertySpecificKeyframe* end, Element*) const +{ + AnimatableValuePropertySpecificKeyframe* to = toAnimatableValuePropertySpecificKeyframe(end); + return LegacyStyleInterpolation::create(value(), to->value(), property); +} + +PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> AnimatableValueKeyframe::PropertySpecificKeyframe::neutralKeyframe(double offset, PassRefPtr<TimingFunction> easing) const +{ + return adoptPtrWillBeNoop(new AnimatableValueKeyframe::PropertySpecificKeyframe(offset, easing, AnimatableValue::neutralValue(), AnimationEffect::CompositeAdd)); +} + +void AnimatableValueKeyframe::PropertySpecificKeyframe::trace(Visitor* visitor) +{ + visitor->trace(m_value); + Keyframe::PropertySpecificKeyframe::trace(visitor); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueKeyframe.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueKeyframe.h new file mode 100644 index 00000000000..c3fd15584e9 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueKeyframe.h @@ -0,0 +1,75 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef AnimatableValueKeyframe_h +#define AnimatableValueKeyframe_h + +#include "core/animation/AnimatableValue.h" +#include "core/animation/Keyframe.h" + +namespace WebCore { + +class AnimatableValueKeyframe : public Keyframe { +public: + static PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> create() + { + return adoptRefWillBeNoop(new AnimatableValueKeyframe); + } + void setPropertyValue(CSSPropertyID property, PassRefPtrWillBeRawPtr<AnimatableValue> value) + { + m_propertyValues.add(property, value); + } + void clearPropertyValue(CSSPropertyID property) { m_propertyValues.remove(property); } + AnimatableValue* propertyValue(CSSPropertyID property) const + { + ASSERT(m_propertyValues.contains(property)); + return m_propertyValues.get(property); + } + virtual PropertySet properties() const OVERRIDE; + + virtual void trace(Visitor*) OVERRIDE; + + class PropertySpecificKeyframe : public Keyframe::PropertySpecificKeyframe { + public: + PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, const AnimatableValue*, AnimationEffect::CompositeOperation); + + AnimatableValue* value() const { return m_value.get(); } + virtual const PassRefPtrWillBeRawPtr<AnimatableValue> getAnimatableValue() const OVERRIDE FINAL { return m_value; } + + virtual PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> neutralKeyframe(double offset, PassRefPtr<TimingFunction> easing) const OVERRIDE FINAL; + virtual PassRefPtrWillBeRawPtr<Interpolation> createInterpolation(CSSPropertyID, WebCore::Keyframe::PropertySpecificKeyframe* end, Element*) const OVERRIDE FINAL; + + virtual void trace(Visitor*) OVERRIDE; + + private: + PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, PassRefPtrWillBeRawPtr<AnimatableValue>); + + virtual PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> cloneWithOffset(double offset) const OVERRIDE; + virtual bool isAnimatableValuePropertySpecificKeyframe() const OVERRIDE { return true; } + + RefPtrWillBeMember<AnimatableValue> m_value; + }; + +private: + AnimatableValueKeyframe() { } + + AnimatableValueKeyframe(const AnimatableValueKeyframe& copyFrom); + + virtual PassRefPtrWillBeRawPtr<Keyframe> clone() const OVERRIDE; + virtual PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> createPropertySpecificKeyframe(CSSPropertyID) const OVERRIDE; + + virtual bool isAnimatableValueKeyframe() const OVERRIDE { return true; } + + typedef HashMap<CSSPropertyID, RefPtrWillBeMember<AnimatableValue> > PropertyValueMap; + PropertyValueMap m_propertyValues; +}; + +typedef AnimatableValueKeyframe::PropertySpecificKeyframe AnimatableValuePropertySpecificKeyframe; + +DEFINE_TYPE_CASTS(AnimatableValueKeyframe, Keyframe, value, value->isAnimatableValueKeyframe(), value.isAnimatableValueKeyframe()); +DEFINE_TYPE_CASTS(AnimatableValuePropertySpecificKeyframe, Keyframe::PropertySpecificKeyframe, value, value->isAnimatableValuePropertySpecificKeyframe(), value.isAnimatableValuePropertySpecificKeyframe()); + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.cpp index da6958cfc21..f43065913a3 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.cpp @@ -32,8 +32,6 @@ #include "core/animation/AnimatableValueTestHelper.h" - - namespace WebCore { bool operator==(const AnimatableValue& a, const AnimatableValue& b) @@ -53,50 +51,9 @@ void PrintTo(const AnimatableColor& animColor, ::std::ostream* os) << animColor.visitedLinkColor().serialized().utf8().data() << ")"; } -void PrintTo(const AnimatableDouble& animDouble, ::std::ostream* os) -{ - PrintTo(*(animDouble.toCSSValue().get()), os, "AnimatableDouble"); -} - void PrintTo(const AnimatableImage& animImage, ::std::ostream* os) { - PrintTo(*(animImage.toCSSValue().get()), os, "AnimatableImage"); -} - -void PrintTo(const AnimatableLength& animLength, ::std::ostream* os) -{ - PrintTo(*(animLength.toCSSValue().get()), os, "AnimatableLength"); -} - -void PrintTo(const AnimatableLengthBox& animLengthBox, ::std::ostream* os) -{ - *os << "AnimatableLengthBox("; - PrintTo(*(animLengthBox.left()), os); - *os << ", "; - PrintTo(*(animLengthBox.right()), os); - *os << ", "; - PrintTo(*(animLengthBox.top()), os); - *os << ", "; - PrintTo(*(animLengthBox.bottom()), os); - *os << ")"; -} - -void PrintTo(const AnimatableLengthPoint& animLengthPoint, ::std::ostream* os) -{ - *os << "AnimatableLengthPoint("; - PrintTo(*(animLengthPoint.x()), os); - *os << ", "; - PrintTo(*(animLengthPoint.y()), os); - *os << ")"; -} - -void PrintTo(const AnimatableLengthSize& animLengthSize, ::std::ostream* os) -{ - *os << "AnimatableLengthSize("; - PrintTo(*(animLengthSize.width()), os); - *os << ", "; - PrintTo(*(animLengthSize.height()), os); - *os << ")"; + PrintTo(*(animImage.toCSSValue()), os, "AnimatableImage"); } void PrintTo(const AnimatableNeutral& animValue, ::std::ostream* os) @@ -108,8 +65,8 @@ void PrintTo(const AnimatableRepeatable& animValue, ::std::ostream* os) { *os << "AnimatableRepeatable("; - const Vector<RefPtr<AnimatableValue> > v = animValue.values(); - for (Vector<RefPtr<AnimatableValue> >::const_iterator it = v.begin(); it != v.end(); ++it) { + const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > v = animValue.values(); + for (WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >::const_iterator it = v.begin(); it != v.end(); ++it) { PrintTo(*(it->get()), os); if (it+1 != v.end()) *os << ", "; @@ -120,19 +77,7 @@ void PrintTo(const AnimatableRepeatable& animValue, ::std::ostream* os) void PrintTo(const AnimatableSVGLength& animSVGLength, ::std::ostream* os) { *os << "AnimatableSVGLength(" - << animSVGLength.toSVGLength().valueAsString().utf8().data() << ")"; -} - -void PrintTo(const AnimatableSVGPaint& animSVGPaint, ::std::ostream* os) -{ - *os << "AnimatableSVGPaint("; - if (animSVGPaint.paintType() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR) - *os << animSVGPaint.color().serialized().utf8().data(); - else if (animSVGPaint.paintType() == SVGPaint::SVG_PAINTTYPE_URI) - *os << "url(" << animSVGPaint.uri().utf8().data() << ")"; - else - *os << animSVGPaint.paintType(); - *os << ")"; + << animSVGLength.toSVGLength()->valueAsString().utf8().data() << ")"; } void PrintTo(const AnimatableShapeValue& animValue, ::std::ostream* os) @@ -143,10 +88,11 @@ void PrintTo(const AnimatableShapeValue& animValue, ::std::ostream* os) void PrintTo(const AnimatableStrokeDasharrayList& animValue, ::std::ostream* os) { *os << "AnimatableStrokeDasharrayList("; - const Vector<SVGLength> v = animValue.toSVGLengthVector(); - for (Vector<SVGLength>::const_iterator it = v.begin(); it != v.end(); ++it) { - *os << it->valueAsString().utf8().data(); - if (it+1 != v.end()) + RefPtr<SVGLengthList> list = animValue.toSVGLengthList(); + size_t length = list->length(); + for (size_t i = 0; i < length; ++i) { + *os << list->at(i)->valueAsString().utf8().data(); + if (i != length-1) *os << ", "; } *os << ")"; @@ -232,18 +178,8 @@ void PrintTo(const AnimatableValue& animValue, ::std::ostream* os) PrintTo(*(toAnimatableClipPathOperation(&animValue)), os); else if (animValue.isColor()) PrintTo(*(toAnimatableColor(&animValue)), os); - else if (animValue.isDouble()) - PrintTo(*(toAnimatableDouble(&animValue)), os); else if (animValue.isImage()) PrintTo(*(toAnimatableImage(&animValue)), os); - else if (animValue.isLength()) - PrintTo(*(toAnimatableLength(&animValue)), os); - else if (animValue.isLengthBox()) - PrintTo(*(toAnimatableLengthBox(&animValue)), os); - else if (animValue.isLengthPoint()) - PrintTo(*(toAnimatableLengthPoint(&animValue)), os); - else if (animValue.isLengthSize()) - PrintTo(*(toAnimatableLengthSize(&animValue)), os); else if (animValue.isNeutral()) PrintTo(*(static_cast<const AnimatableNeutral*>(&animValue)), os); else if (animValue.isRepeatable()) diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.h index 65a86606768..ceed1bd85a7 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelper.h @@ -38,12 +38,7 @@ #include "core/animation/AnimatableClipPathOperation.h" #include "core/animation/AnimatableColor.h" -#include "core/animation/AnimatableDouble.h" #include "core/animation/AnimatableImage.h" -#include "core/animation/AnimatableLength.h" -#include "core/animation/AnimatableLengthBox.h" -#include "core/animation/AnimatableLengthPoint.h" -#include "core/animation/AnimatableLengthSize.h" #include "core/animation/AnimatableNeutral.h" #include "core/animation/AnimatableRepeatable.h" #include "core/animation/AnimatableSVGLength.h" @@ -68,16 +63,10 @@ bool operator==(const AnimatableValue&, const AnimatableValue&); void PrintTo(const AnimatableClipPathOperation&, ::std::ostream*); void PrintTo(const AnimatableColor&, ::std::ostream*); -void PrintTo(const AnimatableDouble&, ::std::ostream*); void PrintTo(const AnimatableImage&, ::std::ostream*); -void PrintTo(const AnimatableLength&, ::std::ostream*); -void PrintTo(const AnimatableLengthBox&, ::std::ostream*); -void PrintTo(const AnimatableLengthPoint&, ::std::ostream*); -void PrintTo(const AnimatableLengthSize&, ::std::ostream*); void PrintTo(const AnimatableNeutral&, ::std::ostream*); void PrintTo(const AnimatableRepeatable&, ::std::ostream*); void PrintTo(const AnimatableSVGLength&, ::std::ostream*); -void PrintTo(const AnimatableSVGPaint&, ::std::ostream*); void PrintTo(const AnimatableShapeValue&, ::std::ostream*); void PrintTo(const AnimatableStrokeDasharrayList&, ::std::ostream*); void PrintTo(const AnimatableTransform&, ::std::ostream*); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelperTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelperTest.cpp index fad1a5a55e4..16bffd4eacc 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelperTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableValueTestHelperTest.cpp @@ -50,14 +50,9 @@ namespace { class AnimationAnimatableValueTestHelperTest : public ::testing::Test { protected: - ::std::string PrintToString(PassRefPtr<AnimatableValue> animValue) + ::std::string PrintToString(PassRefPtrWillBeRawPtr<AnimatableValue> animValue) { - return PrintToString(animValue.get()); - } - - ::std::string PrintToString(const AnimatableValue* animValue) - { - return ::testing::PrintToString(*animValue); + return ::testing::PrintToString(*animValue.get()); } }; @@ -72,70 +67,29 @@ TEST_F(AnimationAnimatableValueTestHelperTest, PrintTo) ::std::string("AnimatableColor(rgba(0, 0, 0, 0), #ff0000)"), PrintToString(AnimatableColor::create(Color(0x000000FF), Color(0xFFFF0000)))); - EXPECT_EQ( - ::std::string("AnimatableDouble(1)"), - PrintToString(AnimatableDouble::create(1.0))); - - EXPECT_EQ( - ::std::string("AnimatableLength(5px)"), - PrintToString(AnimatableLength::create(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_PX).get()))); - - EXPECT_EQ( - ::std::string("AnimatableLengthBox(AnimatableLength(1px), AnimatableLength(2em), AnimatableLength(3rem), AnimatableLength(4pt))"), - PrintToString(AnimatableLengthBox::create( - AnimatableLength::create(CSSPrimitiveValue::create(1, CSSPrimitiveValue::CSS_PX).get()), - AnimatableLength::create(CSSPrimitiveValue::create(2, CSSPrimitiveValue::CSS_EMS).get()), - AnimatableLength::create(CSSPrimitiveValue::create(3, CSSPrimitiveValue::CSS_REMS).get()), - AnimatableLength::create(CSSPrimitiveValue::create(4, CSSPrimitiveValue::CSS_PT).get()) - ))); - - EXPECT_EQ( - ::std::string("AnimatableLengthPoint(AnimatableLength(5%), AnimatableLength(6px))"), - PrintToString(AnimatableLengthPoint::create( - AnimatableLength::create(CSSPrimitiveValue::create(5, CSSPrimitiveValue::CSS_PERCENTAGE).get()), - AnimatableLength::create(CSSPrimitiveValue::create(6, CSSPrimitiveValue::CSS_PX).get()) - ))); - - EXPECT_EQ( - ::std::string("AnimatableLengthSize(AnimatableLength(3rem), AnimatableLength(4pt))"), - PrintToString(AnimatableLengthSize::create( - AnimatableLength::create(CSSPrimitiveValue::create(3, CSSPrimitiveValue::CSS_REMS).get()), - AnimatableLength::create(CSSPrimitiveValue::create(4, CSSPrimitiveValue::CSS_PT).get()) - ))); - EXPECT_THAT( - PrintToString(AnimatableValue::neutralValue()), + PrintToString(const_cast<AnimatableValue*>(AnimatableValue::neutralValue())), testing::StartsWith("AnimatableNeutral@")); - Vector<RefPtr<AnimatableValue> > v1; - v1.append(AnimatableLength::create(CSSPrimitiveValue::create(3, CSSPrimitiveValue::CSS_REMS).get())); - v1.append(AnimatableLength::create(CSSPrimitiveValue::create(4, CSSPrimitiveValue::CSS_PT).get())); - EXPECT_EQ( - ::std::string("AnimatableRepeatable(AnimatableLength(3rem), AnimatableLength(4pt))"), - PrintToString(AnimatableRepeatable::create(v1))); + RefPtr<SVGLength> length1cm = SVGLength::create(LengthModeOther); + RefPtr<SVGLength> length2cm = SVGLength::create(LengthModeOther); + length1cm->setValueAsString("1cm", ASSERT_NO_EXCEPTION); + length2cm->setValueAsString("2cm", ASSERT_NO_EXCEPTION); EXPECT_EQ( ::std::string("AnimatableSVGLength(1cm)"), - PrintToString(AnimatableSVGLength::create(SVGLength(LengthModeOther, "1cm")))); - - EXPECT_EQ( - ::std::string("AnimatableSVGPaint(#ff0000)"), - PrintToString(AnimatableSVGPaint::create(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, Color(0xFFFF0000), ""))); - - EXPECT_EQ( - ::std::string("AnimatableSVGPaint(url(abc))"), - PrintToString(AnimatableSVGPaint::create(SVGPaint::SVG_PAINTTYPE_URI, Color(0xFFFF0000), "abc"))); + PrintToString(AnimatableSVGLength::create(length1cm))); EXPECT_THAT( - PrintToString(AnimatableShapeValue::create(ShapeValue::createShapeValue(BasicShapeCircle::create().get()).get())), + PrintToString(AnimatableShapeValue::create(ShapeValue::createShapeValue(BasicShapeCircle::create().get(), ContentBox).get())), testing::StartsWith("AnimatableShapeValue@")); - Vector<SVGLength> v2; - v2.append(SVGLength(LengthModeOther, "1cm")); - v2.append(SVGLength(LengthModeOther, "2cm")); + RefPtr<SVGLengthList> l2 = SVGLengthList::create(); + l2->append(length1cm); + l2->append(length2cm); EXPECT_EQ( ::std::string("AnimatableStrokeDasharrayList(1cm, 2cm)"), - PrintToString(AnimatableStrokeDasharrayList::create(v2))); + PrintToString(AnimatableStrokeDasharrayList::create(l2))); TransformOperations operations1; operations1.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TranslateX)); diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.cpp index a395efa2b2c..573783f9e1b 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.cpp @@ -33,7 +33,14 @@ namespace WebCore { -PassRefPtr<AnimatableValue> AnimatableVisibility::interpolateTo(const AnimatableValue* value, double fraction) const +bool AnimatableVisibility::usesDefaultInterpolationWith(const AnimatableValue* value) const +{ + EVisibility from = m_visibility; + EVisibility to = toAnimatableVisibility(value)->m_visibility; + return from != VISIBLE && to != VISIBLE; +} + +PassRefPtrWillBeRawPtr<AnimatableValue> AnimatableVisibility::interpolateTo(const AnimatableValue* value, double fraction) const { EVisibility from = m_visibility; EVisibility to = toAnimatableVisibility(value)->m_visibility; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.h b/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.h index bbd377105d0..72d653ebc66 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimatableVisibility.h @@ -36,18 +36,21 @@ namespace WebCore { -class AnimatableVisibility : public AnimatableValue { +class AnimatableVisibility FINAL : public AnimatableValue { public: virtual ~AnimatableVisibility() { } - static PassRefPtr<AnimatableVisibility> create(EVisibility visibility) + static PassRefPtrWillBeRawPtr<AnimatableVisibility> create(EVisibility visibility) { - return adoptRef(new AnimatableVisibility(visibility)); + return adoptRefWillBeNoop(new AnimatableVisibility(visibility)); } EVisibility visibility() const { return m_visibility; } + virtual void trace(Visitor* visitor) OVERRIDE { AnimatableValue::trace(visitor); } + protected: - virtual PassRefPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual PassRefPtrWillBeRawPtr<AnimatableValue> interpolateTo(const AnimatableValue*, double fraction) const OVERRIDE; + virtual bool usesDefaultInterpolationWith(const AnimatableValue*) const OVERRIDE; private: explicit AnimatableVisibility(EVisibility visibility) diff --git a/chromium/third_party/WebKit/Source/core/animation/Animation.cpp b/chromium/third_party/WebKit/Source/core/animation/Animation.cpp index 6e40086d6f2..4eedf009903 100644 --- a/chromium/third_party/WebKit/Source/core/animation/Animation.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/Animation.cpp @@ -31,136 +31,236 @@ #include "config.h" #include "core/animation/Animation.h" +#include "bindings/v8/Dictionary.h" +#include "bindings/v8/ExceptionState.h" #include "core/animation/ActiveAnimations.h" +#include "core/animation/AnimationHelpers.h" +#include "core/animation/AnimationPlayer.h" +#include "core/animation/AnimationTimeline.h" #include "core/animation/CompositorAnimations.h" -#include "core/animation/KeyframeAnimationEffect.h" -#include "core/animation/Player.h" +#include "core/animation/KeyframeEffectModel.h" +#include "core/animation/interpolation/Interpolation.h" #include "core/dom/Element.h" +#include "core/frame/UseCounter.h" +#include "core/rendering/RenderLayer.h" namespace WebCore { -PassRefPtr<Animation> Animation::create(PassRefPtr<Element> target, PassRefPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate) +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate) { - return adoptRef(new Animation(target, effect, timing, priority, eventDelegate)); + return adoptRefWillBeNoop(new Animation(target, effect, timing, priority, eventDelegate)); } -Animation::Animation(PassRefPtr<Element> target, PassRefPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate) - : TimedItem(timing, eventDelegate) +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Dictionary& timingInputDictionary) +{ + ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); + return create(element, effect, TimingInput::convert(timingInputDictionary)); +} +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, double duration) +{ + ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); + return create(element, effect, TimingInput::convert(duration)); +} +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect) +{ + ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); + return create(element, effect, Timing()); +} +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, const Dictionary& timingInputDictionary, ExceptionState& exceptionState) +{ + ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); + if (element) + UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectObjectTiming); + return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(timingInputDictionary)); +} +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState& exceptionState) +{ + ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); + if (element) + UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectDoubleTiming); + return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(duration)); +} +PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState) +{ + ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); + if (element) + UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectNoTiming); + return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), Timing()); +} + +Animation::Animation(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate) + : AnimationNode(timing, eventDelegate) , m_target(target) , m_effect(effect) - , m_activeInAnimationStack(false) + , m_sampledEffect(nullptr) , m_priority(priority) { +#if !ENABLE(OILPAN) + if (m_target) + m_target->ensureActiveAnimations().addAnimation(this); +#endif } -void Animation::didAttach() +Animation::~Animation() { +#if !ENABLE(OILPAN) if (m_target) - m_target->ensureActiveAnimations()->players().add(player()); + m_target->activeAnimations()->notifyAnimationDestroyed(this); +#endif } -void Animation::willDetach() +void Animation::attach(AnimationPlayer* player) +{ + if (m_target) { + m_target->ensureActiveAnimations().addPlayer(player); + m_target->setNeedsAnimationStyleRecalc(); + } + AnimationNode::attach(player); +} + +void Animation::detach() { if (m_target) - m_target->activeAnimations()->players().remove(player()); - if (m_activeInAnimationStack) + m_target->activeAnimations()->removePlayer(player()); + if (m_sampledEffect) clearEffects(); + AnimationNode::detach(); +} + +void Animation::specifiedTimingChanged() +{ + cancelAnimationOnCompositor(); + if (player()) { + // FIXME: Needs to consider groups when added. + ASSERT(player()->source() == this); + player()->schedulePendingAnimationOnCompositor(); + } } static AnimationStack& ensureAnimationStack(Element* element) { - return element->ensureActiveAnimations()->defaultStack(); + return element->ensureActiveAnimations().defaultStack(); } -bool Animation::applyEffects(bool previouslyInEffect) +void Animation::applyEffects() { ASSERT(isInEffect()); + ASSERT(player()); if (!m_target || !m_effect) - return false; - - if (player() && !previouslyInEffect) { - ensureAnimationStack(m_target.get()).add(this); - m_activeInAnimationStack = true; - } + return; double iteration = currentIteration(); ASSERT(iteration >= 0); // FIXME: Handle iteration values which overflow int. - m_compositableValues = m_effect->sample(static_cast<int>(iteration), timeFraction()); - if (player()) { - m_target->setNeedsAnimationStyleRecalc(); - return true; + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations = m_effect->sample(static_cast<int>(iteration), timeFraction(), iterationDuration()); + if (m_sampledEffect) { + m_sampledEffect->setInterpolations(interpolations.release()); + } else if (!interpolations->isEmpty()) { + OwnPtrWillBeRawPtr<SampledEffect> sampledEffect = SampledEffect::create(this, interpolations.release()); + m_sampledEffect = sampledEffect.get(); + ensureAnimationStack(m_target).add(sampledEffect.release()); + } else { + return; } - return false; + + m_target->setNeedsAnimationStyleRecalc(); } void Animation::clearEffects() { ASSERT(player()); - ASSERT(m_activeInAnimationStack); - ensureAnimationStack(m_target.get()).remove(this); + ASSERT(m_sampledEffect); + + m_sampledEffect->clear(); + m_sampledEffect = nullptr; cancelAnimationOnCompositor(); - m_activeInAnimationStack = false; - m_compositableValues.clear(); m_target->setNeedsAnimationStyleRecalc(); invalidate(); } -bool Animation::updateChildrenAndEffects() const +void Animation::updateChildrenAndEffects() const { if (!m_effect) - return false; - + return; if (isInEffect()) - return const_cast<Animation*>(this)->applyEffects(m_activeInAnimationStack); - - if (m_activeInAnimationStack) { + const_cast<Animation*>(this)->applyEffects(); + else if (m_sampledEffect) const_cast<Animation*>(this)->clearEffects(); - return true; - } - return false; } -double Animation::calculateTimeToEffectChange(double localTime, double timeToNextIteration) const +double Animation::calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const { - const double activeStartTime = startTime() + specified().startDelay; + const double start = startTimeInternal() + specifiedTiming().startDelay; + const double end = start + activeDurationInternal(); + switch (phase()) { case PhaseBefore: - return activeStartTime - localTime; + ASSERT(start >= localTime); + return forwards + ? start - localTime + : std::numeric_limits<double>::infinity(); case PhaseActive: - if (hasActiveAnimationsOnCompositor()) { + if (forwards && hasActiveAnimationsOnCompositor()) { + ASSERT(specifiedTiming().playbackRate == 1); // Need service to apply fill / fire events. - const double activeEndTime = activeStartTime + activeDuration(); - return std::min(activeEndTime - localTime, timeToNextIteration); + const double timeToEnd = end - localTime; + if (hasEvents()) { + return std::min(timeToEnd, timeToNextIteration); + } else { + return timeToEnd; + } } return 0; case PhaseAfter: + ASSERT(localTime >= end); // If this Animation 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 std::numeric_limits<double>::infinity(); - case PhaseNone: + return forwards + ? std::numeric_limits<double>::infinity() + : localTime - end; default: ASSERT_NOT_REACHED(); - return 0; + return std::numeric_limits<double>::infinity(); } } +void Animation::notifySampledEffectRemovedFromAnimationStack() +{ + ASSERT(m_sampledEffect); + m_sampledEffect = nullptr; +} + +#if !ENABLE(OILPAN) +void Animation::notifyElementDestroyed() +{ + // If our player is kept alive just by the sampledEffect, we might get our + // destructor called when we call SampledEffect::clear(), so we need to + // clear m_sampledEffect first. + m_target = nullptr; + clearEventDelegate(); + SampledEffect* sampledEffect = m_sampledEffect; + m_sampledEffect = nullptr; + if (sampledEffect) + sampledEffect->clear(); +} +#endif + bool Animation::isCandidateForAnimationOnCompositor() const { if (!effect() || !m_target) return false; - return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specified(), *effect()); + return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specifiedTiming(), *effect()); } -bool Animation::maybeStartAnimationOnCompositor() +bool Animation::maybeStartAnimationOnCompositor(double startTime) { ASSERT(!hasActiveAnimationsOnCompositor()); if (!isCandidateForAnimationOnCompositor()) return false; - if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target.get())) + if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target)) return false; - if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target.get(), specified(), *effect(), m_compositorAnimationIds)) + if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target, startTime, specifiedTiming(), *effect(), m_compositorAnimationIds)) return false; ASSERT(!m_compositorAnimationIds.isEmpty()); return true; @@ -183,12 +283,16 @@ bool Animation::affects(CSSPropertyID property) const void Animation::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; if (!m_target || !m_target->renderer()) return; for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i) - CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target.get(), m_compositorAnimationIds[i]); + CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target, m_compositorAnimationIds[i]); m_compositorAnimationIds.clear(); } @@ -198,7 +302,15 @@ void Animation::pauseAnimationForTestingOnCompositor(double pauseTime) if (!m_target || !m_target->renderer()) return; for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i) - CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target.get(), m_compositorAnimationIds[i], pauseTime); + CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target, m_compositorAnimationIds[i], pauseTime); +} + +void Animation::trace(Visitor* visitor) +{ + visitor->trace(m_target); + visitor->trace(m_effect); + visitor->trace(m_sampledEffect); + AnimationNode::trace(visitor); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/Animation.h b/chromium/third_party/WebKit/Source/core/animation/Animation.h index f4ad206d503..f0340516108 100644 --- a/chromium/third_party/WebKit/Source/core/animation/Animation.h +++ b/chromium/third_party/WebKit/Source/core/animation/Animation.h @@ -32,66 +32,81 @@ #define Animation_h #include "core/animation/AnimationEffect.h" -#include "core/animation/TimedItem.h" +#include "core/animation/AnimationNode.h" +#include "core/animation/EffectInput.h" +#include "core/animation/TimingInput.h" +#include "platform/heap/Handle.h" #include "wtf/RefPtr.h" namespace WebCore { +class Dictionary; class Element; +class ExceptionState; +class SampledEffect; -class Animation FINAL : public TimedItem { - +class Animation FINAL : public AnimationNode { public: enum Priority { DefaultPriority, TransitionPriority }; - static PassRefPtr<Animation> create(PassRefPtr<Element>, PassRefPtr<AnimationEffect>, const Timing&, Priority = DefaultPriority, PassOwnPtr<EventDelegate> = nullptr); - virtual bool isAnimation() const OVERRIDE FINAL { return true; } + static PassRefPtrWillBeRawPtr<Animation> create(Element*, PassRefPtrWillBeRawPtr<AnimationEffect>, const Timing&, Priority = DefaultPriority, PassOwnPtr<EventDelegate> = nullptr); + // Web Animations API Bindings constructors. + static PassRefPtrWillBeRawPtr<Animation> create(Element*, PassRefPtrWillBeRawPtr<AnimationEffect>, const Dictionary& timingInputDictionary); + static PassRefPtrWillBeRawPtr<Animation> create(Element*, PassRefPtrWillBeRawPtr<AnimationEffect>, double duration); + static PassRefPtrWillBeRawPtr<Animation> create(Element*, PassRefPtrWillBeRawPtr<AnimationEffect>); + static PassRefPtrWillBeRawPtr<Animation> create(Element*, const Vector<Dictionary>& keyframeDictionaryVector, const Dictionary& timingInputDictionary, ExceptionState&); + static PassRefPtrWillBeRawPtr<Animation> create(Element*, const Vector<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState&); + static PassRefPtrWillBeRawPtr<Animation> create(Element*, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState&); + + virtual ~Animation(); - const AnimationEffect::CompositableValueList* compositableValues() const - { - ASSERT(m_compositableValues); - return m_compositableValues.get(); - } + virtual bool isAnimation() const OVERRIDE { return true; } bool affects(CSSPropertyID) const; const AnimationEffect* effect() const { return m_effect.get(); } + AnimationEffect* effect() { return m_effect.get(); } Priority priority() const { return m_priority; } - Element* target() { return m_target.get(); } + Element* target() { return m_target; } + + void notifySampledEffectRemovedFromAnimationStack(); +#if !ENABLE(OILPAN) + void notifyElementDestroyed(); +#endif bool isCandidateForAnimationOnCompositor() const; - // Must only be called once and assumes to be part of a player without a start time. - bool maybeStartAnimationOnCompositor(); + // Must only be called once. + bool maybeStartAnimationOnCompositor(double startTime); bool hasActiveAnimationsOnCompositor() const; bool hasActiveAnimationsOnCompositor(CSSPropertyID) const; void cancelAnimationOnCompositor(); void pauseAnimationForTestingOnCompositor(double pauseTime); + virtual void trace(Visitor*); + protected: - // Returns whether style recalc was triggered. - virtual bool applyEffects(bool previouslyInEffect); - virtual void clearEffects(); - virtual bool updateChildrenAndEffects() const OVERRIDE FINAL; - virtual void didAttach() OVERRIDE FINAL; - virtual void willDetach() OVERRIDE FINAL; - virtual double calculateTimeToEffectChange(double inheritedTime, double timeToNextIteration) const OVERRIDE FINAL; + void applyEffects(); + void clearEffects(); + virtual void updateChildrenAndEffects() const OVERRIDE; + virtual void attach(AnimationPlayer*) OVERRIDE; + virtual void detach() OVERRIDE; + virtual void specifiedTimingChanged() OVERRIDE; + virtual double calculateTimeToEffectChange(bool forwards, double inheritedTime, double timeToNextIteration) const OVERRIDE; private: - Animation(PassRefPtr<Element>, PassRefPtr<AnimationEffect>, const Timing&, Priority, PassOwnPtr<EventDelegate>); - - RefPtr<Element> m_target; - RefPtr<AnimationEffect> m_effect; + Animation(Element*, PassRefPtrWillBeRawPtr<AnimationEffect>, const Timing&, Priority, PassOwnPtr<EventDelegate>); - bool m_activeInAnimationStack; - OwnPtr<AnimationEffect::CompositableValueList> m_compositableValues; + RawPtrWillBeMember<Element> m_target; + RefPtrWillBeMember<AnimationEffect> m_effect; + RawPtrWillBeMember<SampledEffect> m_sampledEffect; Priority m_priority; Vector<int> m_compositorAnimationIds; - friend class CSSAnimations; + friend class AnimationAnimationV8Test; }; -DEFINE_TYPE_CASTS(Animation, TimedItem, timedItem, timedItem->isAnimation(), timedItem.isAnimation()); +DEFINE_TYPE_CASTS(Animation, AnimationNode, animationNode, animationNode->isAnimation(), animationNode.isAnimation()); } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/TransitionTimeline.h b/chromium/third_party/WebKit/Source/core/animation/Animation.idl index 6d55f48772e..02b98bf8234 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/TransitionTimeline.h +++ b/chromium/third_party/WebKit/Source/core/animation/Animation.idl @@ -28,21 +28,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TransitionTimeline_h -#define TransitionTimeline_h +// http://dev.w3.org/fxtf/web-animations/#idl-def-Animation -#include "core/animation/DocumentTimeline.h" - -namespace WebCore { - -class TransitionTimeline FINAL : public DocumentTimeline { -public: - static PassRefPtr<TransitionTimeline> create(Document*, PassOwnPtr<PlatformTiming> = nullptr); - -private: - TransitionTimeline(Document*, PassOwnPtr<PlatformTiming>); +[ + // FIXME: should be optional union type http://crbug.com/240176 + Constructor(Element? target, sequence<Dictionary> keyframes), + Constructor(Element? target, sequence<Dictionary> keyframes, double timingInput), + Constructor(Element? target, sequence<Dictionary> keyframes, Dictionary timingInput), + RaisesException=Constructor, + RuntimeEnabled=WebAnimationsAPI, +] interface Animation : AnimationNode { }; - -} // namespace WebCore - -#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationClock.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationClock.cpp new file mode 100644 index 00000000000..d51c95e7aed --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationClock.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/AnimationClock.h" + +#include "wtf/CurrentTime.h" +#include <math.h> + +namespace { + +// FIXME: This is an approximation of time between frames, used when +// ticking the animation clock outside of animation frame callbacks. +// Ideally this would be generated by the compositor. +const double approximateFrameTime = 1 / 60.0; + +} + +namespace WebCore { + +unsigned AnimationClock::s_currentTask = 0; + +void AnimationClock::updateTime(double time) +{ + if (time > m_time) + m_time = time; + m_currentTask = s_currentTask; +} + +double AnimationClock::currentTime() +{ + if (m_currentTask != s_currentTask) { + const double currentTime = m_monotonicallyIncreasingTime(); + if (m_time < currentTime) { + // Advance to the first estimated frame after the current time. + const double frameShift = fmod(currentTime - m_time, approximateFrameTime); + const double newTime = currentTime + (approximateFrameTime - frameShift); + ASSERT(newTime >= currentTime); + ASSERT(newTime <= currentTime + approximateFrameTime); + updateTime(newTime); + } else { + m_currentTask = s_currentTask; + } + } + return m_time; +} + +void AnimationClock::resetTimeForTesting() +{ + m_time = 0; + m_currentTask = 0; + s_currentTask = 0; +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationClock.h b/chromium/third_party/WebKit/Source/core/animation/AnimationClock.h index f6a2cdb8499..b5bb0d9adc6 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimationClock.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationClock.h @@ -32,45 +32,33 @@ #define AnimationClock_h #include "wtf/CurrentTime.h" +#include "wtf/Noncopyable.h" #include "wtf/PassOwnPtr.h" +#include <limits> namespace WebCore { class AnimationClock { + WTF_MAKE_NONCOPYABLE(AnimationClock); public: - static PassOwnPtr<AnimationClock> create(WTF::TimeFunction monotonicallyIncreasingTime = WTF::monotonicallyIncreasingTime) - { - return adoptPtr(new AnimationClock(monotonicallyIncreasingTime)); - } - - void updateTime(double time) - { - if (time > m_time) - m_time = time; - m_frozen = true; - } - - double currentTime() + explicit AnimationClock(WTF::TimeFunction monotonicallyIncreasingTime = WTF::monotonicallyIncreasingTime) + : m_monotonicallyIncreasingTime(monotonicallyIncreasingTime) + , m_time(0) + , m_currentTask(std::numeric_limits<unsigned>::max()) { - if (!m_frozen) - updateTime(m_monotonicallyIncreasingTime()); - return m_time; } - void unfreeze() { m_frozen = false; } + void updateTime(double time); + double currentTime(); + void resetTimeForTesting(); - void resetTimeForTesting() { m_time = 0; m_frozen = true; } + static void notifyTaskStart() { ++s_currentTask; } private: - AnimationClock(WTF::TimeFunction monotonicallyIncreasingTime) - : m_monotonicallyIncreasingTime(monotonicallyIncreasingTime) - , m_time(0) - , m_frozen(false) - { - } WTF::TimeFunction m_monotonicallyIncreasingTime; double m_time; - bool m_frozen; + unsigned m_currentTask; + static unsigned s_currentTask; }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationClockTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationClockTest.cpp index 248da7badee..89412585331 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimationClockTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationClockTest.cpp @@ -39,41 +39,105 @@ using namespace WebCore; namespace { class AnimationAnimationClockTest : public ::testing::Test { +public: + AnimationAnimationClockTest() + : animationClock(mockTimeFunction) + { } protected: virtual void SetUp() { - animationClock = AnimationClock::create(mockTimeFunction); - mockTime = 200; + mockTime = 0; + animationClock.resetTimeForTesting(); } static double mockTimeFunction() { - return mockTime++; + return mockTime; } static double mockTime; - OwnPtr<AnimationClock> animationClock; + AnimationClock animationClock; }; double AnimationAnimationClockTest::mockTime; -TEST_F(AnimationAnimationClockTest, CurrentTime) +TEST_F(AnimationAnimationClockTest, TimeIsGreaterThanZeroForUnitTests) { - EXPECT_EQ(200, animationClock->currentTime()); - EXPECT_EQ(200, animationClock->currentTime()); - animationClock->unfreeze(); - EXPECT_EQ(201, animationClock->currentTime()); - EXPECT_EQ(201, animationClock->currentTime()); + AnimationClock clock; + // unit tests outside core/animation shouldn't need to do anything to get + // a non-zero currentTime(). + EXPECT_GT(clock.currentTime(), 0); } -TEST_F(AnimationAnimationClockTest, UpdateTime) +TEST_F(AnimationAnimationClockTest, TimeDoesNotChange) { - animationClock->updateTime(100); - EXPECT_EQ(100, animationClock->currentTime()); - EXPECT_EQ(100, animationClock->currentTime()); - animationClock->updateTime(150); - EXPECT_EQ(150, animationClock->currentTime()); - EXPECT_EQ(150, animationClock->currentTime()); + animationClock.updateTime(100); + EXPECT_EQ(100, animationClock.currentTime()); + EXPECT_EQ(100, animationClock.currentTime()); +} + +TEST_F(AnimationAnimationClockTest, TimeAdvancesWhenUpdated) +{ + animationClock.updateTime(100); + EXPECT_EQ(100, animationClock.currentTime()); + + animationClock.updateTime(200); + EXPECT_EQ(200, animationClock.currentTime()); +} + +TEST_F(AnimationAnimationClockTest, TimeAdvancesToTaskTime) +{ + animationClock.updateTime(100); + EXPECT_EQ(100, animationClock.currentTime()); + + mockTime = 150; + AnimationClock::notifyTaskStart(); + EXPECT_GE(animationClock.currentTime(), mockTime); +} + +TEST_F(AnimationAnimationClockTest, TimeAdvancesToTaskTimeOnlyWhenRequired) +{ + animationClock.updateTime(100); + EXPECT_EQ(100, animationClock.currentTime()); + + AnimationClock::notifyTaskStart(); + animationClock.updateTime(125); + EXPECT_EQ(125, animationClock.currentTime()); +} + +TEST_F(AnimationAnimationClockTest, UpdateTimeIsMonotonic) +{ + animationClock.updateTime(100); + EXPECT_EQ(100, animationClock.currentTime()); + + // Update can't go backwards. + animationClock.updateTime(50); + EXPECT_EQ(100, animationClock.currentTime()); + + mockTime = 50; + AnimationClock::notifyTaskStart(); + EXPECT_EQ(100, animationClock.currentTime()); + + mockTime = 150; + AnimationClock::notifyTaskStart(); + EXPECT_GE(animationClock.currentTime(), mockTime); + + // Update can't go backwards after advance to estimate. + animationClock.updateTime(100); + EXPECT_GE(animationClock.currentTime(), mockTime); +} + +TEST_F(AnimationAnimationClockTest, CurrentTimeUpdatesTask) +{ + animationClock.updateTime(100); + EXPECT_EQ(100, animationClock.currentTime()); + + mockTime = 100; + AnimationClock::notifyTaskStart(); + EXPECT_EQ(100, animationClock.currentTime()); + + mockTime = 150; + EXPECT_EQ(100, animationClock.currentTime()); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.h b/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.h index d675a5f4a9e..a8772648cf1 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.h @@ -31,38 +31,30 @@ #ifndef AnimationEffect_h #define AnimationEffect_h -#include "CSSPropertyNames.h" +#include "core/CSSPropertyNames.h" +#include "platform/heap/Handle.h" #include "wtf/HashMap.h" #include "wtf/PassOwnPtr.h" #include "wtf/RefCounted.h" namespace WebCore { -class AnimatableValue; +class Interpolation; -class AnimationEffect : public RefCounted<AnimationEffect> { +class AnimationEffect : public RefCountedWillBeGarbageCollectedFinalized<AnimationEffect> { public: enum CompositeOperation { CompositeReplace, CompositeAdd, }; - // Encapsulates the value which results from applying a set of composition operations onto an - // underlying value. It is used to represent the output of the effect phase of the Web - // Animations model. - class CompositableValue : public RefCounted<CompositableValue> { - public: - virtual ~CompositableValue() { } - virtual bool dependsOnUnderlyingValue() const = 0; - virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue*) const = 0; - }; virtual ~AnimationEffect() { } - typedef HashMap<CSSPropertyID, RefPtr<CompositableValue> > CompositableValueMap; - typedef Vector<std::pair<CSSPropertyID, RefPtr<CompositableValue> > > CompositableValueList; - virtual PassOwnPtr<CompositableValueList> sample(int iteration, double fraction) const = 0; + virtual PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample(int iteration, double fraction, double iterationDuration) const = 0; virtual bool affects(CSSPropertyID) { return false; }; - virtual bool isKeyframeAnimationEffect() const { return false; } + virtual bool isKeyframeEffectModel() const { return false; } + + virtual void trace(Visitor*) { } }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.idl b/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.idl new file mode 100644 index 00000000000..f5dc6ce2f57 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationEffect.idl @@ -0,0 +1,10 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + RuntimeEnabled=WebAnimationsAPI, + NoInterfaceObject, + WillBeGarbageCollected +] interface AnimationEffect { +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationHelpers.h b/chromium/third_party/WebKit/Source/core/animation/AnimationHelpers.h new file mode 100644 index 00000000000..daeb2d5b48f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationHelpers.h @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef AnimationHelpers_h +#define AnimationHelpers_h + +#include "core/css/parser/BisonCSSParser.h" +#include "wtf/text/StringBuilder.h" + +namespace WebCore { + +static inline CSSPropertyID camelCaseCSSPropertyNameToID(const String& propertyName) +{ + if (propertyName.find('-') != kNotFound) + return CSSPropertyInvalid; + + StringBuilder builder; + size_t position = 0; + size_t end; + while ((end = propertyName.find(isASCIIUpper, position)) != kNotFound) { + builder.append(propertyName.substring(position, end - position) + "-" + toASCIILower((propertyName)[end])); + position = end + 1; + } + builder.append(propertyName.substring(position)); + // Doesn't handle prefixed properties. + CSSPropertyID id = cssPropertyID(builder.toString()); + return id; +} + +} // namespace WebCore + +#endif // AnimationHelpers_h diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationHelpersTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationHelpersTest.cpp new file mode 100644 index 00000000000..df3727f7d98 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationHelpersTest.cpp @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/AnimationHelpers.h" + +#include <gtest/gtest.h> + +namespace WebCore { + +TEST(AnimationAnimationHelpersTest, ParseCamelCasePropertyNames) +{ + EXPECT_EQ(CSSPropertyInvalid, camelCaseCSSPropertyNameToID(String("line-height"))); + EXPECT_EQ(CSSPropertyLineHeight, camelCaseCSSPropertyNameToID(String("lineHeight"))); + EXPECT_EQ(CSSPropertyBorderTopWidth, camelCaseCSSPropertyNameToID(String("borderTopWidth"))); + EXPECT_EQ(CSSPropertyWidth, camelCaseCSSPropertyNameToID(String("width"))); + EXPECT_EQ(CSSPropertyInvalid, camelCaseCSSPropertyNameToID(String("Width"))); + EXPECT_EQ(CSSPropertyInvalid, camelCaseCSSPropertyNameToID(String("-webkit-transform"))); + EXPECT_EQ(CSSPropertyInvalid, camelCaseCSSPropertyNameToID(String("webkitTransform"))); + EXPECT_EQ(CSSPropertyInvalid, camelCaseCSSPropertyNameToID(String("cssFloat"))); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/TimedItem.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationNode.cpp index 3b7aa948c1f..1e5f863074b 100644 --- a/chromium/third_party/WebKit/Source/core/animation/TimedItem.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationNode.cpp @@ -29,120 +29,167 @@ */ #include "config.h" -#include "core/animation/TimedItem.h" +#include "core/animation/AnimationNode.h" -#include "core/animation/Player.h" -#include "core/animation/TimedItemCalculations.h" +#include "core/animation/AnimationNodeTiming.h" +#include "core/animation/AnimationPlayer.h" +#include "core/animation/TimingCalculations.h" namespace WebCore { -TimedItem::TimedItem(const Timing& timing, PassOwnPtr<EventDelegate> eventDelegate) - : m_parent(0) +namespace { + +Timing::FillMode resolvedFillMode(Timing::FillMode fillMode, bool isAnimation) +{ + if (fillMode != Timing::FillModeAuto) + return fillMode; + if (isAnimation) + return Timing::FillModeNone; + return Timing::FillModeBoth; +} + +} // namespace + +AnimationNode::AnimationNode(const Timing& timing, PassOwnPtr<EventDelegate> eventDelegate) + : m_parent(nullptr) , m_startTime(0) - , m_player(0) - , m_specified(timing) + , m_player(nullptr) + , m_timing(timing) , m_eventDelegate(eventDelegate) , m_calculated() - , m_isFirstSample(true) , m_needsUpdate(true) , m_lastUpdateTime(nullValue()) { - m_specified.assertValid(); + m_timing.assertValid(); +} + +double AnimationNode::iterationDuration() const +{ + double result = std::isnan(m_timing.iterationDuration) ? intrinsicIterationDuration() : m_timing.iterationDuration; + ASSERT(result >= 0); + return result; +} + +double AnimationNode::repeatedDuration() const +{ + const double result = multiplyZeroAlwaysGivesZero(iterationDuration(), m_timing.iterationCount); + ASSERT(result >= 0); + return result; +} + +double AnimationNode::activeDurationInternal() const +{ + const double result = m_timing.playbackRate + ? repeatedDuration() / std::abs(m_timing.playbackRate) + : std::numeric_limits<double>::infinity(); + ASSERT(result >= 0); + return result; } -bool TimedItem::updateInheritedTime(double inheritedTime) const +void AnimationNode::updateSpecifiedTiming(const Timing& timing) +{ + // FIXME: Test whether the timing is actually different? + m_timing = timing; + invalidate(); + if (m_player) + m_player->setOutdated(); + specifiedTimingChanged(); +} + +void AnimationNode::updateInheritedTime(double inheritedTime, TimingUpdateReason reason) const { bool needsUpdate = m_needsUpdate || (m_lastUpdateTime != inheritedTime && !(isNull(m_lastUpdateTime) && isNull(inheritedTime))); m_needsUpdate = false; m_lastUpdateTime = inheritedTime; - const double previousIteration = m_calculated.currentIteration; - const Phase previousPhase = m_calculated.phase; - const double localTime = inheritedTime - m_startTime; double timeToNextIteration = std::numeric_limits<double>::infinity(); if (needsUpdate) { - const double iterationDuration = m_specified.hasIterationDuration - ? m_specified.iterationDuration - : intrinsicIterationDuration(); - ASSERT(iterationDuration >= 0); - - // When iterationDuration = 0 and iterationCount = infinity, or vice- - // versa, repeatedDuration should be 0, not NaN as operator*() would give. - // FIXME: The spec is unclear about this. - const double repeatedDuration = multiplyZeroAlwaysGivesZero(iterationDuration, m_specified.iterationCount); - ASSERT(repeatedDuration >= 0); - const double activeDuration = m_specified.playbackRate - ? repeatedDuration / abs(m_specified.playbackRate) - : std::numeric_limits<double>::infinity(); - ASSERT(activeDuration >= 0); - - const Phase currentPhase = calculatePhase(activeDuration, localTime, m_specified); + const double activeDuration = this->activeDurationInternal(); + + const Phase currentPhase = calculatePhase(activeDuration, localTime, m_timing); // FIXME: parentPhase depends on groups being implemented. - const TimedItem::Phase parentPhase = TimedItem::PhaseActive; - const double activeTime = calculateActiveTime(activeDuration, localTime, parentPhase, currentPhase, m_specified); + const AnimationNode::Phase parentPhase = AnimationNode::PhaseActive; + const double activeTime = calculateActiveTime(activeDuration, resolvedFillMode(m_timing.fillMode, isAnimation()), localTime, parentPhase, currentPhase, m_timing); double currentIteration; double timeFraction; - if (iterationDuration) { - const double startOffset = multiplyZeroAlwaysGivesZero(m_specified.iterationStart, iterationDuration); + if (const double iterationDuration = this->iterationDuration()) { + const double startOffset = multiplyZeroAlwaysGivesZero(m_timing.iterationStart, iterationDuration); ASSERT(startOffset >= 0); - const double scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime, startOffset, m_specified); - const double iterationTime = calculateIterationTime(iterationDuration, repeatedDuration, scaledActiveTime, startOffset, m_specified); + const double scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime, startOffset, m_timing); + const double iterationTime = calculateIterationTime(iterationDuration, repeatedDuration(), scaledActiveTime, startOffset, m_timing); - currentIteration = calculateCurrentIteration(iterationDuration, iterationTime, scaledActiveTime, m_specified); - timeFraction = calculateTransformedTime(currentIteration, iterationDuration, iterationTime, m_specified) / iterationDuration; + currentIteration = calculateCurrentIteration(iterationDuration, iterationTime, scaledActiveTime, m_timing); + timeFraction = calculateTransformedTime(currentIteration, iterationDuration, iterationTime, m_timing) / iterationDuration; if (!isNull(iterationTime)) { - timeToNextIteration = (iterationDuration - iterationTime) / abs(m_specified.playbackRate); + timeToNextIteration = (iterationDuration - iterationTime) / std::abs(m_timing.playbackRate); if (activeDuration - activeTime < timeToNextIteration) timeToNextIteration = std::numeric_limits<double>::infinity(); } } else { const double localIterationDuration = 1; - const double localRepeatedDuration = localIterationDuration * m_specified.iterationCount; + const double localRepeatedDuration = localIterationDuration * m_timing.iterationCount; ASSERT(localRepeatedDuration >= 0); - const double localActiveDuration = m_specified.playbackRate ? localRepeatedDuration / abs(m_specified.playbackRate) : std::numeric_limits<double>::infinity(); + const double localActiveDuration = m_timing.playbackRate ? localRepeatedDuration / std::abs(m_timing.playbackRate) : std::numeric_limits<double>::infinity(); ASSERT(localActiveDuration >= 0); - const double localLocalTime = localTime < m_specified.startDelay ? localTime : localActiveDuration + m_specified.startDelay; - const TimedItem::Phase localCurrentPhase = calculatePhase(localActiveDuration, localLocalTime, m_specified); - const double localActiveTime = calculateActiveTime(localActiveDuration, localLocalTime, parentPhase, localCurrentPhase, m_specified); - const double startOffset = m_specified.iterationStart * localIterationDuration; + const double localLocalTime = localTime < m_timing.startDelay ? localTime : localActiveDuration + m_timing.startDelay; + const AnimationNode::Phase localCurrentPhase = calculatePhase(localActiveDuration, localLocalTime, m_timing); + const double localActiveTime = calculateActiveTime(localActiveDuration, resolvedFillMode(m_timing.fillMode, isAnimation()), localLocalTime, parentPhase, localCurrentPhase, m_timing); + const double startOffset = m_timing.iterationStart * localIterationDuration; ASSERT(startOffset >= 0); - const double scaledActiveTime = calculateScaledActiveTime(localActiveDuration, localActiveTime, startOffset, m_specified); - const double iterationTime = calculateIterationTime(localIterationDuration, localRepeatedDuration, scaledActiveTime, startOffset, m_specified); + const double scaledActiveTime = calculateScaledActiveTime(localActiveDuration, localActiveTime, startOffset, m_timing); + const double iterationTime = calculateIterationTime(localIterationDuration, localRepeatedDuration, scaledActiveTime, startOffset, m_timing); - currentIteration = calculateCurrentIteration(localIterationDuration, iterationTime, scaledActiveTime, m_specified); - timeFraction = calculateTransformedTime(currentIteration, localIterationDuration, iterationTime, m_specified); + currentIteration = calculateCurrentIteration(localIterationDuration, iterationTime, scaledActiveTime, m_timing); + timeFraction = calculateTransformedTime(currentIteration, localIterationDuration, iterationTime, m_timing); } m_calculated.currentIteration = currentIteration; - m_calculated.activeDuration = activeDuration; m_calculated.timeFraction = timeFraction; m_calculated.phase = currentPhase; m_calculated.isInEffect = !isNull(activeTime); m_calculated.isInPlay = phase() == PhaseActive && (!m_parent || m_parent->isInPlay()); m_calculated.isCurrent = phase() == PhaseBefore || isInPlay() || (m_parent && m_parent->isCurrent()); + m_calculated.localTime = m_lastUpdateTime - m_startTime; } // Test for events even if timing didn't need an update as the player may have gained a start time. // FIXME: Refactor so that we can ASSERT(m_player) here, this is currently required to be nullable for testing. - if (!m_player || m_player->hasStartTime()) { - // This logic is specific to CSS animation events and assumes that all - // animations start after the DocumentTimeline has started. - if (m_eventDelegate && (m_isFirstSample || previousPhase != phase() || (phase() == PhaseActive && previousIteration != m_calculated.currentIteration))) - m_eventDelegate->onEventCondition(this, m_isFirstSample, previousPhase, previousIteration); - m_isFirstSample = false; + if (reason == TimingUpdateForAnimationFrame && (!m_player || m_player->hasStartTime())) { + if (m_eventDelegate) + m_eventDelegate->onEventCondition(this); } - bool didTriggerStyleRecalc = false; if (needsUpdate) { // FIXME: This probably shouldn't be recursive. - didTriggerStyleRecalc = updateChildrenAndEffects(); - m_calculated.timeToEffectChange = calculateTimeToEffectChange(localTime, timeToNextIteration); + updateChildrenAndEffects(); + m_calculated.timeToForwardsEffectChange = calculateTimeToEffectChange(true, localTime, timeToNextIteration); + m_calculated.timeToReverseEffectChange = calculateTimeToEffectChange(false, localTime, timeToNextIteration); } - return didTriggerStyleRecalc; +} + +const AnimationNode::CalculatedTiming& AnimationNode::ensureCalculated() const +{ + if (!m_player) + return m_calculated; + if (m_player->outdated()) + m_player->update(TimingUpdateOnDemand); + ASSERT(!m_player->outdated()); + return m_calculated; +} + +PassRefPtrWillBeRawPtr<AnimationNodeTiming> AnimationNode::timing() +{ + return AnimationNodeTiming::create(this); +} + +void AnimationNode::trace(Visitor* visitor) +{ + visitor->trace(m_parent); + visitor->trace(m_player); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/TimedItem.h b/chromium/third_party/WebKit/Source/core/animation/AnimationNode.h index 82da5133f58..4c548541b75 100644 --- a/chromium/third_party/WebKit/Source/core/animation/TimedItem.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationNode.h @@ -28,18 +28,25 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TimedItem_h -#define TimedItem_h +#ifndef AnimationNode_h +#define AnimationNode_h #include "core/animation/Timing.h" +#include "platform/heap/Handle.h" #include "wtf/OwnPtr.h" #include "wtf/PassOwnPtr.h" #include "wtf/RefCounted.h" namespace WebCore { -class Player; -class TimedItem; +class AnimationPlayer; +class AnimationNode; +class AnimationNodeTiming; + +enum TimingUpdateReason { + TimingUpdateOnDemand, + TimingUpdateForAnimationFrame +}; static inline bool isNull(double value) { @@ -51,8 +58,8 @@ static inline double nullValue() return std::numeric_limits<double>::quiet_NaN(); } -class TimedItem : public RefCounted<TimedItem> { - friend class Player; // Calls attach/detach, updateInheritedTime. +class AnimationNode : public RefCountedWillBeGarbageCollectedFinalized<AnimationNode> { + friend class AnimationPlayer; // Calls attach/detach, updateInheritedTime. public: // Note that logic in CSSAnimations depends on the order of these values. enum Phase { @@ -65,10 +72,10 @@ public: class EventDelegate { public: virtual ~EventDelegate() { }; - virtual void onEventCondition(const TimedItem*, bool isFirstSample, Phase previousPhase, double previousIteration) = 0; + virtual void onEventCondition(const AnimationNode*) = 0; }; - virtual ~TimedItem() { } + virtual ~AnimationNode() { } virtual bool isAnimation() const { return false; } @@ -76,71 +83,87 @@ public: bool isCurrent() const { return ensureCalculated().isCurrent; } bool isInEffect() const { return ensureCalculated().isInEffect; } bool isInPlay() const { return ensureCalculated().isInPlay; } - double timeToEffectChange() const { return ensureCalculated().timeToEffectChange; } + double timeToForwardsEffectChange() const { return ensureCalculated().timeToForwardsEffectChange; } + double timeToReverseEffectChange() const { return ensureCalculated().timeToReverseEffectChange; } double currentIteration() const { return ensureCalculated().currentIteration; } - double activeDuration() const { return ensureCalculated().activeDuration; } + double iterationDuration() const; + + // This method returns time in ms as it is unused except via the API. + double duration() const { return iterationDuration() * 1000; } + + double activeDuration() const { return activeDurationInternal() * 1000; } + double activeDurationInternal() const; double timeFraction() const { return ensureCalculated().timeFraction; } - double startTime() const { return m_startTime; } - const Player* player() const { return m_player; } - Player* player() { return m_player; } - const Timing& specified() const { return m_specified; } + double startTime() const { return m_startTime * 1000; } + double startTimeInternal() const { return m_startTime; } + double endTime() const { return endTimeInternal() * 1000; } + double endTimeInternal() const { return startTime() + specifiedTiming().startDelay + activeDurationInternal() + specifiedTiming().endDelay; } + + const AnimationPlayer* player() const { return m_player; } + AnimationPlayer* player() { return m_player; } + AnimationPlayer* player(bool& isNull) { isNull = !m_player; return m_player; } + const Timing& specifiedTiming() const { return m_timing; } + PassRefPtrWillBeRawPtr<AnimationNodeTiming> timing(); + void updateSpecifiedTiming(const Timing&); + + // This method returns time in ms as it is unused except via the API. + double localTime(bool& isNull) const { isNull = !m_player; return ensureCalculated().localTime * 1000; } + double currentIteration(bool& isNull) const { isNull = !ensureCalculated().isInEffect; return ensureCalculated().currentIteration; } + + virtual void trace(Visitor*); protected: - TimedItem(const Timing&, PassOwnPtr<EventDelegate> = nullptr); + explicit AnimationNode(const Timing&, PassOwnPtr<EventDelegate> = nullptr); - // When TimedItem receives a new inherited time via updateInheritedTime + // When AnimationNode receives a new inherited time via updateInheritedTime // it will (if necessary) recalculate timings and (if necessary) call // updateChildrenAndEffects. - // Returns whether style recalc was triggered. - bool updateInheritedTime(double inheritedTime) const; + void updateInheritedTime(double inheritedTime, TimingUpdateReason) const; void invalidate() const { m_needsUpdate = true; }; + bool hasEvents() const { return m_eventDelegate; } + void clearEventDelegate() { m_eventDelegate = nullptr; } -private: - // Returns whether style recalc was triggered. - virtual bool updateChildrenAndEffects() const = 0; - virtual double intrinsicIterationDuration() const { return 0; }; - virtual double calculateTimeToEffectChange(double localTime, double timeToNextIteration) const = 0; - virtual void didAttach() { }; - virtual void willDetach() { }; - - void attach(Player* player) + virtual void attach(AnimationPlayer* player) { m_player = player; - didAttach(); - }; + } - void detach() + virtual void detach() { ASSERT(m_player); - willDetach(); - m_player = 0; - }; + m_player = nullptr; + } + + double repeatedDuration() const; + + virtual void updateChildrenAndEffects() const = 0; + virtual double intrinsicIterationDuration() const { return 0; }; + virtual double calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const = 0; + virtual void specifiedTimingChanged() { } // FIXME: m_parent and m_startTime are placeholders, they depend on timing groups. - TimedItem* const m_parent; + RawPtrWillBeMember<AnimationNode> m_parent; const double m_startTime; - Player* m_player; - Timing m_specified; + RawPtrWillBeMember<AnimationPlayer> m_player; + Timing m_timing; OwnPtr<EventDelegate> m_eventDelegate; - // FIXME: Should be versioned by monotonic value on player. mutable struct CalculatedTiming { - double activeDuration; Phase phase; double currentIteration; double timeFraction; bool isCurrent; bool isInEffect; bool isInPlay; - double timeToEffectChange; + double localTime; + double timeToForwardsEffectChange; + double timeToReverseEffectChange; } m_calculated; - mutable bool m_isFirstSample; mutable bool m_needsUpdate; mutable double m_lastUpdateTime; - // FIXME: Should check the version and reinherit time if inconsistent. - const CalculatedTiming& ensureCalculated() const { return m_calculated; } + const CalculatedTiming& ensureCalculated() const; }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/TransitionTimeline.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationNode.idl index 33ec27f2b0b..c04ede67dfc 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/TransitionTimeline.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationNode.idl @@ -28,25 +28,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "core/animation/css/TransitionTimeline.h" +[ + RuntimeEnabled=WebAnimationsAPI, + WillBeGarbageCollected, +] interface AnimationNode { + // Playback state + readonly attribute double? localTime; + readonly attribute unsigned long? currentIteration; -#include "core/animation/ActiveAnimations.h" -#include "core/animation/AnimationClock.h" -#include "core/animation/AnimationStack.h" + // Calculated timing + readonly attribute double startTime; + readonly attribute double duration; + readonly attribute double activeDuration; + readonly attribute double endTime; -namespace WebCore { - -PassRefPtr<TransitionTimeline> TransitionTimeline::create(Document* document, PassOwnPtr<PlatformTiming> timing) -{ - return adoptRef(new TransitionTimeline(document, timing)); -} - -TransitionTimeline::TransitionTimeline(Document* document, PassOwnPtr<PlatformTiming> timing) - : DocumentTimeline(document, timing) -{ - setZeroTime(document->animationClock().currentTime()); - document->animationClock().unfreeze(); -} - -} // namespace WebCore + readonly attribute Timing timing; + readonly attribute AnimationPlayer? player; +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTest.cpp new file mode 100644 index 00000000000..944c3c4f63a --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTest.cpp @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2013, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/AnimationNode.h" + +#include <gtest/gtest.h> + +using namespace WebCore; + +namespace { + +class TestAnimationNodeEventDelegate : public AnimationNode::EventDelegate { +public: + virtual void onEventCondition(const AnimationNode* animationNode) OVERRIDE + { + m_eventTriggered = true; + + } + void reset() + { + m_eventTriggered = false; + } + bool eventTriggered() { return m_eventTriggered; } + +private: + bool m_eventTriggered; +}; + +class TestAnimationNode : public AnimationNode { +public: + static PassRefPtrWillBeRawPtr<TestAnimationNode> create(const Timing& specified) + { + return adoptRefWillBeNoop(new TestAnimationNode(specified, new TestAnimationNodeEventDelegate())); + } + + void updateInheritedTime(double time) + { + updateInheritedTime(time, TimingUpdateForAnimationFrame); + } + + void updateInheritedTime(double time, TimingUpdateReason reason) + { + m_eventDelegate->reset(); + AnimationNode::updateInheritedTime(time, reason); + } + + virtual void updateChildrenAndEffects() const OVERRIDE { } + void willDetach() { } + TestAnimationNodeEventDelegate* eventDelegate() { return m_eventDelegate; } + virtual double calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const OVERRIDE + { + m_localTime = localTime; + m_timeToNextIteration = timeToNextIteration; + return -1; + } + double takeLocalTime() + { + const double result = m_localTime; + m_localTime = nullValue(); + return result; + } + + double takeTimeToNextIteration() + { + const double result = m_timeToNextIteration; + m_timeToNextIteration = nullValue(); + return result; + } + +private: + TestAnimationNode(const Timing& specified, TestAnimationNodeEventDelegate* eventDelegate) + : AnimationNode(specified, adoptPtr(eventDelegate)) + , m_eventDelegate(eventDelegate) + { + } + + TestAnimationNodeEventDelegate* m_eventDelegate; + mutable double m_localTime; + mutable double m_timeToNextIteration; +}; + +TEST(AnimationAnimationNodeTest, Sanity) +{ + Timing timing; + timing.iterationDuration = 2; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + EXPECT_EQ(0, animationNode->startTime()); + + animationNode->updateInheritedTime(0); + + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->startTime()); + EXPECT_EQ(2, animationNode->activeDurationInternal()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->startTime()); + EXPECT_EQ(2, animationNode->activeDurationInternal()); + EXPECT_EQ(0.5, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2); + + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->startTime()); + EXPECT_EQ(2, animationNode->activeDurationInternal()); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(3); + + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->startTime()); + EXPECT_EQ(2, animationNode->activeDurationInternal()); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, FillAuto) +{ + Timing timing; + timing.iterationDuration = 1; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, FillForwards) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeForwards; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(2); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, FillBackwards) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeBackwards; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2); + EXPECT_TRUE(isNull(animationNode->timeFraction())); +} + +TEST(AnimationAnimationNodeTest, FillBoth) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeBoth; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, StartDelay) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeForwards; + timing.startDelay = 0.5; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0.5); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1.5); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroIteration) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = 0; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_TRUE(isNull(animationNode->currentIteration())); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, InfiniteIteration) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = std::numeric_limits<double>::infinity(); + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_TRUE(isNull(animationNode->currentIteration())); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, Iteration) +{ + Timing timing; + timing.iterationCount = 2; + timing.iterationDuration = 2; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0.5, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(5); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, IterationStart) +{ + Timing timing; + timing.iterationStart = 1.2; + timing.iterationCount = 2.2; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeBoth; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_NEAR(0.2, animationNode->timeFraction(), 0.000000000000001); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_NEAR(0.2, animationNode->timeFraction(), 0.000000000000001); + + animationNode->updateInheritedTime(10); + EXPECT_EQ(3, animationNode->currentIteration()); + EXPECT_NEAR(0.4, animationNode->timeFraction(), 0.000000000000001); +} + +TEST(AnimationAnimationNodeTest, IterationAlternate) +{ + Timing timing; + timing.iterationCount = 10; + timing.iterationDuration = 1; + timing.direction = Timing::PlaybackDirectionAlternate; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0.75); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0.75, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1.75); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(0.25, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2.75); + EXPECT_EQ(2, animationNode->currentIteration()); + EXPECT_EQ(0.75, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, IterationAlternateReverse) +{ + Timing timing; + timing.iterationCount = 10; + timing.iterationDuration = 1; + timing.direction = Timing::PlaybackDirectionAlternateReverse; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0.75); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0.25, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1.75); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(0.75, animationNode->timeFraction()); + + animationNode->updateInheritedTime(2.75); + EXPECT_EQ(2, animationNode->currentIteration()); + EXPECT_EQ(0.25, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationSanity) +{ + Timing timing; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + EXPECT_EQ(0, animationNode->startTime()); + + animationNode->updateInheritedTime(0); + + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->startTime()); + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->startTime()); + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationFillForwards) +{ + Timing timing; + timing.fillMode = Timing::FillModeForwards; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationFillBackwards) +{ + Timing timing; + timing.fillMode = Timing::FillModeBackwards; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(0); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(1); + EXPECT_TRUE(isNull(animationNode->timeFraction())); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationFillBoth) +{ + Timing timing; + timing.fillMode = Timing::FillModeBoth; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationStartDelay) +{ + Timing timing; + timing.fillMode = Timing::FillModeForwards; + timing.startDelay = 0.5; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0.5); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1.5); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationIterationStartAndCount) +{ + Timing timing; + timing.iterationStart = 0.1; + timing.iterationCount = 0.2; + timing.fillMode = Timing::FillModeBoth; + timing.startDelay = 0.3; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(0.1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(0.3); + EXPECT_DOUBLE_EQ(0.3, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_DOUBLE_EQ(0.3, animationNode->timeFraction()); +} + +// FIXME: Needs specification work. +TEST(AnimationAnimationNodeTest, ZeroDurationInfiniteIteration) +{ + Timing timing; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = std::numeric_limits<double>::infinity(); + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_TRUE(isNull(animationNode->currentIteration())); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->currentIteration()); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationIteration) +{ + Timing timing; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = 2; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_TRUE(isNull(animationNode->currentIteration())); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationIterationStart) +{ + Timing timing; + timing.iterationStart = 1.2; + timing.iterationCount = 2.2; + timing.fillMode = Timing::FillModeBoth; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_NEAR(0.2, animationNode->timeFraction(), 0.000000000000001); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(3, animationNode->currentIteration()); + EXPECT_NEAR(0.4, animationNode->timeFraction(), 0.000000000000001); + + animationNode->updateInheritedTime(10); + EXPECT_EQ(3, animationNode->currentIteration()); + EXPECT_NEAR(0.4, animationNode->timeFraction(), 0.000000000000001); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationIterationAlternate) +{ + Timing timing; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = 2; + timing.direction = Timing::PlaybackDirectionAlternate; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_TRUE(isNull(animationNode->currentIteration())); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, ZeroDurationIterationAlternateReverse) +{ + Timing timing; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = 2; + timing.direction = Timing::PlaybackDirectionAlternateReverse; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(-1); + EXPECT_TRUE(isNull(animationNode->currentIteration())); + EXPECT_TRUE(isNull(animationNode->timeFraction())); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(1, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + EXPECT_EQ(1, animationNode->currentIteration()); + EXPECT_EQ(1, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, InfiniteDurationSanity) +{ + Timing timing; + timing.iterationDuration = std::numeric_limits<double>::infinity(); + timing.iterationCount = 1; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + EXPECT_EQ(0, animationNode->startTime()); + + animationNode->updateInheritedTime(0); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +// FIXME: Needs specification work. +TEST(AnimationAnimationNodeTest, InfiniteDurationZeroIterations) +{ + Timing timing; + timing.iterationDuration = std::numeric_limits<double>::infinity(); + timing.iterationCount = 0; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + EXPECT_EQ(0, animationNode->startTime()); + + animationNode->updateInheritedTime(0); + + EXPECT_EQ(0, animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, InfiniteDurationInfiniteIterations) +{ + Timing timing; + timing.iterationDuration = std::numeric_limits<double>::infinity(); + timing.iterationCount = std::numeric_limits<double>::infinity(); + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + EXPECT_EQ(0, animationNode->startTime()); + + animationNode->updateInheritedTime(0); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(1); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, InfiniteDurationZeroPlaybackRate) +{ + Timing timing; + timing.iterationDuration = std::numeric_limits<double>::infinity(); + timing.playbackRate = 0; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + EXPECT_EQ(0, animationNode->startTime()); + + animationNode->updateInheritedTime(0); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_TRUE(animationNode->isInPlay()); + EXPECT_TRUE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); + + animationNode->updateInheritedTime(std::numeric_limits<double>::infinity()); + + EXPECT_EQ(std::numeric_limits<double>::infinity(), animationNode->activeDurationInternal()); + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_FALSE(animationNode->isInPlay()); + EXPECT_FALSE(animationNode->isCurrent()); + EXPECT_TRUE(animationNode->isInEffect()); + EXPECT_EQ(0, animationNode->currentIteration()); + EXPECT_EQ(0, animationNode->timeFraction()); +} + +TEST(AnimationAnimationNodeTest, EndTime) +{ + Timing timing; + timing.startDelay = 1; + timing.endDelay = 2; + timing.iterationDuration = 4; + timing.iterationCount = 2; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + EXPECT_EQ(11, animationNode->endTimeInternal()); +} + +TEST(AnimationAnimationNodeTest, Events) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeForwards; + timing.iterationCount = 2; + timing.startDelay = 1; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0.0, TimingUpdateOnDemand); + EXPECT_FALSE(animationNode->eventDelegate()->eventTriggered()); + + animationNode->updateInheritedTime(0.0, TimingUpdateForAnimationFrame); + EXPECT_TRUE(animationNode->eventDelegate()->eventTriggered()); + + animationNode->updateInheritedTime(1.5, TimingUpdateOnDemand); + EXPECT_FALSE(animationNode->eventDelegate()->eventTriggered()); + + animationNode->updateInheritedTime(1.5, TimingUpdateForAnimationFrame); + EXPECT_TRUE(animationNode->eventDelegate()->eventTriggered()); + +} + +TEST(AnimationAnimationNodeTest, TimeToEffectChange) +{ + Timing timing; + timing.iterationDuration = 1; + timing.fillMode = Timing::FillModeForwards; + timing.iterationStart = 0.2; + timing.iterationCount = 2.5; + timing.startDelay = 1; + timing.direction = Timing::PlaybackDirectionAlternate; + RefPtrWillBeRawPtr<TestAnimationNode> animationNode = TestAnimationNode::create(timing); + + animationNode->updateInheritedTime(0); + EXPECT_EQ(0, animationNode->takeLocalTime()); + EXPECT_TRUE(std::isinf(animationNode->takeTimeToNextIteration())); + + // Normal iteration. + animationNode->updateInheritedTime(1.75); + EXPECT_EQ(1.75, animationNode->takeLocalTime()); + EXPECT_NEAR(0.05, animationNode->takeTimeToNextIteration(), 0.000000000000001); + + // Reverse iteration. + animationNode->updateInheritedTime(2.75); + EXPECT_EQ(2.75, animationNode->takeLocalTime()); + EXPECT_NEAR(0.05, animationNode->takeTimeToNextIteration(), 0.000000000000001); + + // Item ends before iteration finishes. + animationNode->updateInheritedTime(3.4); + EXPECT_EQ(AnimationNode::PhaseActive, animationNode->phase()); + EXPECT_EQ(3.4, animationNode->takeLocalTime()); + EXPECT_TRUE(std::isinf(animationNode->takeTimeToNextIteration())); + + // Item has finished. + animationNode->updateInheritedTime(3.5); + EXPECT_EQ(AnimationNode::PhaseAfter, animationNode->phase()); + EXPECT_EQ(3.5, animationNode->takeLocalTime()); + EXPECT_TRUE(std::isinf(animationNode->takeTimeToNextIteration())); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTiming.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTiming.cpp new file mode 100644 index 00000000000..9c3ade81ab5 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTiming.cpp @@ -0,0 +1,180 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/AnimationNodeTiming.h" + +#include "core/animation/Animation.h" +#include "core/animation/AnimationNode.h" +#include "platform/animation/TimingFunction.h" + +namespace WebCore { + +PassRefPtrWillBeRawPtr<AnimationNodeTiming> AnimationNodeTiming::create(AnimationNode* parent) +{ + return adoptRefWillBeNoop(new AnimationNodeTiming(parent)); +} + +AnimationNodeTiming::AnimationNodeTiming(AnimationNode* parent) +: m_parent(parent) +{ +} + +double AnimationNodeTiming::delay() +{ + return m_parent->specifiedTiming().startDelay * 1000; +} + +double AnimationNodeTiming::endDelay() +{ + return m_parent->specifiedTiming().endDelay * 1000; +} + +String AnimationNodeTiming::fill() +{ + Timing::FillMode fillMode = m_parent->specifiedTiming().fillMode; + switch (fillMode) { + case Timing::FillModeNone: + return "none"; + case Timing::FillModeForwards: + return "forwards"; + case Timing::FillModeBackwards: + return "backwards"; + case Timing::FillModeBoth: + return "both"; + case Timing::FillModeAuto: + return "auto"; + } + ASSERT_NOT_REACHED(); + return "auto"; +} + +double AnimationNodeTiming::iterationStart() +{ + return m_parent->specifiedTiming().iterationStart; +} + +double AnimationNodeTiming::iterations() +{ + return m_parent->specifiedTiming().iterationCount; +} + +// This logic was copied from the example in bindings/tests/idls/TestInterface.idl +// and bindings/tests/results/V8TestInterface.cpp. +// FIXME: It might be possible to have 'duration' defined as an attribute in the idl. +// If possible, fix will be in a follow-up patch. +void AnimationNodeTiming::getDuration(String propertyName, bool& element0Enabled, double& element0, bool& element1Enabled, String& element1) +{ + if (propertyName != "duration") + return; + + if (std::isnan(m_parent->specifiedTiming().iterationDuration)) { + element1Enabled = true; + element1 = "auto"; + return; + } + element0Enabled = true; + element0 = m_parent->specifiedTiming().iterationDuration * 1000; + return; +} + +double AnimationNodeTiming::playbackRate() +{ + return m_parent->specifiedTiming().playbackRate; +} + +String AnimationNodeTiming::direction() +{ + Timing::PlaybackDirection direction = m_parent->specifiedTiming().direction; + switch (direction) { + case Timing::PlaybackDirectionNormal: + return "normal"; + case Timing::PlaybackDirectionReverse: + return "reverse"; + case Timing::PlaybackDirectionAlternate: + return "alternate"; + case Timing::PlaybackDirectionAlternateReverse: + return "alternate-reverse"; + } + ASSERT_NOT_REACHED(); + return "normal"; +} + +String AnimationNodeTiming::easing() +{ + return m_parent->specifiedTiming().timingFunction->toString(); +} + +void AnimationNodeTiming::setDelay(double delay) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setStartDelay(timing, delay); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::setEndDelay(double endDelay) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setEndDelay(timing, endDelay); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::setFill(String fill) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setFillMode(timing, fill); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::setIterationStart(double iterationStart) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setIterationStart(timing, iterationStart); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::setIterations(double iterations) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setIterationCount(timing, iterations); + m_parent->updateSpecifiedTiming(timing); +} + +bool AnimationNodeTiming::setDuration(String name, double duration) +{ + if (name != "duration") + return false; + Timing timing = m_parent->specifiedTiming(); + TimingInput::setIterationDuration(timing, duration); + m_parent->updateSpecifiedTiming(timing); + return true; +} + +void AnimationNodeTiming::setPlaybackRate(double playbackRate) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setPlaybackRate(timing, playbackRate); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::setDirection(String direction) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setPlaybackDirection(timing, direction); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::setEasing(String easing) +{ + Timing timing = m_parent->specifiedTiming(); + TimingInput::setTimingFunction(timing, easing); + m_parent->updateSpecifiedTiming(timing); +} + +void AnimationNodeTiming::trace(Visitor* visitor) +{ + visitor->trace(m_parent); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTiming.h b/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTiming.h new file mode 100644 index 00000000000..c6570ec501f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationNodeTiming.h @@ -0,0 +1,46 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef AnimationNodeTiming_h +#define AnimationNodeTiming_h + +#include "core/animation/AnimationNode.h" +#include "wtf/RefCounted.h" +#include "wtf/text/WTFString.h" + +namespace WebCore { + +class AnimationNodeTiming : public RefCountedWillBeGarbageCollectedFinalized<AnimationNodeTiming> { +public: + static PassRefPtrWillBeRawPtr<AnimationNodeTiming> create(AnimationNode* parent); + double delay(); + double endDelay(); + String fill(); + double iterationStart(); + double iterations(); + void getDuration(String propertyName, bool& element0Enabled, double& element0, bool& element1Enabled, String& element1); + double playbackRate(); + String direction(); + String easing(); + + void setDelay(double); + void setEndDelay(double); + void setFill(String); + void setIterationStart(double); + void setIterations(double); + bool setDuration(String name, double duration); + void setPlaybackRate(double); + void setDirection(String); + void setEasing(String); + + void trace(Visitor*); + +private: + RefPtrWillBeMember<AnimationNode> m_parent; + explicit AnimationNodeTiming(AnimationNode*); +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.cpp new file mode 100644 index 00000000000..a99fec82f98 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.cpp @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/AnimationPlayer.h" + +#include "core/animation/Animation.h" +#include "core/animation/AnimationTimeline.h" +#include "core/dom/Document.h" +#include "core/events/AnimationPlayerEvent.h" +#include "core/frame/UseCounter.h" + +namespace WebCore { + +namespace { + +static unsigned nextSequenceNumber() +{ + static unsigned next = 0; + return ++next; +} + +} + +PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content) +{ + RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeRefCountedGarbageCollected(new AnimationPlayer(executionContext, timeline, content)); + player->suspendIfNeeded(); + return player.release(); +} + +AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content) + : ActiveDOMObject(executionContext) + , m_playbackRate(1) + , m_startTime(nullValue()) + , m_holdTime(nullValue()) + , m_storedTimeLag(0) + , m_sortInfo(nextSequenceNumber(), timeline.effectiveTime()) + , m_content(content) + , m_timeline(&timeline) + , m_paused(false) + , m_held(false) + , m_isPausedForTesting(false) + , m_outdated(true) + , m_finished(false) +{ + if (m_content) { + if (m_content->player()) + m_content->player()->cancel(); + m_content->attach(this); + } +} + +AnimationPlayer::~AnimationPlayer() +{ +#if !ENABLE(OILPAN) + if (m_content) + m_content->detach(); + if (m_timeline) + m_timeline->playerDestroyed(this); +#endif +} + +double AnimationPlayer::sourceEnd() const +{ + return m_content ? m_content->endTimeInternal() : 0; +} + +bool AnimationPlayer::limited(double currentTime) const +{ + return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && currentTime >= sourceEnd()); +} + +double AnimationPlayer::currentTimeWithoutLag() const +{ + if (isNull(m_startTime) || !m_timeline) + return 0; + return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; +} + +double AnimationPlayer::currentTimeWithLag() const +{ + ASSERT(!m_held); + double time = currentTimeWithoutLag(); + return std::isinf(time) ? time : time - m_storedTimeLag; +} + +void AnimationPlayer::updateTimingState(double newCurrentTime) +{ + ASSERT(!isNull(newCurrentTime)); + bool oldHeld = m_held; + m_held = m_paused || !m_playbackRate || limited(newCurrentTime); + if (m_held) { + if (!oldHeld || m_holdTime != newCurrentTime) + setOutdated(); + m_holdTime = newCurrentTime; + m_storedTimeLag = nullValue(); + } else { + m_holdTime = nullValue(); + m_storedTimeLag = currentTimeWithoutLag() - newCurrentTime; + m_finished = false; + setOutdated(); + } +} + +void AnimationPlayer::updateCurrentTimingState() +{ + if (m_held) { + updateTimingState(m_holdTime); + return; + } + if (!limited(currentTimeWithLag())) + return; + m_held = true; + m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); + m_storedTimeLag = nullValue(); +} + +double AnimationPlayer::currentTime() +{ + return currentTimeInternal() * 1000; +} + +double AnimationPlayer::currentTimeInternal() +{ + updateCurrentTimingState(); + if (m_held) + return m_holdTime; + return currentTimeWithLag(); +} + +void AnimationPlayer::setCurrentTime(double newCurrentTime) +{ + setCurrentTimeInternal(newCurrentTime / 1000); +} + +void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime) +{ + if (!std::isfinite(newCurrentTime)) + return; + updateTimingState(newCurrentTime); + cancelAnimationOnCompositor(); + schedulePendingAnimationOnCompositor(); +} + +void AnimationPlayer::setStartTimeInternal(double newStartTime, bool isUpdateFromCompositor) +{ + ASSERT(!isUpdateFromCompositor || !hasStartTime()); + + if (!std::isfinite(newStartTime)) + return; + if (newStartTime == m_startTime) + return; + updateCurrentTimingState(); // Update the value of held + bool hadStartTime = hasStartTime(); + double previousCurrentTime = currentTimeInternal(); + m_startTime = newStartTime; + m_sortInfo.m_startTime = newStartTime; + updateCurrentTimingState(); + if (previousCurrentTime != currentTimeInternal()) { + setOutdated(); + } else if (!hadStartTime && m_timeline) { + // Even though this player is not outdated, time to effect change is + // infinity until start time is set. + m_timeline->wake(); + } + if (!isUpdateFromCompositor) { + cancelAnimationOnCompositor(); + schedulePendingAnimationOnCompositor(); + } +} + +void AnimationPlayer::setSource(AnimationNode* newSource) +{ + if (m_content == newSource) + return; + cancelAnimationOnCompositor(); + double storedCurrentTime = currentTimeInternal(); + if (m_content) + m_content->detach(); + m_content = newSource; + if (newSource) { + // FIXME: This logic needs to be updated once groups are implemented + if (newSource->player()) + newSource->player()->cancel(); + newSource->attach(this); + } + updateTimingState(storedCurrentTime); + schedulePendingAnimationOnCompositor(); +} + +void AnimationPlayer::pause() +{ + if (m_paused) + return; + m_paused = true; + updateTimingState(currentTimeInternal()); + cancelAnimationOnCompositor(); +} + +void AnimationPlayer::unpause() +{ + if (!m_paused) + return; + m_paused = false; + updateTimingState(currentTimeInternal()); + schedulePendingAnimationOnCompositor(); +} + +void AnimationPlayer::play() +{ + cancelAnimationOnCompositor(); + // Note, unpause schedules pending animation on compositor if necessary. + unpause(); + if (!m_content) + return; + double currentTime = this->currentTimeInternal(); + if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) + setCurrentTimeInternal(0); + else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd())) + setCurrentTimeInternal(sourceEnd()); + m_finished = false; +} + +void AnimationPlayer::reverse() +{ + if (!m_playbackRate) + return; + if (m_content) { + if (m_playbackRate > 0 && currentTimeInternal() > sourceEnd()) + setCurrentTimeInternal(sourceEnd()); + else if (m_playbackRate < 0 && currentTimeInternal() < 0) + setCurrentTimeInternal(0); + } + setPlaybackRate(-m_playbackRate); + cancelAnimationOnCompositor(); + // Note, unpause schedules pending animation on compositor if necessary. + unpause(); +} + +void AnimationPlayer::finish(ExceptionState& exceptionState) +{ + if (!m_playbackRate) + return; + if (m_playbackRate < 0) { + setCurrentTimeInternal(0); + } else { + if (sourceEnd() == std::numeric_limits<double>::infinity()) { + exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity."); + return; + } + setCurrentTimeInternal(sourceEnd()); + } + ASSERT(finished()); + cancelAnimationOnCompositor(); +} + +const AtomicString& AnimationPlayer::interfaceName() const +{ + return EventTargetNames::AnimationPlayer; +} + +ExecutionContext* AnimationPlayer::executionContext() const +{ + return ActiveDOMObject::executionContext(); +} + +bool AnimationPlayer::hasPendingActivity() const +{ + return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventTypeNames::finish)); +} + +void AnimationPlayer::stop() +{ + m_finished = true; + m_pendingFinishedEvent = nullptr; +} + +bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) +{ + if (m_pendingFinishedEvent == event) + m_pendingFinishedEvent = nullptr; + return EventTargetWithInlineData::dispatchEvent(event); +} + +void AnimationPlayer::setPlaybackRate(double playbackRate) +{ + if (!std::isfinite(playbackRate)) + return; + double storedCurrentTime = currentTimeInternal(); + if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0)) + m_finished = false; + m_playbackRate = playbackRate; + updateTimingState(storedCurrentTime); + cancelAnimationOnCompositor(); + schedulePendingAnimationOnCompositor(); +} + +void AnimationPlayer::setOutdated() +{ + m_outdated = true; + if (m_timeline) + m_timeline->setOutdatedAnimationPlayer(this); +} + +bool AnimationPlayer::canStartAnimationOnCompositor() +{ + // FIXME: Need compositor support for playback rate != 1. + if (playbackRate() != 1) + return false; + + return m_timeline && m_content && m_content->isAnimation() && !m_held; +} + +bool AnimationPlayer::maybeStartAnimationOnCompositor() +{ + if (!canStartAnimationOnCompositor()) + return false; + + return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(timeline()->zeroTime() + startTimeInternal() + timeLagInternal()); +} + +void AnimationPlayer::schedulePendingAnimationOnCompositor() +{ + ASSERT(!hasActiveAnimationsOnCompositor()); + + if (canStartAnimationOnCompositor()) + timeline()->document()->compositorPendingAnimations().add(this); +} + +bool AnimationPlayer::hasActiveAnimationsOnCompositor() +{ + if (!m_content || !m_content->isAnimation()) + return false; + + return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); +} + +void AnimationPlayer::cancelAnimationOnCompositor() +{ + if (hasActiveAnimationsOnCompositor()) + toAnimation(m_content.get())->cancelAnimationOnCompositor(); +} + +bool AnimationPlayer::update(TimingUpdateReason reason) +{ + m_outdated = false; + + if (!m_timeline) + return false; + + if (m_content) { + double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal(); + m_content->updateInheritedTime(inheritedTime, reason); + } + + if (finished() && !m_finished) { + if (reason == TimingUpdateForAnimationFrame && hasStartTime()) { + const AtomicString& eventType = EventTypeNames::finish; + if (executionContext() && hasEventListeners(eventType)) { + m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, currentTime(), timeline()->currentTime()); + m_pendingFinishedEvent->setTarget(this); + m_pendingFinishedEvent->setCurrentTarget(this); + m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFinishedEvent); + } + m_finished = true; + } + } + ASSERT(!m_outdated); + return !m_finished || !finished(); +} + +double AnimationPlayer::timeToEffectChange() +{ + ASSERT(!m_outdated); + if (m_held || !hasStartTime()) + return std::numeric_limits<double>::infinity(); + if (!m_content) + return -currentTimeInternal() / m_playbackRate; + if (m_playbackRate > 0) + return m_content->timeToForwardsEffectChange() / m_playbackRate; + return m_content->timeToReverseEffectChange() / -m_playbackRate; +} + +void AnimationPlayer::cancel() +{ + setSource(0); +} + +bool AnimationPlayer::SortInfo::operator<(const SortInfo& other) const +{ + ASSERT(!std::isnan(m_startTime) && !std::isnan(other.m_startTime)); + if (m_startTime < other.m_startTime) + return true; + if (m_startTime > other.m_startTime) + return false; + return m_sequenceNumber < other.m_sequenceNumber; +} + +#if !ENABLE(OILPAN) +bool AnimationPlayer::canFree() const +{ + ASSERT(m_content); + return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef(); +} +#endif + +bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ + if (eventType == EventTypeNames::finish) + UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishEvent); + return EventTargetWithInlineData::addEventListener(eventType, listener, useCapture); +} + +void AnimationPlayer::pauseForTesting(double pauseTime) +{ + RELEASE_ASSERT(!paused()); + updateTimingState(pauseTime); + if (!m_isPausedForTesting && hasActiveAnimationsOnCompositor()) + toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal()); + m_isPausedForTesting = true; + pause(); +} + +void AnimationPlayer::trace(Visitor* visitor) +{ + visitor->trace(m_content); + visitor->trace(m_timeline); + visitor->trace(m_pendingFinishedEvent); + EventTargetWithInlineData::trace(visitor); +} + +} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.h b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.h new file mode 100644 index 00000000000..e0c44eb72a9 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AnimationPlayer_h +#define AnimationPlayer_h + +#include "core/animation/AnimationNode.h" +#include "core/dom/ActiveDOMObject.h" +#include "core/events/EventTarget.h" +#include "wtf/RefPtr.h" + +namespace WebCore { + +class AnimationTimeline; +class ExceptionState; + +class AnimationPlayer FINAL : public RefCountedWillBeRefCountedGarbageCollected<AnimationPlayer> + , public ActiveDOMObject + , public EventTargetWithInlineData { + REFCOUNTED_EVENT_TARGET(AnimationPlayer); + WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(AnimationPlayer); +public: + + ~AnimationPlayer(); + static PassRefPtrWillBeRawPtr<AnimationPlayer> create(ExecutionContext*, AnimationTimeline&, AnimationNode*); + + // Returns whether the player is finished. + bool update(TimingUpdateReason); + + // timeToEffectChange returns: + // infinity - if this player is no longer in effect + // 0 - if this player requires an update on the next frame + // n - if this player requires an update after 'n' units of time + double timeToEffectChange(); + + void cancel(); + + double currentTime(); + void setCurrentTime(double newCurrentTime); + + double currentTimeInternal(); + void setCurrentTimeInternal(double newCurrentTime); + + bool paused() const { return m_paused && !m_isPausedForTesting; } + void pause(); + void play(); + void reverse(); + void finish(ExceptionState&); + bool finished() { return limited(currentTimeInternal()); } + // FIXME: Resolve whether finished() should just return the flag, and + // remove this method. + bool finishedInternal() const { return m_finished; } + + DEFINE_ATTRIBUTE_EVENT_LISTENER(finish); + + virtual const AtomicString& interfaceName() const OVERRIDE; + virtual ExecutionContext* executionContext() const OVERRIDE; + virtual bool hasPendingActivity() const OVERRIDE; + virtual void stop() OVERRIDE; + virtual bool dispatchEvent(PassRefPtrWillBeRawPtr<Event>) OVERRIDE; + + double playbackRate() const { return m_playbackRate; } + void setPlaybackRate(double); + const AnimationTimeline* timeline() const { return m_timeline; } + AnimationTimeline* timeline() { return m_timeline; } + +#if !ENABLE(OILPAN) + void timelineDestroyed() { m_timeline = nullptr; } +#endif + + bool hasStartTime() const { return !isNull(m_startTime); } + double startTime() const { return m_startTime * 1000; } + double startTimeInternal() const { return m_startTime; } + void setStartTime(double startTime) { setStartTimeInternal(startTime / 1000); } + void setStartTimeInternal(double, bool isUpdateFromCompositor = false); + + const AnimationNode* source() const { return m_content.get(); } + AnimationNode* source() { return m_content.get(); } + AnimationNode* source(bool& isNull) { isNull = !m_content; return m_content.get(); } + void setSource(AnimationNode*); + + double timeLag() { return timeLagInternal() * 1000; } + double timeLagInternal() { return currentTimeWithoutLag() - currentTimeInternal(); } + + // Pausing via this method is not reflected in the value returned by + // paused() and must never overlap with pausing via pause(). + void pauseForTesting(double pauseTime); + // This should only be used for CSS + void unpause(); + + void setOutdated(); + bool outdated() { return m_outdated; } + + bool canStartAnimationOnCompositor(); + bool maybeStartAnimationOnCompositor(); + void cancelAnimationOnCompositor(); + void schedulePendingAnimationOnCompositor(); + bool hasActiveAnimationsOnCompositor(); + + class SortInfo { + public: + friend class AnimationPlayer; + bool operator<(const SortInfo& other) const; + double startTime() const { return m_startTime; } + private: + SortInfo(unsigned sequenceNumber, double startTime) + : m_sequenceNumber(sequenceNumber) + , m_startTime(startTime) + { + } + unsigned m_sequenceNumber; + double m_startTime; + }; + + const SortInfo& sortInfo() const { return m_sortInfo; } + + static bool hasLowerPriority(AnimationPlayer* player1, AnimationPlayer* player2) + { + return player1->sortInfo() < player2->sortInfo(); + } + +#if !ENABLE(OILPAN) + // Checks if the AnimationStack is the last reference holder to the Player. + // This won't be needed when AnimationPlayer is moved to Oilpan. + bool canFree() const; +#endif + + virtual bool addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture = false) OVERRIDE; + + virtual void trace(Visitor*) OVERRIDE; + +private: + AnimationPlayer(ExecutionContext*, AnimationTimeline&, AnimationNode*); + double sourceEnd() const; + bool limited(double currentTime) const; + double currentTimeWithoutLag() const; + double currentTimeWithLag() const; + void updateTimingState(double newCurrentTime); + void updateCurrentTimingState(); + + double m_playbackRate; + double m_startTime; + double m_holdTime; + double m_storedTimeLag; + + SortInfo m_sortInfo; + + RefPtrWillBeMember<AnimationNode> m_content; + RawPtrWillBeMember<AnimationTimeline> m_timeline; + // Reflects all pausing, including via pauseForTesting(). + bool m_paused; + bool m_held; + bool m_isPausedForTesting; + + // This indicates timing information relevant to the player has changed by + // means other than the ordinary progression of time + bool m_outdated; + + bool m_finished; + // Holds a 'finished' event queued for asynchronous dispatch via the + // ScriptedAnimationController. This object remains active until the + // event is actually dispatched. + RefPtrWillBeMember<Event> m_pendingFinishedEvent; +}; + +} // namespace + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.idl b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.idl new file mode 100644 index 00000000000..8dc0d416d6a --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayer.idl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + RuntimeEnabled=WebAnimationsElementAnimate, + NoInterfaceObject, + WillBeGarbageCollected, + ActiveDOMObject, +] interface AnimationPlayer : EventTarget { + [RuntimeEnabled=WebAnimationsAPI] attribute AnimationNode? source; + [RuntimeEnabled=WebAnimationsAPI] attribute double startTime; + [RuntimeEnabled=WebAnimationsAPI] attribute double currentTime; + [RuntimeEnabled=WebAnimationsAPI] readonly attribute double timeLag; + [RuntimeEnabled=WebAnimationsAPI] attribute double playbackRate; + [RuntimeEnabled=WebAnimationsAPI] readonly attribute boolean paused; + [RuntimeEnabled=WebAnimationsAPI] readonly attribute boolean finished; + [RuntimeEnabled=WebAnimationsAPI, RaisesException] void finish(); + [RuntimeEnabled=WebAnimationsAPI] void play(); + [RuntimeEnabled=WebAnimationsAPI] void pause(); + [RuntimeEnabled=WebAnimationsAPI] void reverse(); + + void cancel(); + [MeasureAs=AnimationPlayerFinishEvent] attribute EventHandler onfinish; +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationPlayerTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayerTest.cpp new file mode 100644 index 00000000000..37660e69b82 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationPlayerTest.cpp @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2013, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/AnimationPlayer.h" + +#include "core/animation/ActiveAnimations.h" +#include "core/animation/Animation.h" +#include "core/animation/AnimationClock.h" +#include "core/animation/AnimationTimeline.h" +#include "core/dom/Document.h" +#include "core/dom/QualifiedName.h" +#include "platform/weborigin/KURL.h" +#include <gtest/gtest.h> + +using namespace WebCore; + +namespace { + +class AnimationAnimationPlayerTest : public ::testing::Test { +protected: + virtual void SetUp() + { + setUpWithoutStartingTimeline(); + startTimeline(); + } + + void setUpWithoutStartingTimeline() + { + document = Document::create(); + document->animationClock().resetTimeForTesting(); + timeline = AnimationTimeline::create(document.get()); + player = timeline->createAnimationPlayer(0); + player->setStartTimeInternal(0); + player->setSource(makeAnimation().get()); + } + + void startTimeline() + { + updateTimeline(0); + } + + PassRefPtrWillBeRawPtr<Animation> makeAnimation(double duration = 30, double playbackRate = 1) + { + Timing timing; + timing.iterationDuration = duration; + timing.playbackRate = playbackRate; + return Animation::create(0, nullptr, timing); + } + + bool updateTimeline(double time) + { + document->animationClock().updateTime(time); + // The timeline does not know about our player, so we have to explicitly call update(). + return player->update(TimingUpdateOnDemand); + } + + RefPtrWillBePersistent<Document> document; + RefPtrWillBePersistent<AnimationTimeline> timeline; + RefPtrWillBePersistent<AnimationPlayer> player; + TrackExceptionState exceptionState; +}; + +TEST_F(AnimationAnimationPlayerTest, InitialState) +{ + setUpWithoutStartingTimeline(); + player = timeline->createAnimationPlayer(0); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_FALSE(player->paused()); + EXPECT_EQ(1, player->playbackRate()); + EXPECT_EQ(0, player->timeLagInternal()); + EXPECT_FALSE(player->hasStartTime()); + EXPECT_TRUE(isNull(player->startTimeInternal())); + + startTimeline(); + player->setStartTimeInternal(0); + EXPECT_EQ(0, timeline->currentTimeInternal()); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_FALSE(player->paused()); + EXPECT_EQ(1, player->playbackRate()); + EXPECT_EQ(0, player->startTimeInternal()); + EXPECT_EQ(0, player->timeLagInternal()); + EXPECT_TRUE(player->hasStartTime()); +} + + +TEST_F(AnimationAnimationPlayerTest, CurrentTimeDoesNotSetOutdated) +{ + EXPECT_FALSE(player->outdated()); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_FALSE(player->outdated()); + // FIXME: We should split updateTimeline into a version that doesn't update + // the player and one that does, as most of the tests don't require update() + // to be called. + document->animationClock().updateTime(10); + EXPECT_EQ(10, player->currentTimeInternal()); + EXPECT_FALSE(player->outdated()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTime) +{ + player->setCurrentTimeInternal(10); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(10); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTimeNegative) +{ + player->setCurrentTimeInternal(-10); + EXPECT_EQ(-10, player->currentTimeInternal()); + updateTimeline(20); + EXPECT_EQ(10, player->currentTimeInternal()); + + player->setPlaybackRate(-2); + player->setCurrentTimeInternal(-10); + EXPECT_EQ(-10, player->currentTimeInternal()); + updateTimeline(40); + EXPECT_EQ(-10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTimePastContentEnd) +{ + player->setCurrentTimeInternal(50); + EXPECT_EQ(50, player->currentTimeInternal()); + updateTimeline(20); + EXPECT_EQ(50, player->currentTimeInternal()); + + player->setPlaybackRate(-2); + player->setCurrentTimeInternal(50); + EXPECT_EQ(50, player->currentTimeInternal()); + updateTimeline(40); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTimeBeforeTimelineStarted) +{ + setUpWithoutStartingTimeline(); + player->setCurrentTimeInternal(5); + EXPECT_EQ(5, player->currentTimeInternal()); + startTimeline(); + updateTimeline(10); + EXPECT_EQ(15, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTimePastContentEndBeforeTimelineStarted) +{ + setUpWithoutStartingTimeline(); + player->setCurrentTimeInternal(250); + EXPECT_EQ(250, player->currentTimeInternal()); + startTimeline(); + updateTimeline(10); + EXPECT_EQ(250, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTimeMax) +{ + player->setCurrentTimeInternal(std::numeric_limits<double>::max()); + EXPECT_EQ(std::numeric_limits<double>::max(), player->currentTimeInternal()); + updateTimeline(100); + EXPECT_EQ(std::numeric_limits<double>::max(), player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetCurrentTimeUnrestrictedDouble) +{ + updateTimeline(10); + player->setCurrentTimeInternal(nullValue()); + EXPECT_EQ(10, player->currentTimeInternal()); + player->setCurrentTimeInternal(std::numeric_limits<double>::infinity()); + EXPECT_EQ(10, player->currentTimeInternal()); + player->setCurrentTimeInternal(-std::numeric_limits<double>::infinity()); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, TimeLag) +{ + player->setCurrentTimeInternal(10); + EXPECT_EQ(-10, player->timeLagInternal()); + updateTimeline(10); + EXPECT_EQ(-10, player->timeLagInternal()); + player->setCurrentTimeInternal(40); + EXPECT_EQ(-30, player->timeLagInternal()); + updateTimeline(20); + EXPECT_EQ(-20, player->timeLagInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, SetStartTime) +{ + updateTimeline(20); + EXPECT_EQ(0, player->startTimeInternal()); + EXPECT_EQ(20, player->currentTimeInternal()); + player->setStartTimeInternal(10); + EXPECT_EQ(10, player->startTimeInternal()); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(30); + EXPECT_EQ(10, player->startTimeInternal()); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetStartTimeLimitsAnimationPlayer) +{ + player->setStartTimeInternal(-50); + EXPECT_EQ(30, player->currentTimeInternal()); + player->setPlaybackRate(-1); + player->setStartTimeInternal(-100); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); +} + +TEST_F(AnimationAnimationPlayerTest, SetStartTimeOnLimitedAnimationPlayer) +{ + updateTimeline(30); + player->setStartTimeInternal(-10); + EXPECT_EQ(30, player->currentTimeInternal()); + player->setCurrentTimeInternal(50); + player->setStartTimeInternal(-40); + EXPECT_EQ(50, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); +} + +TEST_F(AnimationAnimationPlayerTest, SetStartTimeWhilePaused) +{ + updateTimeline(10); + player->pause(); + player->setStartTimeInternal(-40); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(50); + player->setStartTimeInternal(60); + EXPECT_EQ(10, player->currentTimeInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, PausePlay) +{ + updateTimeline(10); + player->pause(); + EXPECT_TRUE(player->paused()); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(20); + player->play(); + EXPECT_FALSE(player->paused()); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(30); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, PauseBeforeTimelineStarted) +{ + setUpWithoutStartingTimeline(); + player->pause(); + EXPECT_TRUE(player->paused()); + player->play(); + EXPECT_FALSE(player->paused()); + + player->pause(); + startTimeline(); + updateTimeline(100); + EXPECT_TRUE(player->paused()); + EXPECT_EQ(0, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, PauseBeforeStartTimeSet) +{ + player = timeline->createAnimationPlayer(makeAnimation().get()); + updateTimeline(100); + player->pause(); + updateTimeline(200); + EXPECT_EQ(0, player->currentTimeInternal()); + + player->setStartTimeInternal(150); + player->play(); + EXPECT_EQ(0, player->currentTimeInternal()); + updateTimeline(220); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, PlayRewindsToStart) +{ + player->setCurrentTimeInternal(30); + player->play(); + EXPECT_EQ(0, player->currentTimeInternal()); + + player->setCurrentTimeInternal(40); + player->play(); + EXPECT_EQ(0, player->currentTimeInternal()); + + player->setCurrentTimeInternal(-10); + player->play(); + EXPECT_EQ(0, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, PlayRewindsToEnd) +{ + player->setPlaybackRate(-1); + player->play(); + EXPECT_EQ(30, player->currentTimeInternal()); + + player->setCurrentTimeInternal(40); + player->play(); + EXPECT_EQ(30, player->currentTimeInternal()); + + player->setCurrentTimeInternal(-10); + player->play(); + EXPECT_EQ(30, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, PlayWithPlaybackRateZeroDoesNotSeek) +{ + player->setPlaybackRate(0); + player->play(); + EXPECT_EQ(0, player->currentTimeInternal()); + + player->setCurrentTimeInternal(40); + player->play(); + EXPECT_EQ(40, player->currentTimeInternal()); + + player->setCurrentTimeInternal(-10); + player->play(); + EXPECT_EQ(-10, player->currentTimeInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, Reverse) +{ + player->setCurrentTimeInternal(10); + player->pause(); + player->reverse(); + EXPECT_FALSE(player->paused()); + EXPECT_EQ(-1, player->playbackRate()); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, ReverseDoesNothingWithPlaybackRateZero) +{ + player->setCurrentTimeInternal(10); + player->setPlaybackRate(0); + player->pause(); + player->reverse(); + EXPECT_TRUE(player->paused()); + EXPECT_EQ(0, player->playbackRate()); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, ReverseDoesNotSeekWithNoSource) +{ + player->setSource(0); + player->setCurrentTimeInternal(10); + player->reverse(); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, ReverseSeeksToStart) +{ + player->setCurrentTimeInternal(-10); + player->setPlaybackRate(-1); + player->reverse(); + EXPECT_EQ(0, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, ReverseSeeksToEnd) +{ + player->setCurrentTimeInternal(40); + player->reverse(); + EXPECT_EQ(30, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, ReverseLimitsAnimationPlayer) +{ + player->setCurrentTimeInternal(40); + player->setPlaybackRate(-1); + player->reverse(); + EXPECT_TRUE(player->finished()); + EXPECT_EQ(40, player->currentTimeInternal()); + + player->setCurrentTimeInternal(-10); + player->reverse(); + EXPECT_TRUE(player->finished()); + EXPECT_EQ(-10, player->currentTimeInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, Finish) +{ + player->finish(exceptionState); + EXPECT_EQ(30, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); + + player->setPlaybackRate(-1); + player->finish(exceptionState); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); + + EXPECT_FALSE(exceptionState.hadException()); +} + +TEST_F(AnimationAnimationPlayerTest, FinishAfterSourceEnd) +{ + player->setCurrentTimeInternal(40); + player->finish(exceptionState); + EXPECT_EQ(30, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, FinishBeforeStart) +{ + player->setCurrentTimeInternal(-10); + player->setPlaybackRate(-1); + player->finish(exceptionState); + EXPECT_EQ(0, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, FinishDoesNothingWithPlaybackRateZero) +{ + player->setCurrentTimeInternal(10); + player->setPlaybackRate(0); + player->finish(exceptionState); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, FinishRaisesException) +{ + Timing timing; + timing.iterationDuration = 1; + timing.iterationCount = std::numeric_limits<double>::infinity(); + player->setSource(Animation::create(0, nullptr, timing).get()); + player->setCurrentTimeInternal(10); + + player->finish(exceptionState); + EXPECT_EQ(10, player->currentTimeInternal()); + EXPECT_TRUE(exceptionState.hadException()); + EXPECT_EQ(InvalidStateError, exceptionState.code()); +} + + +TEST_F(AnimationAnimationPlayerTest, LimitingAtSourceEnd) +{ + updateTimeline(30); + EXPECT_EQ(30, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); + updateTimeline(40); + EXPECT_EQ(30, player->currentTimeInternal()); + EXPECT_FALSE(player->paused()); +} + +TEST_F(AnimationAnimationPlayerTest, LimitingAtStart) +{ + updateTimeline(30); + player->setPlaybackRate(-2); + updateTimeline(45); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); + updateTimeline(60); + EXPECT_EQ(0, player->currentTimeInternal()); + EXPECT_FALSE(player->paused()); +} + +TEST_F(AnimationAnimationPlayerTest, LimitingWithNoSource) +{ + player->setSource(0); + EXPECT_TRUE(player->finished()); + updateTimeline(30); + EXPECT_EQ(0, player->currentTimeInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, SetPlaybackRate) +{ + player->setPlaybackRate(2); + EXPECT_EQ(2, player->playbackRate()); + EXPECT_EQ(0, player->currentTimeInternal()); + updateTimeline(10); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetPlaybackRateBeforeTimelineStarted) +{ + setUpWithoutStartingTimeline(); + player->setPlaybackRate(2); + EXPECT_EQ(2, player->playbackRate()); + EXPECT_EQ(0, player->currentTimeInternal()); + startTimeline(); + updateTimeline(10); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetPlaybackRateWhilePaused) +{ + updateTimeline(10); + player->pause(); + player->setPlaybackRate(2); + updateTimeline(20); + player->play(); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(25); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetPlaybackRateWhileLimited) +{ + updateTimeline(40); + EXPECT_EQ(30, player->currentTimeInternal()); + player->setPlaybackRate(2); + updateTimeline(50); + EXPECT_EQ(30, player->currentTimeInternal()); + player->setPlaybackRate(-2); + EXPECT_FALSE(player->finished()); + updateTimeline(60); + EXPECT_EQ(10, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetPlaybackRateZero) +{ + updateTimeline(10); + player->setPlaybackRate(0); + EXPECT_EQ(10, player->currentTimeInternal()); + updateTimeline(20); + EXPECT_EQ(10, player->currentTimeInternal()); + player->setCurrentTimeInternal(20); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetPlaybackRateMax) +{ + player->setPlaybackRate(std::numeric_limits<double>::max()); + EXPECT_EQ(std::numeric_limits<double>::max(), player->playbackRate()); + EXPECT_EQ(0, player->currentTimeInternal()); + updateTimeline(1); + EXPECT_EQ(30, player->currentTimeInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, SetSource) +{ + player = timeline->createAnimationPlayer(0); + player->setStartTimeInternal(0); + RefPtrWillBeRawPtr<AnimationNode> source1 = makeAnimation(); + RefPtrWillBeRawPtr<AnimationNode> source2 = makeAnimation(); + player->setSource(source1.get()); + EXPECT_EQ(source1, player->source()); + EXPECT_EQ(0, player->currentTimeInternal()); + player->setCurrentTimeInternal(15); + player->setSource(source2.get()); + EXPECT_EQ(15, player->currentTimeInternal()); + EXPECT_EQ(0, source1->player()); + EXPECT_EQ(player.get(), source2->player()); + EXPECT_EQ(source2, player->source()); +} + +TEST_F(AnimationAnimationPlayerTest, SetSourceLimitsAnimationPlayer) +{ + player->setCurrentTimeInternal(20); + player->setSource(makeAnimation(10).get()); + EXPECT_EQ(20, player->currentTimeInternal()); + EXPECT_TRUE(player->finished()); + updateTimeline(10); + EXPECT_EQ(20, player->currentTimeInternal()); +} + +TEST_F(AnimationAnimationPlayerTest, SetSourceUnlimitsAnimationPlayer) +{ + player->setCurrentTimeInternal(40); + player->setSource(makeAnimation(60).get()); + EXPECT_FALSE(player->finished()); + EXPECT_EQ(40, player->currentTimeInternal()); + updateTimeline(10); + EXPECT_EQ(50, player->currentTimeInternal()); +} + + +TEST_F(AnimationAnimationPlayerTest, EmptyAnimationPlayersDontUpdateEffects) +{ + player = timeline->createAnimationPlayer(0); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(std::numeric_limits<double>::infinity(), player->timeToEffectChange()); + + updateTimeline(1234); + EXPECT_EQ(std::numeric_limits<double>::infinity(), player->timeToEffectChange()); +} + +TEST_F(AnimationAnimationPlayerTest, AnimationPlayersDisassociateFromSource) +{ + AnimationNode* animationNode = player->source(); + AnimationPlayer* player2 = timeline->createAnimationPlayer(animationNode); + EXPECT_EQ(0, player->source()); + player->setSource(animationNode); + EXPECT_EQ(0, player2->source()); +} + +TEST_F(AnimationAnimationPlayerTest, AnimationPlayersReturnTimeToNextEffect) +{ + Timing timing; + timing.startDelay = 1; + timing.iterationDuration = 1; + timing.endDelay = 1; + RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); + player = timeline->createAnimationPlayer(animation.get()); + player->setStartTimeInternal(0); + + updateTimeline(0); + EXPECT_EQ(1, player->timeToEffectChange()); + + updateTimeline(0.5); + EXPECT_EQ(0.5, player->timeToEffectChange()); + + updateTimeline(1); + EXPECT_EQ(0, player->timeToEffectChange()); + + updateTimeline(1.5); + EXPECT_EQ(0, player->timeToEffectChange()); + + updateTimeline(2); + EXPECT_EQ(std::numeric_limits<double>::infinity(), player->timeToEffectChange()); + + updateTimeline(3); + EXPECT_EQ(std::numeric_limits<double>::infinity(), player->timeToEffectChange()); + + player->setCurrentTimeInternal(0); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(1, player->timeToEffectChange()); + + player->setPlaybackRate(2); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(0.5, player->timeToEffectChange()); + + player->setPlaybackRate(0); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(std::numeric_limits<double>::infinity(), player->timeToEffectChange()); + + player->setCurrentTimeInternal(3); + player->setPlaybackRate(-1); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(1, player->timeToEffectChange()); + + player->setPlaybackRate(-2); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(0.5, player->timeToEffectChange()); +} + +TEST_F(AnimationAnimationPlayerTest, TimeToNextEffectWhenPaused) +{ + EXPECT_EQ(0, player->timeToEffectChange()); + player->pause(); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(std::numeric_limits<double>::infinity(), player->timeToEffectChange()); +} + +TEST_F(AnimationAnimationPlayerTest, TimeToNextEffectWhenCancelledBeforeStart) +{ + EXPECT_EQ(0, player->timeToEffectChange()); + player->setCurrentTimeInternal(-8); + player->setPlaybackRate(2); + player->cancel(); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(4, player->timeToEffectChange()); +} + +TEST_F(AnimationAnimationPlayerTest, TimeToNextEffectWhenCancelledBeforeStartReverse) +{ + EXPECT_EQ(0, player->timeToEffectChange()); + player->setCurrentTimeInternal(9); + player->setPlaybackRate(-3); + player->cancel(); + player->update(TimingUpdateOnDemand); + EXPECT_EQ(3, player->timeToEffectChange()); +} + +TEST_F(AnimationAnimationPlayerTest, AttachedAnimationPlayers) +{ + RefPtrWillBePersistent<Element> element = document->createElement("foo", ASSERT_NO_EXCEPTION); + + Timing timing; + RefPtrWillBeRawPtr<Animation> animation = Animation::create(element.get(), nullptr, timing); + RefPtrWillBeRawPtr<AnimationPlayer> player = timeline->createAnimationPlayer(animation.get()); + player->setStartTime(0); + timeline->serviceAnimations(TimingUpdateForAnimationFrame); + EXPECT_EQ(1, element->activeAnimations()->players().find(player.get())->value); + + player.release(); + Heap::collectAllGarbage(); + EXPECT_TRUE(element->activeAnimations()->players().isEmpty()); +} + +TEST_F(AnimationAnimationPlayerTest, HasLowerPriority) +{ + // Sort time defaults to timeline current time + updateTimeline(15); + RefPtrWillBeRawPtr<AnimationPlayer> player1 = timeline->createAnimationPlayer(0); + RefPtrWillBeRawPtr<AnimationPlayer> player2 = timeline->createAnimationPlayer(0); + player2->setStartTimeInternal(10); + RefPtrWillBeRawPtr<AnimationPlayer> player3 = timeline->createAnimationPlayer(0); + RefPtrWillBeRawPtr<AnimationPlayer> player4 = timeline->createAnimationPlayer(0); + player4->setStartTimeInternal(20); + RefPtrWillBeRawPtr<AnimationPlayer> player5 = timeline->createAnimationPlayer(0); + player5->setStartTimeInternal(10); + RefPtrWillBeRawPtr<AnimationPlayer> player6 = timeline->createAnimationPlayer(0); + player6->setStartTimeInternal(-10); + Vector<RefPtrWillBeMember<AnimationPlayer> > players; + players.append(player6); + players.append(player2); + players.append(player5); + players.append(player1); + players.append(player3); + players.append(player4); + for (size_t i = 0; i < players.size(); i++) { + for (size_t j = 0; j < players.size(); j++) + EXPECT_EQ(i < j, AnimationPlayer::hasLowerPriority(players[i].get(), players[j].get())); + } +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationStack.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationStack.cpp index 601e7e598e4..7b840a16e8f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimationStack.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationStack.cpp @@ -32,25 +32,49 @@ #include "core/animation/AnimationStack.h" #include "core/animation/css/CSSAnimations.h" +#include "core/animation/interpolation/StyleInterpolation.h" +#include "wtf/BitArray.h" +#include "wtf/NonCopyingSort.h" +#include <algorithm> namespace WebCore { namespace { -void copyToCompositableValueMap(const AnimationEffect::CompositableValueList* source, AnimationEffect::CompositableValueMap& target) +void copyToActiveInterpolationMap(const WillBeHeapVector<RefPtrWillBeMember<WebCore::Interpolation> >& source, WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<WebCore::Interpolation> >& target) { - if (!source) - return; - for (AnimationEffect::CompositableValueList::const_iterator iter = source->begin(); iter != source->end(); ++iter) - target.set(iter->first, iter->second); + for (size_t i = 0; i < source.size(); ++i) { + Interpolation* interpolation = source[i].get(); + target.set(toStyleInterpolation(interpolation)->id(), interpolation); + } +} + +bool compareEffects(const OwnPtrWillBeMember<SampledEffect>& effect1, const OwnPtrWillBeMember<SampledEffect>& effect2) +{ + ASSERT(effect1 && effect2); + return effect1->sortInfo() < effect2->sortInfo(); +} + +void copyNewAnimationsToActiveInterpolationMap(const WillBeHeapVector<RawPtrWillBeMember<InertAnimation> >& newAnimations, WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >& result) +{ + for (size_t i = 0; i < newAnimations.size(); ++i) { + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample = newAnimations[i]->sample(0); + if (sample) { + copyToActiveInterpolationMap(*sample, result); + } + } } } // namespace +AnimationStack::AnimationStack() +{ +} + bool AnimationStack::affects(CSSPropertyID property) const { - for (size_t i = 0; i < m_activeAnimations.size(); ++i) { - if (m_activeAnimations[i]->affects(property)) + for (size_t i = 0; i < m_effects.size(); ++i) { + if (m_effects[i]->animation() && m_effects[i]->animation()->affects(property)) return true; } return false; @@ -58,35 +82,71 @@ bool AnimationStack::affects(CSSPropertyID property) const bool AnimationStack::hasActiveAnimationsOnCompositor(CSSPropertyID property) const { - for (size_t i = 0; i < m_activeAnimations.size(); ++i) { - if (m_activeAnimations[i]->hasActiveAnimationsOnCompositor(property)) + for (size_t i = 0; i < m_effects.size(); ++i) { + if (m_effects[i]->animation() && m_effects[i]->animation()->hasActiveAnimationsOnCompositor(property)) return true; } return false; } -AnimationEffect::CompositableValueMap AnimationStack::compositableValues(const AnimationStack* animationStack, const Vector<InertAnimation*>* newAnimations, const HashSet<const Player*>* cancelledPlayers, Animation::Priority priority) +WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > AnimationStack::activeInterpolations(AnimationStack* animationStack, const WillBeHeapVector<RawPtrWillBeMember<InertAnimation> >* newAnimations, const WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> >* cancelledAnimationPlayers, Animation::Priority priority, double timelineCurrentTime) { - AnimationEffect::CompositableValueMap result; + // We don't exactly know when new animations will start, but timelineCurrentTime is a good estimate. + + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > result; if (animationStack) { - const Vector<Animation*>& animations = animationStack->m_activeAnimations; - for (size_t i = 0; i < animations.size(); ++i) { - Animation* animation = animations[i]; - if (animation->priority() != priority) - continue; - if (cancelledPlayers && cancelledPlayers->contains(animation->player())) + WillBeHeapVector<OwnPtrWillBeMember<SampledEffect> >& effects = animationStack->m_effects; + // std::sort doesn't work with OwnPtrs + nonCopyingSort(effects.begin(), effects.end(), compareEffects); + animationStack->simplifyEffects(); + for (size_t i = 0; i < effects.size(); ++i) { + const SampledEffect& effect = *effects[i]; + if (effect.priority() != priority || (cancelledAnimationPlayers && effect.animation() && cancelledAnimationPlayers->contains(effect.animation()->player()))) continue; - copyToCompositableValueMap(animation->compositableValues(), result); + if (newAnimations && effect.sortInfo().startTime() > timelineCurrentTime) { + copyNewAnimationsToActiveInterpolationMap(*newAnimations, result); + newAnimations = 0; + } + copyToActiveInterpolationMap(effect.interpolations(), result); } } - if (newAnimations) { - for (size_t i = 0; i < newAnimations->size(); ++i) - copyToCompositableValueMap(newAnimations->at(i)->sample().get(), result); - } + if (newAnimations) + copyNewAnimationsToActiveInterpolationMap(*newAnimations, result); return result; } +void AnimationStack::simplifyEffects() +{ + // FIXME: This will need to be updated when we have 'add' keyframes. + + BitArray<numCSSProperties> replacedProperties; + for (size_t i = m_effects.size(); i--; ) { + SampledEffect& effect = *m_effects[i]; + effect.removeReplacedInterpolationsIfNeeded(replacedProperties); + if (!effect.canChange()) { + for (size_t i = 0; i < effect.interpolations().size(); ++i) + replacedProperties.set(toStyleInterpolation(effect.interpolations()[i].get())->id()); + } + } + + size_t dest = 0; + for (size_t i = 0; i < m_effects.size(); ++i) { + if (!m_effects[i]->interpolations().isEmpty()) { + m_effects[dest++].swap(m_effects[i]); + continue; + } + if (m_effects[i]->animation()) + m_effects[i]->animation()->notifySampledEffectRemovedFromAnimationStack(); + } + m_effects.shrink(dest); +} + +void AnimationStack::trace(Visitor* visitor) +{ + visitor->trace(m_effects); +} + } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationStack.h b/chromium/third_party/WebKit/Source/core/animation/AnimationStack.h index e7ad32f10b5..83371e8554f 100644 --- a/chromium/third_party/WebKit/Source/core/animation/AnimationStack.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationStack.h @@ -33,6 +33,8 @@ #include "core/animation/Animation.h" #include "core/animation/AnimationEffect.h" +#include "core/animation/AnimationPlayer.h" +#include "core/animation/SampledEffect.h" #include "wtf/HashSet.h" #include "wtf/Vector.h" @@ -41,22 +43,25 @@ namespace WebCore { class InertAnimation; class AnimationStack { - + DISALLOW_ALLOCATION(); + WTF_MAKE_NONCOPYABLE(AnimationStack); public: - void add(Animation* animation) { m_activeAnimations.append(animation); } - void remove(Animation* animation) - { - size_t position = m_activeAnimations.find(animation); - ASSERT(position != kNotFound); - m_activeAnimations.remove(position); - } - bool isEmpty() const { return m_activeAnimations.isEmpty(); } + AnimationStack(); + + void add(PassOwnPtrWillBeRawPtr<SampledEffect> effect) { m_effects.append(effect); } + bool isEmpty() const { return m_effects.isEmpty(); } bool affects(CSSPropertyID) const; bool hasActiveAnimationsOnCompositor(CSSPropertyID) const; - static AnimationEffect::CompositableValueMap compositableValues(const AnimationStack*, const Vector<InertAnimation*>* newAnimations, const HashSet<const Player*>* cancelledPlayers, Animation::Priority); + static WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolations(AnimationStack*, const WillBeHeapVector<RawPtrWillBeMember<InertAnimation> >* newAnimations, const WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> >* cancelledAnimationPlayers, Animation::Priority, double timelineCurrentTime); + + void trace(Visitor*); private: - Vector<Animation*> m_activeAnimations; + void simplifyEffects(); + // Effects sorted by priority. Lower priority at the start of the list. + WillBeHeapVector<OwnPtrWillBeMember<SampledEffect> > m_effects; + + friend class AnimationAnimationStackTest; }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationStackTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationStackTest.cpp new file mode 100644 index 00000000000..5ebdf835d6f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationStackTest.cpp @@ -0,0 +1,163 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/AnimationStack.h" + +#include "core/animation/ActiveAnimations.h" +#include "core/animation/AnimatableDouble.h" +#include "core/animation/AnimationClock.h" +#include "core/animation/AnimationTimeline.h" +#include "core/animation/KeyframeEffectModel.h" +#include "core/animation/interpolation/LegacyStyleInterpolation.h" +#include <gtest/gtest.h> + +namespace WebCore { + +class AnimationAnimationStackTest : public ::testing::Test { +protected: + virtual void SetUp() + { + document = Document::create(); + document->animationClock().resetTimeForTesting(); + timeline = AnimationTimeline::create(document.get()); + element = document->createElement("foo", ASSERT_NO_EXCEPTION); + } + + AnimationPlayer* play(Animation* animation, double startTime) + { + AnimationPlayer* player = timeline->createAnimationPlayer(animation); + player->setStartTimeInternal(startTime); + player->update(TimingUpdateOnDemand); + return player; + } + + void updateTimeline(double time) + { + document->animationClock().updateTime(time); + timeline->serviceAnimations(TimingUpdateForAnimationFrame); + } + + const WillBeHeapVector<OwnPtrWillBeMember<SampledEffect> >& effects() + { + return element->ensureActiveAnimations().defaultStack().m_effects; + } + + PassRefPtrWillBeRawPtr<AnimationEffect> makeAnimationEffect(CSSPropertyID id, PassRefPtrWillBeRawPtr<AnimatableValue> value) + { + AnimatableValueKeyframeVector keyframes(2); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(id, value.get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(1.0); + keyframes[1]->setPropertyValue(id, value.get()); + return AnimatableValueKeyframeEffectModel::create(keyframes); + } + + PassRefPtrWillBeRawPtr<InertAnimation> makeInertAnimation(PassRefPtrWillBeRawPtr<AnimationEffect> effect) + { + Timing timing; + timing.fillMode = Timing::FillModeBoth; + return InertAnimation::create(effect, timing, false); + } + + PassRefPtrWillBeRawPtr<Animation> makeAnimation(PassRefPtrWillBeRawPtr<AnimationEffect> effect, double duration = 10) + { + Timing timing; + timing.fillMode = Timing::FillModeBoth; + timing.iterationDuration = duration; + return Animation::create(element.get(), effect, timing); + } + + AnimatableValue* interpolationValue(Interpolation* interpolation) + { + return toLegacyStyleInterpolation(interpolation)->currentValue().get(); + } + + RefPtrWillBePersistent<Document> document; + RefPtrWillBePersistent<AnimationTimeline> timeline; + RefPtrWillBePersistent<Element> element; +}; + +TEST_F(AnimationAnimationStackTest, ActiveAnimationsSorted) +{ + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 10); + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(2))).get(), 15); + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(3))).get(), 5); + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > result = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), 0, 0, Animation::DefaultPriority, 0); + EXPECT_EQ(1u, result.size()); + EXPECT_TRUE(interpolationValue(result.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(2).get())); +} + +TEST_F(AnimationAnimationStackTest, NewAnimations) +{ + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 15); + play(makeAnimation(makeAnimationEffect(CSSPropertyZIndex, AnimatableDouble::create(2))).get(), 10); + WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newAnimations; + RefPtrWillBeRawPtr<InertAnimation> inert1 = makeInertAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(3))); + RefPtrWillBeRawPtr<InertAnimation> inert2 = makeInertAnimation(makeAnimationEffect(CSSPropertyZIndex, AnimatableDouble::create(4))); + newAnimations.append(inert1.get()); + newAnimations.append(inert2.get()); + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > result = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), &newAnimations, 0, Animation::DefaultPriority, 10); + EXPECT_EQ(2u, result.size()); + EXPECT_TRUE(interpolationValue(result.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(1).get())); + EXPECT_TRUE(interpolationValue(result.get(CSSPropertyZIndex))->equals(AnimatableDouble::create(4).get())); +} + +TEST_F(AnimationAnimationStackTest, CancelledAnimationPlayers) +{ + WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> > cancelledAnimationPlayers; + RefPtrWillBeRawPtr<AnimationPlayer> player = play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 0); + cancelledAnimationPlayers.add(player.get()); + play(makeAnimation(makeAnimationEffect(CSSPropertyZIndex, AnimatableDouble::create(2))).get(), 0); + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > result = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), 0, &cancelledAnimationPlayers, Animation::DefaultPriority, 0); + EXPECT_EQ(1u, result.size()); + EXPECT_TRUE(interpolationValue(result.get(CSSPropertyZIndex))->equals(AnimatableDouble::create(2).get())); +} + +TEST_F(AnimationAnimationStackTest, ForwardsFillDiscarding) +{ + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 2); + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(2))).get(), 6); + play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(3))).get(), 4); + document->compositorPendingAnimations().startPendingAnimations(); + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > interpolations; + + updateTimeline(11); + Heap::collectAllGarbage(); + interpolations = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), 0, 0, Animation::DefaultPriority, 0); + EXPECT_TRUE(interpolationValue(interpolations.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(2).get())); + EXPECT_EQ(3u, effects().size()); + EXPECT_EQ(1u, interpolations.size()); + EXPECT_EQ(2, effects()[0]->sortInfo().startTime()); + EXPECT_EQ(4, effects()[1]->sortInfo().startTime()); + EXPECT_EQ(6, effects()[2]->sortInfo().startTime()); + + updateTimeline(13); + Heap::collectAllGarbage(); + interpolations = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), 0, 0, Animation::DefaultPriority, 0); + EXPECT_TRUE(interpolationValue(interpolations.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(2).get())); + EXPECT_EQ(3u, effects().size()); + EXPECT_EQ(2, effects()[0]->sortInfo().startTime()); + EXPECT_EQ(4, effects()[1]->sortInfo().startTime()); + EXPECT_EQ(6, effects()[2]->sortInfo().startTime()); + + updateTimeline(15); + Heap::collectAllGarbage(); + interpolations = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), 0, 0, Animation::DefaultPriority, 0); + EXPECT_TRUE(interpolationValue(interpolations.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(2).get())); + EXPECT_EQ(2u, effects().size()); + EXPECT_EQ(4, effects()[0]->sortInfo().startTime()); + EXPECT_EQ(6, effects()[1]->sortInfo().startTime()); + + updateTimeline(17); + Heap::collectAllGarbage(); + interpolations = AnimationStack::activeInterpolations(&element->activeAnimations()->defaultStack(), 0, 0, Animation::DefaultPriority, 0); + EXPECT_TRUE(interpolationValue(interpolations.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(2).get())); + EXPECT_EQ(1u, effects().size()); + EXPECT_EQ(6, effects()[0]->sortInfo().startTime()); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationTest.cpp new file mode 100644 index 00000000000..2d6ea65a4fd --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTest.cpp @@ -0,0 +1,467 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/Animation.h" + +#include "bindings/v8/Dictionary.h" +#include "core/animation/AnimationClock.h" +#include "core/animation/AnimationHelpers.h" +#include "core/animation/AnimationNodeTiming.h" +#include "core/animation/AnimationTestHelper.h" +#include "core/animation/AnimationTimeline.h" +#include "core/animation/KeyframeEffectModel.h" +#include "core/animation/Timing.h" +#include "core/dom/Document.h" +#include <gtest/gtest.h> +#include <v8.h> + +namespace WebCore { + +class AnimationAnimationTest : public ::testing::Test { +protected: + AnimationAnimationTest() + : document(Document::create()) + , element(document->createElement("foo", ASSERT_NO_EXCEPTION)) + { + document->animationClock().resetTimeForTesting(); + EXPECT_EQ(0, document->timeline().currentTime()); + } + + RefPtrWillBePersistent<Document> document; + RefPtrWillBePersistent<Element> element; + TrackExceptionState exceptionState; +}; + +class AnimationAnimationV8Test : public AnimationAnimationTest { +protected: + AnimationAnimationV8Test() + : m_isolate(v8::Isolate::GetCurrent()) + , m_scope(m_isolate) + { + } + + template<typename T> + static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, T timingInput, ExceptionState& exceptionState) + { + return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), timingInput); + } + static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, ExceptionState& exceptionState) + { + return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState)); + } + + v8::Isolate* m_isolate; + +private: + V8TestingScope m_scope; +}; + +TEST_F(AnimationAnimationV8Test, CanCreateAnAnimation) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0"); + setV8ObjectPropertyAsString(keyframe1, "easing", "ease-in-out"); + setV8ObjectPropertyAsString(keyframe2, "width", "0px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "1"); + setV8ObjectPropertyAsString(keyframe2, "easing", "cubic-bezier(1, 1, 0.3, 0.3)"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + String value1; + ASSERT_TRUE(jsKeyframes[0].get("width", value1)); + ASSERT_EQ("100px", value1); + + String value2; + ASSERT_TRUE(jsKeyframes[1].get("width", value2)); + ASSERT_EQ("0px", value2); + + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, 0, exceptionState); + + Element* target = animation->target(); + EXPECT_EQ(*element.get(), *target); + + const KeyframeVector keyframes = toKeyframeEffectModelBase(animation->effect())->getFrames(); + + EXPECT_EQ(0, keyframes[0]->offset()); + EXPECT_EQ(1, keyframes[1]->offset()); + + const CSSValue* keyframe1Width = toStringKeyframe(keyframes[0].get())->propertyValue(CSSPropertyWidth); + const CSSValue* keyframe2Width = toStringKeyframe(keyframes[1].get())->propertyValue(CSSPropertyWidth); + ASSERT(keyframe1Width); + ASSERT(keyframe2Width); + + EXPECT_EQ("100px", keyframe1Width->cssText()); + EXPECT_EQ("0px", keyframe2Width->cssText()); + + EXPECT_EQ(*(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut)), *keyframes[0]->easing()); + EXPECT_EQ(*(CubicBezierTimingFunction::create(1, 1, 0.3, 0.3).get()), *keyframes[1]->easing()); +} + +TEST_F(AnimationAnimationV8Test, CanSetDuration) +{ + Vector<Dictionary, 0> jsKeyframes; + double duration = 2000; + + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, duration, exceptionState); + + EXPECT_EQ(duration / 1000, animation->specifiedTiming().iterationDuration); +} + +TEST_F(AnimationAnimationV8Test, CanOmitSpecifiedDuration) +{ + Vector<Dictionary, 0> jsKeyframes; + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, exceptionState); + EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration)); +} + +TEST_F(AnimationAnimationV8Test, NegativeDurationIsAuto) +{ + Vector<Dictionary, 0> jsKeyframes; + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, -2, exceptionState); + EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration)); +} + +TEST_F(AnimationAnimationV8Test, MismatchedKeyframePropertyRaisesException) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0"); + + // Height property appears only in keyframe2 + setV8ObjectPropertyAsString(keyframe2, "height", "100px"); + setV8ObjectPropertyAsString(keyframe2, "width", "0px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "1"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + createAnimation(element.get(), jsKeyframes, 0, exceptionState); + + EXPECT_TRUE(exceptionState.hadException()); + EXPECT_EQ(NotSupportedError, exceptionState.code()); +} + +TEST_F(AnimationAnimationV8Test, MissingOffsetZeroRaisesException) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0.1"); + setV8ObjectPropertyAsString(keyframe2, "width", "0px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "1"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + createAnimation(element.get(), jsKeyframes, 0, exceptionState); + + EXPECT_TRUE(exceptionState.hadException()); + EXPECT_EQ(NotSupportedError, exceptionState.code()); +} + +TEST_F(AnimationAnimationV8Test, MissingOffsetOneRaisesException) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0"); + setV8ObjectPropertyAsString(keyframe2, "width", "0px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "0.1"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + createAnimation(element.get(), jsKeyframes, 0, exceptionState); + + EXPECT_TRUE(exceptionState.hadException()); + EXPECT_EQ(NotSupportedError, exceptionState.code()); +} + +TEST_F(AnimationAnimationV8Test, MissingOffsetZeroAndOneRaisesException) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0.1"); + setV8ObjectPropertyAsString(keyframe2, "width", "0px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "0.2"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + createAnimation(element.get(), jsKeyframes, 0, exceptionState); + + EXPECT_TRUE(exceptionState.hadException()); + EXPECT_EQ(NotSupportedError, exceptionState.code()); +} + +TEST_F(AnimationAnimationV8Test, SpecifiedGetters) +{ + Vector<Dictionary, 0> jsKeyframes; + + v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); + setV8ObjectPropertyAsNumber(timingInput, "delay", 2); + setV8ObjectPropertyAsNumber(timingInput, "endDelay", 0.5); + setV8ObjectPropertyAsString(timingInput, "fill", "backwards"); + setV8ObjectPropertyAsNumber(timingInput, "iterationStart", 2); + setV8ObjectPropertyAsNumber(timingInput, "iterations", 10); + setV8ObjectPropertyAsNumber(timingInput, "playbackRate", 2); + setV8ObjectPropertyAsString(timingInput, "direction", "reverse"); + setV8ObjectPropertyAsString(timingInput, "easing", "step-start"); + Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); + + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState); + + RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing(); + EXPECT_EQ(2, specified->delay()); + EXPECT_EQ(0.5, specified->endDelay()); + EXPECT_EQ("backwards", specified->fill()); + EXPECT_EQ(2, specified->iterationStart()); + EXPECT_EQ(10, specified->iterations()); + EXPECT_EQ(2, specified->playbackRate()); + EXPECT_EQ("reverse", specified->direction()); + EXPECT_EQ("step-start", specified->easing()); +} + +TEST_F(AnimationAnimationV8Test, SpecifiedDurationGetter) +{ + Vector<Dictionary, 0> jsKeyframes; + + v8::Handle<v8::Object> timingInputWithDuration = v8::Object::New(m_isolate); + setV8ObjectPropertyAsNumber(timingInputWithDuration, "duration", 2.5); + Dictionary timingInputDictionaryWithDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputWithDuration), m_isolate); + + RefPtrWillBeRawPtr<Animation> animationWithDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryWithDuration, exceptionState); + + RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedWithDuration = animationWithDuration->timing(); + bool isNumber = false; + double numberDuration = std::numeric_limits<double>::quiet_NaN(); + bool isString = false; + String stringDuration = ""; + specifiedWithDuration->getDuration("duration", isNumber, numberDuration, isString, stringDuration); + EXPECT_TRUE(isNumber); + EXPECT_EQ(2.5, numberDuration); + EXPECT_FALSE(isString); + EXPECT_EQ("", stringDuration); + + + v8::Handle<v8::Object> timingInputNoDuration = v8::Object::New(m_isolate); + Dictionary timingInputDictionaryNoDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputNoDuration), m_isolate); + + RefPtrWillBeRawPtr<Animation> animationNoDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryNoDuration, exceptionState); + + RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedNoDuration = animationNoDuration->timing(); + isNumber = false; + numberDuration = std::numeric_limits<double>::quiet_NaN(); + isString = false; + stringDuration = ""; + specifiedNoDuration->getDuration("duration", isNumber, numberDuration, isString, stringDuration); + EXPECT_FALSE(isNumber); + EXPECT_TRUE(std::isnan(numberDuration)); + EXPECT_TRUE(isString); + EXPECT_EQ("auto", stringDuration); +} + +TEST_F(AnimationAnimationV8Test, SpecifiedSetters) +{ + Vector<Dictionary, 0> jsKeyframes; + v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); + Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState); + + RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing(); + + EXPECT_EQ(0, specified->delay()); + specified->setDelay(2); + EXPECT_EQ(2, specified->delay()); + + EXPECT_EQ(0, specified->endDelay()); + specified->setEndDelay(0.5); + EXPECT_EQ(0.5, specified->endDelay()); + + EXPECT_EQ("auto", specified->fill()); + specified->setFill("backwards"); + EXPECT_EQ("backwards", specified->fill()); + + EXPECT_EQ(0, specified->iterationStart()); + specified->setIterationStart(2); + EXPECT_EQ(2, specified->iterationStart()); + + EXPECT_EQ(1, specified->iterations()); + specified->setIterations(10); + EXPECT_EQ(10, specified->iterations()); + + EXPECT_EQ(1, specified->playbackRate()); + specified->setPlaybackRate(2); + EXPECT_EQ(2, specified->playbackRate()); + + EXPECT_EQ("normal", specified->direction()); + specified->setDirection("reverse"); + EXPECT_EQ("reverse", specified->direction()); + + EXPECT_EQ("linear", specified->easing()); + specified->setEasing("step-start"); + EXPECT_EQ("step-start", specified->easing()); +} + +TEST_F(AnimationAnimationV8Test, SetSpecifiedDuration) +{ + Vector<Dictionary, 0> jsKeyframes; + v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); + Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); + RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState); + + RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing(); + + bool isNumber = false; + double numberDuration = std::numeric_limits<double>::quiet_NaN(); + bool isString = false; + String stringDuration = ""; + specified->getDuration("duration", isNumber, numberDuration, isString, stringDuration); + EXPECT_FALSE(isNumber); + EXPECT_TRUE(std::isnan(numberDuration)); + EXPECT_TRUE(isString); + EXPECT_EQ("auto", stringDuration); + + specified->setDuration("duration", 2.5); + isNumber = false; + numberDuration = std::numeric_limits<double>::quiet_NaN(); + isString = false; + stringDuration = ""; + specified->getDuration("duration", isNumber, numberDuration, isString, stringDuration); + EXPECT_TRUE(isNumber); + EXPECT_EQ(2.5, numberDuration); + EXPECT_FALSE(isString); + EXPECT_EQ("", stringDuration); +} + +TEST_F(AnimationAnimationTest, TimeToEffectChange) +{ + Timing timing; + timing.iterationDuration = 100; + timing.startDelay = 100; + timing.endDelay = 100; + timing.fillMode = Timing::FillModeNone; + RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); + RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get()); + double inf = std::numeric_limits<double>::infinity(); + + EXPECT_EQ(100, animation->timeToForwardsEffectChange()); + EXPECT_EQ(inf, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(100); + EXPECT_EQ(0, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(199); + EXPECT_EQ(0, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(200); + // End-exclusive. + EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(300); + EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); + EXPECT_EQ(100, animation->timeToReverseEffectChange()); +} + +TEST_F(AnimationAnimationTest, TimeToEffectChangeWithPlaybackRate) +{ + Timing timing; + timing.iterationDuration = 100; + timing.startDelay = 100; + timing.endDelay = 100; + timing.playbackRate = 2; + timing.fillMode = Timing::FillModeNone; + RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); + RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get()); + double inf = std::numeric_limits<double>::infinity(); + + EXPECT_EQ(100, animation->timeToForwardsEffectChange()); + EXPECT_EQ(inf, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(100); + EXPECT_EQ(0, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(149); + EXPECT_EQ(0, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(150); + // End-exclusive. + EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(200); + EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); + EXPECT_EQ(50, animation->timeToReverseEffectChange()); +} + +TEST_F(AnimationAnimationTest, TimeToEffectChangeWithNegativePlaybackRate) +{ + Timing timing; + timing.iterationDuration = 100; + timing.startDelay = 100; + timing.endDelay = 100; + timing.playbackRate = -2; + timing.fillMode = Timing::FillModeNone; + RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); + RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get()); + double inf = std::numeric_limits<double>::infinity(); + + EXPECT_EQ(100, animation->timeToForwardsEffectChange()); + EXPECT_EQ(inf, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(100); + EXPECT_EQ(0, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(149); + EXPECT_EQ(0, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(150); + EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); + EXPECT_EQ(0, animation->timeToReverseEffectChange()); + + player->setCurrentTimeInternal(200); + EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); + EXPECT_EQ(50, animation->timeToReverseEffectChange()); +} + +TEST_F(AnimationAnimationTest, ElementDestructorClearsAnimationTarget) +{ + // This test expects incorrect behaviour should be removed once Element + // and Animation are moved to Oilpan. See crbug.com/362404 for context. + Timing timing; + timing.iterationDuration = 5; + RefPtrWillBeRawPtr<Animation> animation = Animation::create(element.get(), nullptr, timing); + EXPECT_EQ(element.get(), animation->target()); + document->timeline().play(animation.get()); + document.clear(); + element.clear(); +#if !ENABLE(OILPAN) + EXPECT_EQ(0, animation->target()); +#endif +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTestHelper.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationTestHelper.cpp new file mode 100644 index 00000000000..12f4e378527 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTestHelper.cpp @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/AnimationTestHelper.h" + +#include "bindings/v8/V8Binding.h" + +namespace WebCore { + +v8::Handle<v8::Value> stringToV8Value(String string) +{ + return v8::Handle<v8::Value>::Cast(v8String(v8::Isolate::GetCurrent(), string)); +} + +v8::Handle<v8::Value> doubleToV8Value(double number) +{ + return v8::Handle<v8::Value>::Cast(v8::Number::New(v8::Isolate::GetCurrent(), number)); +} + +void setV8ObjectPropertyAsString(v8::Handle<v8::Object> object, String name, String value) +{ + object->Set(stringToV8Value(name), stringToV8Value(value)); +} + +void setV8ObjectPropertyAsNumber(v8::Handle<v8::Object> object, String name, double value) +{ + object->Set(stringToV8Value(name), doubleToV8Value(value)); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTestHelper.h b/chromium/third_party/WebKit/Source/core/animation/AnimationTestHelper.h new file mode 100644 index 00000000000..5d20c7c7152 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTestHelper.h @@ -0,0 +1,23 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef AnimationTestHelper_h +#define AnimationTestHelper_h + +#include "wtf/text/WTFString.h" +#include <v8.h> + +namespace WebCore { + +v8::Handle<v8::Value> stringToV8Value(String); + +v8::Handle<v8::Value> doubleToV8Value(double); + +void setV8ObjectPropertyAsString(v8::Handle<v8::Object>, String, String); + +void setV8ObjectPropertyAsNumber(v8::Handle<v8::Object>, String, double); + +} // namespace WebCore + +#endif // AnimationTestHelper_h diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.cpp new file mode 100644 index 00000000000..4553fbe880f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/AnimationTimeline.h" + +#include "core/animation/ActiveAnimations.h" +#include "core/animation/AnimationClock.h" +#include "core/dom/Document.h" +#include "core/frame/FrameView.h" +#include "core/page/Page.h" +#include "platform/TraceEvent.h" + +namespace WebCore { + +// This value represents 1 frame at 30Hz plus a little bit of wiggle room. +// TODO: Plumb a nominal framerate through and derive this value from that. +const double AnimationTimeline::s_minimumDelay = 0.04; + + +PassRefPtrWillBeRawPtr<AnimationTimeline> AnimationTimeline::create(Document* document, PassOwnPtrWillBeRawPtr<PlatformTiming> timing) +{ + return adoptRefWillBeNoop(new AnimationTimeline(document, timing)); +} + +AnimationTimeline::AnimationTimeline(Document* document, PassOwnPtrWillBeRawPtr<PlatformTiming> timing) + : m_document(document) +{ + if (!timing) + m_timing = adoptPtrWillBeNoop(new AnimationTimelineTiming(this)); + else + m_timing = timing; + + ASSERT(document); +} + +AnimationTimeline::~AnimationTimeline() +{ +#if !ENABLE(OILPAN) + for (WillBeHeapHashSet<RawPtrWillBeWeakMember<AnimationPlayer> >::iterator it = m_players.begin(); it != m_players.end(); ++it) + (*it)->timelineDestroyed(); +#endif +} + +AnimationPlayer* AnimationTimeline::createAnimationPlayer(AnimationNode* child) +{ + RefPtrWillBeRawPtr<AnimationPlayer> player = AnimationPlayer::create(m_document->contextDocument().get(), *this, child); + AnimationPlayer* result = player.get(); + m_players.add(result); + setOutdatedAnimationPlayer(result); + return result; +} + +AnimationPlayer* AnimationTimeline::play(AnimationNode* child) +{ + if (!m_document) + return 0; + AnimationPlayer* player = createAnimationPlayer(child); + m_document->compositorPendingAnimations().add(player); + return player; +} + +void AnimationTimeline::wake() +{ + m_timing->serviceOnNextFrame(); +} + +void AnimationTimeline::serviceAnimations(TimingUpdateReason reason) +{ + TRACE_EVENT0("webkit", "AnimationTimeline::serviceAnimations"); + + m_timing->cancelWake(); + + double timeToNextEffect = std::numeric_limits<double>::infinity(); + WillBeHeapVector<RawPtrWillBeMember<AnimationPlayer> > players; + for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) + players.append(it->get()); + + std::sort(players.begin(), players.end(), AnimationPlayer::hasLowerPriority); + + for (size_t i = 0; i < players.size(); ++i) { + AnimationPlayer* player = players[i]; + if (player->update(reason)) + timeToNextEffect = std::min(timeToNextEffect, player->timeToEffectChange()); + else + m_playersNeedingUpdate.remove(player); + } + + if (timeToNextEffect < s_minimumDelay) + m_timing->serviceOnNextFrame(); + else if (timeToNextEffect != std::numeric_limits<double>::infinity()) + m_timing->wakeAfter(timeToNextEffect - s_minimumDelay); + + ASSERT(!hasOutdatedAnimationPlayer()); +} + +void AnimationTimeline::AnimationTimelineTiming::wakeAfter(double duration) +{ + m_timer.startOneShot(duration, FROM_HERE); +} + +void AnimationTimeline::AnimationTimelineTiming::cancelWake() +{ + m_timer.stop(); +} + +void AnimationTimeline::AnimationTimelineTiming::serviceOnNextFrame() +{ + if (m_timeline->m_document && m_timeline->m_document->view()) + m_timeline->m_document->view()->scheduleAnimation(); +} + +void AnimationTimeline::AnimationTimelineTiming::trace(Visitor* visitor) +{ + visitor->trace(m_timeline); + AnimationTimeline::PlatformTiming::trace(visitor); +} + +double AnimationTimeline::currentTime(bool& isNull) +{ + return currentTimeInternal(isNull) * 1000; +} + +double AnimationTimeline::currentTimeInternal(bool& isNull) +{ + if (!m_document) { + isNull = true; + return std::numeric_limits<double>::quiet_NaN(); + } + double result = m_document->animationClock().currentTime() - zeroTime(); + isNull = std::isnan(result); + return result; +} + +double AnimationTimeline::currentTime() +{ + return currentTimeInternal() * 1000; +} + +double AnimationTimeline::currentTimeInternal() +{ + bool isNull; + return currentTimeInternal(isNull); +} + +double AnimationTimeline::effectiveTime() +{ + double time = currentTimeInternal(); + return std::isnan(time) ? 0 : time; +} + +void AnimationTimeline::pauseAnimationsForTesting(double pauseTime) +{ + for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) + (*it)->pauseForTesting(pauseTime); + serviceAnimations(TimingUpdateOnDemand); +} + +bool AnimationTimeline::hasOutdatedAnimationPlayer() const +{ + for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) { + if ((*it)->outdated()) + return true; + } + return false; +} + +void AnimationTimeline::setOutdatedAnimationPlayer(AnimationPlayer* player) +{ + ASSERT(player->outdated()); + m_playersNeedingUpdate.add(player); + if (m_document && m_document->page() && !m_document->page()->animator().isServicingAnimations()) + m_timing->serviceOnNextFrame(); +} + +size_t AnimationTimeline::numberOfActiveAnimationsForTesting() const +{ + // Includes all players whose directly associated timed items + // are current or in effect. + size_t count = 0; + for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) { + const AnimationNode* animationNode = (*it)->source(); + if ((*it)->hasStartTime()) + count += (animationNode && (animationNode->isCurrent() || animationNode->isInEffect())); + } + return count; +} + +#if !ENABLE(OILPAN) +void AnimationTimeline::detachFromDocument() +{ + // FIXME: AnimationTimeline should keep Document alive. + m_document = nullptr; +} +#endif + +void AnimationTimeline::trace(Visitor* visitor) +{ + visitor->trace(m_document); + visitor->trace(m_timing); + visitor->trace(m_playersNeedingUpdate); + visitor->trace(m_players); +} + +} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentTimeline.h b/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.h index 2bf58f852c6..1a523a28651 100644 --- a/chromium/third_party/WebKit/Source/core/animation/DocumentTimeline.h +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.h @@ -28,14 +28,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentTimeline_h -#define DocumentTimeline_h +#ifndef AnimationTimeline_h +#define AnimationTimeline_h #include "core/animation/AnimationEffect.h" -#include "core/animation/Player.h" +#include "core/animation/AnimationPlayer.h" #include "core/dom/Element.h" #include "core/events/Event.h" #include "platform/Timer.h" +#include "platform/heap/Handle.h" #include "wtf/RefCounted.h" #include "wtf/RefPtr.h" #include "wtf/Vector.h" @@ -43,80 +44,80 @@ namespace WebCore { class Document; -class TimedItem; +class AnimationNode; -// DocumentTimeline is constructed and owned by Document, and tied to its lifecycle. -class DocumentTimeline : public RefCounted<DocumentTimeline> { +// AnimationTimeline is constructed and owned by Document, and tied to its lifecycle. +class AnimationTimeline : public RefCountedWillBeGarbageCollectedFinalized<AnimationTimeline> { public: - class PlatformTiming { + class PlatformTiming : public NoBaseWillBeGarbageCollectedFinalized<PlatformTiming> { public: - // Calls DocumentTimeline's wake() method after duration seconds. + // Calls AnimationTimeline's wake() method after duration seconds. virtual void wakeAfter(double duration) = 0; virtual void cancelWake() = 0; virtual void serviceOnNextFrame() = 0; virtual ~PlatformTiming() { } - + virtual void trace(Visitor*) { } }; - static PassRefPtr<DocumentTimeline> create(Document*, PassOwnPtr<PlatformTiming> = nullptr); - // Returns whether style recalc was triggered. - bool serviceAnimations(); + static PassRefPtrWillBeRawPtr<AnimationTimeline> create(Document*, PassOwnPtrWillBeRawPtr<PlatformTiming> = nullptr); + ~AnimationTimeline(); + + void serviceAnimations(TimingUpdateReason); // Creates a player attached to this timeline, but without a start time. - Player* createPlayer(TimedItem*); - Player* play(TimedItem*); - - // Called from setReadyState() in Document.cpp to set m_zeroTime to - // performance.timing.domInteractive - void setZeroTime(double); - bool hasStarted() const { return !isNull(m_zeroTime); } - double zeroTime() const { return m_zeroTime; } + AnimationPlayer* createAnimationPlayer(AnimationNode*); + AnimationPlayer* play(AnimationNode*); + +#if !ENABLE(OILPAN) + void playerDestroyed(AnimationPlayer* player) + { + ASSERT(m_players.contains(player)); + m_players.remove(player); + } +#endif + + bool hasPendingUpdates() const { return !m_playersNeedingUpdate.isEmpty(); } + double zeroTime() const { return 0; } + double currentTime(bool& isNull); double currentTime(); + double currentTimeInternal(bool& isNull); + double currentTimeInternal(); + double effectiveTime(); void pauseAnimationsForTesting(double); size_t numberOfActiveAnimationsForTesting() const; - const Vector<RefPtr<Player> >& players() const { return m_players; } - void addEventToDispatch(EventTarget* target, PassRefPtr<Event> event) - { - m_events.append(EventToDispatch(target, event)); - } + void setOutdatedAnimationPlayer(AnimationPlayer*); + bool hasOutdatedAnimationPlayer() const; + + Document* document() { return m_document.get(); } +#if !ENABLE(OILPAN) + void detachFromDocument(); +#endif + void wake(); - void dispatchEvents(); - void dispatchEventsAsync(); + void trace(Visitor*); protected: - DocumentTimeline(Document*, PassOwnPtr<PlatformTiming>); + AnimationTimeline(Document*, PassOwnPtrWillBeRawPtr<PlatformTiming>); private: - double m_zeroTime; - Document* m_document; - Timer<DocumentTimeline> m_eventDistpachTimer; - Vector<RefPtr<Player> > m_players; - - void eventDispatchTimerFired(Timer<DocumentTimeline>*); - void wake(); - - struct EventToDispatch { - EventToDispatch(EventTarget* target, PassRefPtr<Event> event) - : target(target) - , event(event) - { - } - RefPtr<EventTarget> target; - RefPtr<Event> event; - }; - Vector<EventToDispatch> m_events; + RawPtrWillBeMember<Document> m_document; + // AnimationPlayers which will be updated on the next frame + // i.e. current, in effect, or had timing changed + WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> > m_playersNeedingUpdate; + WillBeHeapHashSet<RawPtrWillBeWeakMember<AnimationPlayer> > m_players; + friend class SMILTimeContainer; static const double s_minimumDelay; - OwnPtr<PlatformTiming> m_timing; + OwnPtrWillBeMember<PlatformTiming> m_timing; - class DocumentTimelineTiming : public PlatformTiming { + class AnimationTimelineTiming FINAL : public PlatformTiming { public: - DocumentTimelineTiming(DocumentTimeline* documentTimeline) - : m_timeline(documentTimeline) - , m_timer(this, &DocumentTimelineTiming::timerFired) + AnimationTimelineTiming(AnimationTimeline* timeline) + : m_timeline(timeline) + , m_timer(this, &AnimationTimelineTiming::timerFired) { ASSERT(m_timeline); } @@ -125,15 +126,16 @@ private: virtual void cancelWake() OVERRIDE; virtual void serviceOnNextFrame() OVERRIDE; - void timerFired(Timer<DocumentTimelineTiming>*) { m_timeline->wake(); } + void timerFired(Timer<AnimationTimelineTiming>*) { m_timeline->wake(); } - private: - DocumentTimeline* m_timeline; - Timer<DocumentTimelineTiming> m_timer; + virtual void trace(Visitor*) OVERRIDE; + private: + RawPtrWillBeMember<AnimationTimeline> m_timeline; + Timer<AnimationTimelineTiming> m_timer; }; - friend class AnimationDocumentTimelineTest; + friend class AnimationAnimationTimelineTest; }; } // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.idl b/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.idl new file mode 100644 index 00000000000..b2c627afde3 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTimeline.idl @@ -0,0 +1,11 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + RuntimeEnabled=WebAnimationsAPI, + WillBeGarbageCollected, +] interface AnimationTimeline { + readonly attribute double? currentTime; + AnimationPlayer play(AnimationNode source); +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentTimelineTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationTimelineTest.cpp index 41fad8ce2c4..68cc2895a9b 100644 --- a/chromium/third_party/WebKit/Source/core/animation/DocumentTimelineTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTimelineTest.cpp @@ -29,12 +29,12 @@ */ #include "config.h" -#include "core/animation/DocumentTimeline.h" +#include "core/animation/AnimationTimeline.h" #include "core/animation/Animation.h" #include "core/animation/AnimationClock.h" -#include "core/animation/KeyframeAnimationEffect.h" -#include "core/animation/TimedItem.h" +#include "core/animation/AnimationNode.h" +#include "core/animation/KeyframeEffectModel.h" #include "core/dom/Document.h" #include "core/dom/Element.h" #include "core/dom/QualifiedName.h" @@ -45,7 +45,7 @@ namespace WebCore { -class MockPlatformTiming : public DocumentTimeline::PlatformTiming { +class MockPlatformTiming : public AnimationTimeline::PlatformTiming { public: MOCK_METHOD1(wakeAfter, void(double)); @@ -53,7 +53,7 @@ public: MOCK_METHOD0(serviceOnNextFrame, void()); /** - * DocumentTimelines should do one of the following things after servicing animations: + * AnimationTimelines should do one of the following things after servicing animations: * - cancel the timer and not request to be woken again (expectNoMoreActions) * - cancel the timer and request to be woken on the next frame (expectNextFrameAction) * - cancel the timer and request to be woken at some point in the future (expectDelayedAction) @@ -77,37 +77,41 @@ public: EXPECT_CALL(*this, cancelWake()).InSequence(sequence); EXPECT_CALL(*this, wakeAfter(when)).InSequence(sequence); } + + void trace(Visitor* visitor) + { + AnimationTimeline::PlatformTiming::trace(visitor); + } }; -class AnimationDocumentTimelineTest : public ::testing::Test { +class AnimationAnimationTimelineTest : public ::testing::Test { protected: virtual void SetUp() { document = Document::create(); document->animationClock().resetTimeForTesting(); - element = Element::create(nullQName() , document.get()); + element = Element::create(QualifiedName::null() , document.get()); platformTiming = new MockPlatformTiming; - timeline = DocumentTimeline::create(document.get(), adoptPtr(platformTiming)); - timeline->setZeroTime(0); - ASSERT_EQ(0, timeline->currentTime()); + timeline = AnimationTimeline::create(document.get(), adoptPtrWillBeNoop(platformTiming)); + ASSERT_EQ(0, timeline->currentTimeInternal()); } virtual void TearDown() { - timeline.release(); document.release(); element.release(); + timeline.release(); } void updateClockAndService(double time) { document->animationClock().updateTime(time); - timeline->serviceAnimations(); + timeline->serviceAnimations(TimingUpdateForAnimationFrame); } - RefPtr<Document> document; - RefPtr<Element> element; - RefPtr<DocumentTimeline> timeline; + RefPtrWillBePersistent<Document> document; + RefPtrWillBePersistent<Element> element; + RefPtrWillBePersistent<AnimationTimeline> timeline; Timing timing; MockPlatformTiming* platformTiming; @@ -118,155 +122,135 @@ protected: double minimumDelay() { - return DocumentTimeline::s_minimumDelay; + return AnimationTimeline::s_minimumDelay; } }; -TEST_F(AnimationDocumentTimelineTest, HasStarted) +TEST_F(AnimationAnimationTimelineTest, HasStarted) { - timeline = DocumentTimeline::create(document.get()); - EXPECT_FALSE(timeline->hasStarted()); - timeline->setZeroTime(0); - EXPECT_TRUE(timeline->hasStarted()); + timeline = AnimationTimeline::create(document.get()); } -TEST_F(AnimationDocumentTimelineTest, EmptyKeyframeAnimation) +TEST_F(AnimationAnimationTimelineTest, EmptyKeyframeAnimation) { - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()); - RefPtr<Animation> anim = Animation::create(element.get(), effect, timing); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()); + RefPtrWillBeRawPtr<Animation> anim = Animation::create(element.get(), effect, timing); timeline->play(anim.get()); platformTiming->expectNoMoreActions(); updateClockAndService(0); - EXPECT_FLOAT_EQ(0, timeline->currentTime()); - EXPECT_TRUE(anim->compositableValues()->isEmpty()); + EXPECT_FLOAT_EQ(0, timeline->currentTimeInternal()); + EXPECT_FALSE(anim->isInEffect()); platformTiming->expectNoMoreActions(); updateClockAndService(100); - EXPECT_FLOAT_EQ(100, timeline->currentTime()); + EXPECT_FLOAT_EQ(100, timeline->currentTimeInternal()); } -TEST_F(AnimationDocumentTimelineTest, EmptyTimelineDoesNotTriggerStyleRecalc) +TEST_F(AnimationAnimationTimelineTest, EmptyForwardsKeyframeAnimation) { - document->animationClock().updateTime(100); - EXPECT_FALSE(timeline->serviceAnimations()); -} + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()); + timing.fillMode = Timing::FillModeForwards; + RefPtrWillBeRawPtr<Animation> anim = Animation::create(element.get(), effect, timing); -TEST_F(AnimationDocumentTimelineTest, EmptyPlayerDoesNotTriggerStyleRecalc) -{ - timeline->play(0); - document->animationClock().updateTime(100); - EXPECT_FALSE(timeline->serviceAnimations()); -} - -TEST_F(AnimationDocumentTimelineTest, EmptyTargetDoesNotTriggerStyleRecalc) -{ - timing.iterationDuration = 200; - timeline->play(Animation::create(0, KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timing).get()); - document->animationClock().updateTime(100); - EXPECT_FALSE(timeline->serviceAnimations()); -} + timeline->play(anim.get()); -TEST_F(AnimationDocumentTimelineTest, EmptyEffectDoesNotTriggerStyleRecalc) -{ - timeline->play(Animation::create(element.get(), 0, timing).get()); - document->animationClock().updateTime(100); - EXPECT_FALSE(timeline->serviceAnimations()); -} + platformTiming->expectNoMoreActions(); + updateClockAndService(0); + EXPECT_FLOAT_EQ(0, timeline->currentTimeInternal()); + EXPECT_TRUE(anim->isInEffect()); -TEST_F(AnimationDocumentTimelineTest, TriggerStyleRecalc) -{ - timeline->play(Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timing).get()); - document->animationClock().updateTime(100); - EXPECT_TRUE(timeline->serviceAnimations()); + platformTiming->expectNoMoreActions(); + updateClockAndService(100); + EXPECT_FLOAT_EQ(100, timeline->currentTimeInternal()); } -TEST_F(AnimationDocumentTimelineTest, ZeroTime) +TEST_F(AnimationAnimationTimelineTest, ZeroTime) { - timeline = DocumentTimeline::create(document.get()); + timeline = AnimationTimeline::create(document.get()); + bool isNull; document->animationClock().updateTime(100); - EXPECT_TRUE(isNull(timeline->currentTime())); + EXPECT_EQ(100, timeline->currentTimeInternal()); + EXPECT_EQ(100, timeline->currentTimeInternal(isNull)); + EXPECT_FALSE(isNull); document->animationClock().updateTime(200); - EXPECT_TRUE(isNull(timeline->currentTime())); - - timeline->setZeroTime(300); - document->animationClock().updateTime(300); - EXPECT_EQ(0, timeline->currentTime()); - - document->animationClock().updateTime(400); - EXPECT_EQ(100, timeline->currentTime()); + EXPECT_EQ(200, timeline->currentTimeInternal()); + EXPECT_EQ(200, timeline->currentTimeInternal(isNull)); + EXPECT_FALSE(isNull); } -TEST_F(AnimationDocumentTimelineTest, PauseForTesting) +TEST_F(AnimationAnimationTimelineTest, PauseForTesting) { float seekTime = 1; - RefPtr<Animation> anim1 = Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timing); - RefPtr<Animation> anim2 = Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timing); - Player* player1 = timeline->play(anim1.get()); - Player* player2 = timeline->play(anim2.get()); + timing.fillMode = Timing::FillModeForwards; + RefPtrWillBeRawPtr<Animation> anim1 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timing); + RefPtrWillBeRawPtr<Animation> anim2 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timing); + AnimationPlayer* player1 = timeline->play(anim1.get()); + AnimationPlayer* player2 = timeline->play(anim2.get()); timeline->pauseAnimationsForTesting(seekTime); - EXPECT_FLOAT_EQ(seekTime, player1->currentTime()); - EXPECT_FLOAT_EQ(seekTime, player2->currentTime()); + EXPECT_FLOAT_EQ(seekTime, player1->currentTimeInternal()); + EXPECT_FLOAT_EQ(seekTime, player2->currentTimeInternal()); } -TEST_F(AnimationDocumentTimelineTest, NumberOfActiveAnimations) +TEST_F(AnimationAnimationTimelineTest, NumberOfActiveAnimations) { Timing timingForwardFill; - timingForwardFill.hasIterationDuration = true; timingForwardFill.iterationDuration = 2; + timingForwardFill.fillMode = Timing::FillModeForwards; Timing timingNoFill; - timingNoFill.hasIterationDuration = true; timingNoFill.iterationDuration = 2; timingNoFill.fillMode = Timing::FillModeNone; Timing timingBackwardFillDelay; - timingBackwardFillDelay.hasIterationDuration = true; timingBackwardFillDelay.iterationDuration = 1; timingBackwardFillDelay.fillMode = Timing::FillModeBackwards; timingBackwardFillDelay.startDelay = 1; Timing timingNoFillDelay; - timingNoFillDelay.hasIterationDuration = true; timingNoFillDelay.iterationDuration = 1; timingNoFillDelay.fillMode = Timing::FillModeNone; timingNoFillDelay.startDelay = 1; - RefPtr<Animation> anim1 = Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timingForwardFill); - RefPtr<Animation> anim2 = Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timingNoFill); - RefPtr<Animation> anim3 = Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timingBackwardFillDelay); - RefPtr<Animation> anim4 = Animation::create(element.get(), KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()), timingNoFillDelay); + Timing timingAutoFill; + timingAutoFill.iterationDuration = 2; + + RefPtrWillBeRawPtr<Animation> anim1 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timingForwardFill); + RefPtrWillBeRawPtr<Animation> anim2 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timingNoFill); + RefPtrWillBeRawPtr<Animation> anim3 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timingBackwardFillDelay); + RefPtrWillBeRawPtr<Animation> anim4 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timingNoFillDelay); + RefPtrWillBeRawPtr<Animation> anim5 = Animation::create(element.get(), AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()), timingAutoFill); timeline->play(anim1.get()); timeline->play(anim2.get()); timeline->play(anim3.get()); timeline->play(anim4.get()); + timeline->play(anim5.get()); platformTiming->expectNextFrameAction(); updateClockAndService(0); - EXPECT_EQ(4U, timeline->numberOfActiveAnimationsForTesting()); + EXPECT_EQ(5U, timeline->numberOfActiveAnimationsForTesting()); platformTiming->expectNextFrameAction(); updateClockAndService(0.5); - EXPECT_EQ(4U, timeline->numberOfActiveAnimationsForTesting()); + EXPECT_EQ(5U, timeline->numberOfActiveAnimationsForTesting()); platformTiming->expectNextFrameAction(); updateClockAndService(1.5); - EXPECT_EQ(4U, timeline->numberOfActiveAnimationsForTesting()); + EXPECT_EQ(5U, timeline->numberOfActiveAnimationsForTesting()); platformTiming->expectNoMoreActions(); updateClockAndService(3); - EXPECT_EQ(1U, timeline->numberOfActiveAnimationsForTesting()); + EXPECT_EQ(0U, timeline->numberOfActiveAnimationsForTesting()); } -TEST_F(AnimationDocumentTimelineTest, DelayBeforeAnimationStart) +TEST_F(AnimationAnimationTimelineTest, DelayBeforeAnimationStart) { - timing.hasIterationDuration = true; timing.iterationDuration = 2; timing.startDelay = 5; - RefPtr<Animation> anim = Animation::create(element.get(), 0, timing); + RefPtrWillBeRawPtr<Animation> anim = Animation::create(element.get(), nullptr, timing); timeline->play(anim.get()); @@ -284,4 +268,26 @@ TEST_F(AnimationDocumentTimelineTest, DelayBeforeAnimationStart) updateClockAndService(4.98); } +TEST_F(AnimationAnimationTimelineTest, PlayAfterDocumentDeref) +{ + timing.iterationDuration = 2; + timing.startDelay = 5; + + timeline = &document->timeline(); + element = nullptr; + document = nullptr; + + RefPtrWillBeRawPtr<Animation> anim = Animation::create(0, nullptr, timing); + // Test passes if this does not crash. + timeline->play(anim.get()); +} + +TEST_F(AnimationAnimationTimelineTest, UseAnimationPlayerAfterTimelineDeref) +{ + RefPtrWillBeRawPtr<AnimationPlayer> player = timeline->createAnimationPlayer(0); + timeline.clear(); + // Test passes if this does not crash. + player->setStartTime(0); +} + } diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp new file mode 100644 index 00000000000..3fbdf306611 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "core/animation/AnimationTranslationUtil.h" + +#include "platform/graphics/filters/FilterOperations.h" +#include "platform/graphics/filters/SkiaImageFilterBuilder.h" +#include "platform/transforms/InterpolatedTransformOperation.h" +#include "platform/transforms/Matrix3DTransformOperation.h" +#include "platform/transforms/MatrixTransformOperation.h" +#include "platform/transforms/PerspectiveTransformOperation.h" +#include "platform/transforms/RotateTransformOperation.h" +#include "platform/transforms/ScaleTransformOperation.h" +#include "platform/transforms/SkewTransformOperation.h" +#include "platform/transforms/TransformOperations.h" +#include "platform/transforms/TransformationMatrix.h" +#include "platform/transforms/TranslateTransformOperation.h" +#include "public/platform/WebTransformOperations.h" + +using namespace blink; + +namespace WebCore { + +void toWebTransformOperations(const TransformOperations& transformOperations, WebTransformOperations* webTransformOperations) +{ + // We need to do a deep copy the transformOperations may contain ref pointers to TransformOperation objects. + for (size_t j = 0; j < transformOperations.size(); ++j) { + switch (transformOperations.operations()[j]->type()) { + case TransformOperation::ScaleX: + case TransformOperation::ScaleY: + case TransformOperation::ScaleZ: + case TransformOperation::Scale3D: + case TransformOperation::Scale: { + ScaleTransformOperation* transform = static_cast<ScaleTransformOperation*>(transformOperations.operations()[j].get()); + webTransformOperations->appendScale(transform->x(), transform->y(), transform->z()); + break; + } + case TransformOperation::TranslateX: + case TransformOperation::TranslateY: + case TransformOperation::TranslateZ: + case TransformOperation::Translate3D: + case TransformOperation::Translate: { + TranslateTransformOperation* transform = static_cast<TranslateTransformOperation*>(transformOperations.operations()[j].get()); + ASSERT(transform->x().isFixed() && transform->y().isFixed()); + webTransformOperations->appendTranslate(transform->x().value(), transform->y().value(), transform->z()); + break; + } + case TransformOperation::RotateX: + case TransformOperation::RotateY: + case TransformOperation::Rotate3D: + case TransformOperation::Rotate: { + RotateTransformOperation* transform = static_cast<RotateTransformOperation*>(transformOperations.operations()[j].get()); + webTransformOperations->appendRotate(transform->x(), transform->y(), transform->z(), transform->angle()); + break; + } + case TransformOperation::SkewX: + case TransformOperation::SkewY: + case TransformOperation::Skew: { + SkewTransformOperation* transform = static_cast<SkewTransformOperation*>(transformOperations.operations()[j].get()); + webTransformOperations->appendSkew(transform->angleX(), transform->angleY()); + break; + } + case TransformOperation::Matrix: { + MatrixTransformOperation* transform = static_cast<MatrixTransformOperation*>(transformOperations.operations()[j].get()); + TransformationMatrix m = transform->matrix(); + webTransformOperations->appendMatrix(TransformationMatrix::toSkMatrix44(m)); + break; + } + case TransformOperation::Matrix3D: { + Matrix3DTransformOperation* transform = static_cast<Matrix3DTransformOperation*>(transformOperations.operations()[j].get()); + TransformationMatrix m = transform->matrix(); + webTransformOperations->appendMatrix(TransformationMatrix::toSkMatrix44(m)); + break; + } + case TransformOperation::Perspective: { + PerspectiveTransformOperation* transform = static_cast<PerspectiveTransformOperation*>(transformOperations.operations()[j].get()); + webTransformOperations->appendPerspective(transform->perspective()); + break; + } + case TransformOperation::Interpolated: { + TransformationMatrix m; + transformOperations.operations()[j]->apply(m, FloatSize()); + webTransformOperations->appendMatrix(TransformationMatrix::toSkMatrix44(m)); + break; + } + case TransformOperation::Identity: + webTransformOperations->appendIdentity(); + break; + case TransformOperation::None: + // Do nothing. + break; + } // switch + } // for each operation +} + +bool toWebFilterOperations(const FilterOperations& inOperations, WebFilterOperations* outOperations) +{ + SkiaImageFilterBuilder builder; + FilterOutsets outsets = inOperations.outsets(); + builder.setCropOffset(FloatSize(outsets.left(), outsets.top())); + return builder.buildFilterOperations(inOperations, outOperations); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.h b/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.h new file mode 100644 index 00000000000..c69635b5200 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AnimationTranslationUtil_h +#define AnimationTranslationUtil_h + +namespace blink { +class WebTransformOperations; +class WebFilterOperations; +} + +namespace WebCore { + +class FilterOperations; +class TransformOperations; + +void toWebTransformOperations(const TransformOperations& inOperations, blink::WebTransformOperations* outOperations); +bool toWebFilterOperations(const FilterOperations& inOperations, blink::WebFilterOperations* outOperations); + +} // namespace WebCore + +#endif // AnimationTranslationUtil_h diff --git a/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtilTest.cpp b/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtilTest.cpp new file mode 100644 index 00000000000..d55e790d2af --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/AnimationTranslationUtilTest.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "core/animation/AnimationTranslationUtil.h" + +#include "core/animation/css/CSSAnimationData.h" +#include "platform/animation/KeyframeValueList.h" +#include "platform/geometry/IntSize.h" +#include "platform/graphics/filters/FilterOperations.h" +#include "platform/transforms/Matrix3DTransformOperation.h" +#include "platform/transforms/RotateTransformOperation.h" +#include "platform/transforms/ScaleTransformOperation.h" +#include "platform/transforms/TransformOperations.h" +#include "platform/transforms/TranslateTransformOperation.h" +#include "public/platform/WebAnimation.h" +#include "public/platform/WebFilterOperations.h" +#include "public/platform/WebTransformOperations.h" +#include "wtf/RefPtr.h" +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +using namespace WebCore; +using namespace blink; + +namespace { + +class WebTransformOperationsMock : public blink::WebTransformOperations { +public: + MOCK_CONST_METHOD1(canBlendWith, bool(const WebTransformOperations&)); + MOCK_METHOD3(appendTranslate, void(double, double, double)); + MOCK_METHOD4(appendRotate, void(double, double, double, double)); + MOCK_METHOD3(appendScale, void(double, double, double)); + MOCK_METHOD2(appendSkew, void(double, double)); + MOCK_METHOD1(appendPerspective, void(double)); + MOCK_METHOD1(appendMatrix, void(const SkMatrix44&)); + MOCK_METHOD0(appendIdentity, void()); + MOCK_CONST_METHOD0(isIdentity, bool()); +}; + +class WebFilterOperationsMock : public blink::WebFilterOperations { +public: + MOCK_METHOD1(appendGrayscaleFilter, void(float)); + MOCK_METHOD1(appendSepiaFilter, void(float)); + MOCK_METHOD1(appendSaturateFilter, void(float)); + MOCK_METHOD1(appendHueRotateFilter, void(float)); + MOCK_METHOD1(appendInvertFilter, void(float)); + MOCK_METHOD1(appendBrightnessFilter, void(float)); + MOCK_METHOD1(appendContrastFilter, void(float)); + MOCK_METHOD1(appendOpacityFilter, void(float)); + MOCK_METHOD1(appendBlurFilter, void(float)); + MOCK_METHOD3(appendDropShadowFilter, void(WebPoint, float, WebColor)); + MOCK_METHOD1(appendColorMatrixFilter, void(SkScalar[20])); + MOCK_METHOD2(appendZoomFilter, void(float, int)); + MOCK_METHOD1(appendSaturatingBrightnessFilter, void(float)); + MOCK_METHOD1(appendReferenceFilter, void(SkImageFilter*)); + MOCK_METHOD0(clear, void()); +}; + +TEST(AnimationTranslationUtilTest, transformsWork) +{ + TransformOperations ops; + WebTransformOperationsMock outOps; + + EXPECT_CALL(outOps, appendTranslate(2, 0, 0)); + EXPECT_CALL(outOps, appendRotate(0.1, 0.2, 0.3, 200000.4)); + EXPECT_CALL(outOps, appendScale(50.2, 100, -4)); + + ops.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TranslateX)); + ops.operations().append(RotateTransformOperation::create(0.1, 0.2, 0.3, 200000.4, TransformOperation::Rotate3D)); + ops.operations().append(ScaleTransformOperation::create(50.2, 100, -4, TransformOperation::Scale3D)); + toWebTransformOperations(ops, &outOps); +} + +TEST(AnimationTranslationUtilTest, filtersWork) +{ + FilterOperations ops; + WebFilterOperationsMock outOps; + + EXPECT_CALL(outOps, appendSaturateFilter(0.5)); + EXPECT_CALL(outOps, appendGrayscaleFilter(0.2f)); + EXPECT_CALL(outOps, appendSepiaFilter(0.8f)); + EXPECT_CALL(outOps, appendOpacityFilter(0.1f)); + + ops.operations().append(BasicColorMatrixFilterOperation::create(0.5, FilterOperation::SATURATE)); + ops.operations().append(BasicColorMatrixFilterOperation::create(0.2, FilterOperation::GRAYSCALE)); + ops.operations().append(BasicColorMatrixFilterOperation::create(0.8, FilterOperation::SEPIA)); + ops.operations().append(BasicColorMatrixFilterOperation::create(0.1, FilterOperation::OPACITY)); + toWebFilterOperations(ops, &outOps); +} + +} + diff --git a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp index 929f0737165..f2128db3b61 100644 --- a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.cpp @@ -35,12 +35,12 @@ #include "core/animation/AnimatableFilterOperations.h" #include "core/animation/AnimatableTransform.h" #include "core/animation/AnimatableValue.h" +#include "core/animation/AnimationTranslationUtil.h" #include "core/animation/CompositorAnimationsImpl.h" -#include "core/platform/animation/AnimationTranslationUtil.h" -#include "core/rendering/CompositedLayerMapping.h" #include "core/rendering/RenderBoxModelObject.h" #include "core/rendering/RenderLayer.h" #include "core/rendering/RenderObject.h" +#include "core/rendering/compositing/CompositedLayerMapping.h" #include "public/platform/Platform.h" #include "public/platform/WebAnimation.h" #include "public/platform/WebCompositorSupport.h" @@ -58,10 +58,10 @@ namespace WebCore { namespace { -void getKeyframeValuesForProperty(const KeyframeAnimationEffect* effect, CSSPropertyID id, double scale, bool reverse, KeyframeVector& values) +void getKeyframeValuesForProperty(const KeyframeEffectModelBase* effect, CSSPropertyID id, double scale, bool reverse, PropertySpecificKeyframeVector& values) { ASSERT(values.isEmpty()); - const KeyframeVector& group = effect->getPropertySpecificKeyframes(id); + const PropertySpecificKeyframeVector& group = effect->getPropertySpecificKeyframes(id); if (reverse) { for (size_t i = group.size(); i--;) { @@ -105,18 +105,6 @@ PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(c } } -PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const ChainedTimingFunction* timefunc) -{ - RefPtr<ChainedTimingFunction> reversed = ChainedTimingFunction::create(); - for (size_t i = 0; i < timefunc->m_segments.size(); i++) { - size_t index = timefunc->m_segments.size() - i - 1; - - RefPtr<TimingFunction> rtf = reverse(timefunc->m_segments[index].m_timingFunction.get()); - reversed->appendSegment(1 - timefunc->m_segments[index].m_min, rtf.get()); - } - return reversed; -} - PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const TimingFunction* timefunc) { switch (timefunc->type()) { @@ -128,10 +116,6 @@ PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(c const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timefunc); return reverse(cubic); } - case TimingFunction::ChainedFunction: { - const ChainedTimingFunction* chained = toChainedTimingFunction(timefunc); - return reverse(chained); - } // Steps function can not be reversed. case TimingFunction::StepsFunction: @@ -147,103 +131,54 @@ PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(c bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect) { - const KeyframeAnimationEffect& keyframeEffect = *toKeyframeAnimationEffect(&effect); + const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(&effect); - // Are the keyframes convertible? - const KeyframeAnimationEffect::KeyframeVector frames = keyframeEffect.getFrames(); - for (size_t i = 0; i < frames.size(); ++i) { - // Only replace mode can be accelerated - if (frames[i]->composite() != AnimationEffect::CompositeReplace) - return false; + PropertySet properties = keyframeEffect.properties(); - // Check all the properties can be accelerated - const PropertySet properties = frames[i]->properties(); // FIXME: properties creates a whole new PropertySet! + if (properties.isEmpty()) + return false; - if (properties.isEmpty()) - return false; + for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) { + const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it); + ASSERT(frames.size() >= 2); + for (size_t i = 0; i < frames.size(); ++i) { + const Keyframe::PropertySpecificKeyframe *frame = frames[i].get(); + // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue. + if (frame->composite() != AnimationEffect::CompositeReplace || !frame->getAnimatableValue()) + return false; - for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) { switch (*it) { case CSSPropertyOpacity: - continue; - case CSSPropertyWebkitTransform: - if (toAnimatableTransform(frames[i]->propertyValue(CSSPropertyWebkitTransform))->transformOperations().dependsOnBoxSize()) + break; + case CSSPropertyTransform: + if (toAnimatableTransform(frame->getAnimatableValue().get())->transformOperations().dependsOnBoxSize()) return false; - continue; + break; case CSSPropertyWebkitFilter: { - const FilterOperations& operations = toAnimatableFilterOperations(frames[i]->propertyValue(CSSPropertyWebkitFilter))->operations(); + const FilterOperations& operations = toAnimatableFilterOperations(frame->getAnimatableValue().get())->operations(); if (operations.hasFilterThatMovesPixels()) return false; - for (size_t i = 0; i < operations.size(); i++) { - const FilterOperation& op = *operations.at(i); - if (op.type() == FilterOperation::VALIDATED_CUSTOM || op.type() == FilterOperation::CUSTOM) - return false; - } - continue; + break; } default: return false; } + + // FIXME: Remove this check when crbug.com/229405 is resolved + if (i < frames.size() - 1 && frame->easing()->type() == TimingFunction::StepsFunction) + return false; } } - // Is the timing object convertible? CompositorAnimationsImpl::CompositorTiming out; if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, out)) return false; - // Is the timing function convertible? - switch (timing.timingFunction->type()) { - case TimingFunction::LinearFunction: - break; - - case TimingFunction::CubicBezierFunction: - // Can have a cubic if we don't have to split it (IE only have two frames). - if (frames.size() != 2) - return false; - - ASSERT(frames[0]->offset() == 0.0 && frames[1]->offset() == 1.0); - break; - - case TimingFunction::StepsFunction: + // FIXME: We should support non-linear timing functions in the compositor + // eventually. + if (timing.timingFunction->type() != TimingFunction::LinearFunction) return false; - case TimingFunction::ChainedFunction: { - // Currently we only support chained segments in the form the CSS code - // generates. These chained segments are only one level deep and have - // one timing function per frame. - const ChainedTimingFunction* chained = static_cast<const ChainedTimingFunction*>(timing.timingFunction.get()); - if (!chained->m_segments.size()) - return false; - - if (frames.size() != chained->m_segments.size() + 1) - return false; - - for (size_t timeIndex = 0; timeIndex < chained->m_segments.size(); timeIndex++) { - const ChainedTimingFunction::Segment& segment = chained->m_segments[timeIndex]; - - if (frames[timeIndex]->offset() != segment.m_min || frames[timeIndex + 1]->offset() != segment.m_max) - return false; - - switch (segment.m_timingFunction->type()) { - case TimingFunction::LinearFunction: - case TimingFunction::CubicBezierFunction: - continue; - - case TimingFunction::StepsFunction: - case TimingFunction::ChainedFunction: - default: - return false; - } - } - - break; - } - default: - ASSERT_NOT_REACHED(); - return false; - } - return true; } @@ -252,19 +187,19 @@ bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element) return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking; } -bool CompositorAnimations::startAnimationOnCompositor(const Element& element, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds) +bool CompositorAnimations::startAnimationOnCompositor(const Element& element, double startTime, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds) { ASSERT(startedAnimationIds.isEmpty()); ASSERT(isCandidateForAnimationOnCompositor(timing, effect)); ASSERT(canStartAnimationOnCompositor(element)); - const KeyframeAnimationEffect& keyframeEffect = *toKeyframeAnimationEffect(&effect); + const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(&effect); RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer(); ASSERT(layer); Vector<OwnPtr<blink::WebAnimation> > animations; - CompositorAnimationsImpl::getAnimationOnCompositor(timing, keyframeEffect, animations); + CompositorAnimationsImpl::getAnimationOnCompositor(timing, startTime, keyframeEffect, animations); ASSERT(!animations.isEmpty()); for (size_t i = 0; i < animations.size(); ++i) { int id = animations[i]->id(); @@ -284,7 +219,11 @@ bool CompositorAnimations::startAnimationOnCompositor(const Element& element, co void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id) { if (!canStartAnimationOnCompositor(element)) { - ASSERT_NOT_REACHED(); + // When an element is being detached, we cancel any associated + // AnimationPlayers for CSS animations. But by the time we get + // here the mapping will have been removed. + // FIXME: Defer remove/pause operations until after the + // compositing update. return; } toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id); @@ -292,6 +231,10 @@ void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, i void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime) { + // FIXME: canStartAnimationOnCompositor queries compositingState, which is not necessarily up to date. + // https://code.google.com/p/chromium/issues/detail?id=339847 + DisableCompositingQueryAsserts disabler; + if (!canStartAnimationOnCompositor(element)) { ASSERT_NOT_REACHED(); return; @@ -318,7 +261,7 @@ bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, if ((std::floor(timing.iterationCount) != timing.iterationCount) || timing.iterationCount <= 0) return false; - if (!timing.iterationDuration) + if (std::isnan(timing.iterationDuration) || !timing.iterationDuration) return false; // FIXME: Support other playback rates @@ -402,7 +345,6 @@ void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const Plat } case TimingFunction::StepsFunction: - case TimingFunction::ChainedFunction: default: ASSERT_NOT_REACHED(); return; @@ -411,39 +353,29 @@ void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const Plat } // namespace anoymous -void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& curve, const KeyframeVector& keyframes, const TimingFunction& timingFunction) +void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& curve, const PropertySpecificKeyframeVector& keyframes, bool reverse) { for (size_t i = 0; i < keyframes.size(); i++) { + RefPtr<TimingFunction> reversedTimingFunction; const TimingFunction* keyframeTimingFunction = 0; - if (i + 1 < keyframes.size()) { // Last keyframe has no timing function - switch (timingFunction.type()) { - case TimingFunction::LinearFunction: - case TimingFunction::CubicBezierFunction: - keyframeTimingFunction = &timingFunction; - break; - - case TimingFunction::ChainedFunction: { - const ChainedTimingFunction& chained = toChainedTimingFunction(timingFunction); - // ChainedTimingFunction criteria was checked in isCandidate, - // assert it is valid. - ASSERT(keyframes.size() == chained.m_segments.size() + 1); - - keyframeTimingFunction = chained.m_segments[i].m_timingFunction.get(); - break; - } - case TimingFunction::StepsFunction: - default: - ASSERT_NOT_REACHED(); + if (i < keyframes.size() - 1) { // Ignore timing function of last frame. + if (reverse) { + reversedTimingFunction = CompositorAnimationsTimingFunctionReverser::reverse(keyframes[i + 1]->easing()); + keyframeTimingFunction = reversedTimingFunction.get(); + } else { + keyframeTimingFunction = keyframes[i]->easing(); } } - ASSERT(!keyframes[i]->value()->dependsOnUnderlyingValue()); - RefPtr<AnimatableValue> value = keyframes[i]->value()->compositeOnto(0); + // FIXME: This relies on StringKeyframes being eagerly evaluated, which will + // not happen eventually. Instead we should extract the CSSValue here + // and convert using another set of toAnimatableXXXOperations functions. + const AnimatableValue* value = keyframes[i]->getAnimatableValue().get(); switch (curve.type()) { case blink::WebAnimationCurve::AnimationCurveTypeFilter: { OwnPtr<blink::WebFilterOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createFilterOperations()); - bool converted = toWebFilterOperations(toAnimatableFilterOperations(value.get())->operations(), ops.get()); + bool converted = toWebFilterOperations(toAnimatableFilterOperations(value)->operations(), ops.get()); ASSERT_UNUSED(converted, converted); blink::WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release()); @@ -452,14 +384,14 @@ void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& cur break; } case blink::WebAnimationCurve::AnimationCurveTypeFloat: { - blink::WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value.get())->toDouble()); + blink::WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value)->toDouble()); blink::WebFloatAnimationCurve* floatCurve = static_cast<blink::WebFloatAnimationCurve*>(&curve); addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction); break; } case blink::WebAnimationCurve::AnimationCurveTypeTransform: { OwnPtr<blink::WebTransformOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createTransformOperations()); - toWebTransformOperations(toAnimatableTransform(value.get())->transformOperations(), FloatSize(), ops.get()); + toWebTransformOperations(toAnimatableTransform(value)->transformOperations(), ops.get()); blink::WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release()); blink::WebTransformAnimationCurve* transformCurve = static_cast<blink::WebTransformAnimationCurve*>(&curve); @@ -472,23 +404,18 @@ void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& cur } } -void CompositorAnimationsImpl::getAnimationOnCompositor( - const Timing& timing, const KeyframeAnimationEffect& effect, Vector<OwnPtr<blink::WebAnimation> >& animations) +void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, double startTime, const KeyframeEffectModelBase& effect, Vector<OwnPtr<blink::WebAnimation> >& animations) { ASSERT(animations.isEmpty()); CompositorTiming compositorTiming; bool timingValid = convertTimingForCompositor(timing, compositorTiming); ASSERT_UNUSED(timingValid, timingValid); - RefPtr<TimingFunction> timingFunction = timing.timingFunction; - if (compositorTiming.reverse) - timingFunction = CompositorAnimationsTimingFunctionReverser::reverse(timingFunction.get()); - PropertySet properties = effect.properties(); ASSERT(!properties.isEmpty()); for (PropertySet::iterator it = properties.begin(); it != properties.end(); ++it) { - KeyframeVector values; + PropertySpecificKeyframeVector values; getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDuration, compositorTiming.reverse, values); blink::WebAnimation::TargetProperty targetProperty; @@ -498,21 +425,21 @@ void CompositorAnimationsImpl::getAnimationOnCompositor( targetProperty = blink::WebAnimation::TargetPropertyOpacity; blink::WebFloatAnimationCurve* floatCurve = blink::Platform::current()->compositorSupport()->createFloatAnimationCurve(); - addKeyframesToCurve(*floatCurve, values, *timingFunction.get()); + addKeyframesToCurve(*floatCurve, values, compositorTiming.reverse); curve = adoptPtr(floatCurve); break; } case CSSPropertyWebkitFilter: { targetProperty = blink::WebAnimation::TargetPropertyFilter; blink::WebFilterAnimationCurve* filterCurve = blink::Platform::current()->compositorSupport()->createFilterAnimationCurve(); - addKeyframesToCurve(*filterCurve, values, *timingFunction); + addKeyframesToCurve(*filterCurve, values, compositorTiming.reverse); curve = adoptPtr(filterCurve); break; } - case CSSPropertyWebkitTransform: { + case CSSPropertyTransform: { targetProperty = blink::WebAnimation::TargetPropertyTransform; blink::WebTransformAnimationCurve* transformCurve = blink::Platform::current()->compositorSupport()->createTransformAnimationCurve(); - addKeyframesToCurve(*transformCurve, values, *timingFunction.get()); + addKeyframesToCurve(*transformCurve, values, compositorTiming.reverse); curve = adoptPtr(transformCurve); break; } @@ -524,6 +451,9 @@ void CompositorAnimationsImpl::getAnimationOnCompositor( OwnPtr<blink::WebAnimation> animation = adoptPtr(blink::Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty)); + if (!std::isnan(startTime)) + animation->setStartTime(startTime); + animation->setIterations(compositorTiming.adjustedIterationCount); animation->setTimeOffset(compositorTiming.scaledTimeOffset); animation->setAlternatesDirection(compositorTiming.alternate); diff --git a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.h b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.h index 9a529731fa0..da2cf497de2 100644 --- a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.h +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimations.h @@ -33,7 +33,7 @@ #include "core/animation/AnimationEffect.h" #include "core/animation/Timing.h" -#include "core/platform/animation/TimingFunction.h" +#include "platform/animation/TimingFunction.h" #include "wtf/Vector.h" namespace WebCore { @@ -49,7 +49,6 @@ class CompositorAnimationsTimingFunctionReverser { public: static PassRefPtr<TimingFunction> reverse(const LinearTimingFunction* timefunc); static PassRefPtr<TimingFunction> reverse(const CubicBezierTimingFunction* timefunc); - static PassRefPtr<TimingFunction> reverse(const ChainedTimingFunction* timefunc); static PassRefPtr<TimingFunction> reverse(const TimingFunction* timefunc); }; @@ -61,7 +60,7 @@ public: virtual bool isCandidateForAnimationOnCompositor(const Timing&, const AnimationEffect&); virtual bool canStartAnimationOnCompositor(const Element&); // FIXME: This should return void. We should know ahead of time whether these animations can be started. - virtual bool startAnimationOnCompositor(const Element&, const Timing&, const AnimationEffect&, Vector<int>& startedAnimationIds); + virtual bool startAnimationOnCompositor(const Element&, double startTime, const Timing&, const AnimationEffect&, Vector<int>& startedAnimationIds); virtual void cancelAnimationOnCompositor(const Element&, int id); virtual void pauseAnimationForTestingOnCompositor(const Element&, int id, double pauseTime); diff --git a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsImpl.h b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsImpl.h index 7c32705f23b..d2f06f5b81c 100644 --- a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsImpl.h +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsImpl.h @@ -29,15 +29,13 @@ */ #include "core/animation/AnimationEffect.h" -#include "core/animation/KeyframeAnimationEffect.h" +#include "core/animation/KeyframeEffectModel.h" #include "core/animation/Timing.h" -#include "core/platform/animation/TimingFunction.h" +#include "platform/animation/TimingFunction.h" #include "public/platform/WebAnimation.h" namespace WebCore { -typedef KeyframeAnimationEffect::PropertySpecificKeyframeVector KeyframeVector; - class CompositorAnimationsImpl { private: struct CompositorTiming { @@ -50,9 +48,9 @@ private: static bool convertTimingForCompositor(const Timing&, CompositorTiming& out); - static void getAnimationOnCompositor(const Timing&, const KeyframeAnimationEffect&, Vector<OwnPtr<blink::WebAnimation> >& animations); + static void getAnimationOnCompositor(const Timing&, double startTime, const KeyframeEffectModelBase&, Vector<OwnPtr<blink::WebAnimation> >& animations); - static void addKeyframesToCurve(blink::WebAnimationCurve&, const KeyframeVector&, const TimingFunction&); + static void addKeyframesToCurve(blink::WebAnimationCurve&, const AnimatableValuePropertySpecificKeyframeVector&, bool reverse); friend class CompositorAnimations; friend class AnimationCompositorAnimationsTest; diff --git a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp index db0d9a6c4bd..ad88c352261 100644 --- a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp @@ -38,7 +38,6 @@ #include "core/animation/AnimatableValueTestHelper.h" #include "core/animation/CompositorAnimationsImpl.h" #include "core/animation/CompositorAnimationsTestHelper.h" -#include "core/platform/animation/TimingFunctionTestHelper.h" #include "platform/geometry/IntSize.h" #include "platform/graphics/filters/FilterOperations.h" #include "platform/transforms/TransformOperations.h" @@ -62,7 +61,6 @@ using ::testing::Return; using ::testing::_; class AnimationCompositorAnimationsTest : public AnimationCompositorAnimationsTestBase { - protected: RefPtr<TimingFunction> m_linearTimingFunction; RefPtr<TimingFunction> m_cubicEaseTimingFunction; @@ -71,19 +69,19 @@ protected: Timing m_timing; CompositorAnimationsImpl::CompositorTiming m_compositorTiming; - KeyframeAnimationEffect::KeyframeVector m_keyframeVector2; - RefPtr<KeyframeAnimationEffect> m_keyframeAnimationEffect2; - KeyframeAnimationEffect::KeyframeVector m_keyframeVector5; - RefPtr<KeyframeAnimationEffect> m_keyframeAnimationEffect5; + OwnPtrWillBePersistent<AnimatableValueKeyframeVector> m_keyframeVector2; + RefPtrWillBePersistent<AnimatableValueKeyframeEffectModel> m_keyframeAnimationEffect2; + OwnPtrWillBePersistent<AnimatableValueKeyframeVector> m_keyframeVector5; + RefPtrWillBePersistent<AnimatableValueKeyframeEffectModel> m_keyframeAnimationEffect5; virtual void SetUp() { AnimationCompositorAnimationsTestBase::SetUp(); - m_linearTimingFunction = LinearTimingFunction::create(); + m_linearTimingFunction = LinearTimingFunction::shared(); m_cubicEaseTimingFunction = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); m_cubicCustomTimingFunction = CubicBezierTimingFunction::create(1, 2, 3, 4); - m_stepTimingFunction = StepsTimingFunction::create(1, false); + m_stepTimingFunction = StepsTimingFunction::create(1, StepsTimingFunction::StepAtEnd); m_timing = createCompositableTiming(); m_compositorTiming = CompositorAnimationsImpl::CompositorTiming(); @@ -92,10 +90,10 @@ protected: ASSERT(convertTimingForCompositor(m_timing, m_compositorTiming)); m_keyframeVector2 = createCompositableFloatKeyframeVector(2); - m_keyframeAnimationEffect2 = KeyframeAnimationEffect::create(m_keyframeVector2); + m_keyframeAnimationEffect2 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector2); m_keyframeVector5 = createCompositableFloatKeyframeVector(5); - m_keyframeAnimationEffect5 = KeyframeAnimationEffect::create(m_keyframeVector5); + m_keyframeAnimationEffect5 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector5); } public: @@ -108,19 +106,20 @@ public: { return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(timing, effect); } - void getAnimationOnCompositor(Timing& timing, KeyframeAnimationEffect& effect, Vector<OwnPtr<blink::WebAnimation> >& animations) + void getAnimationOnCompositor(Timing& timing, AnimatableValueKeyframeEffectModel& effect, Vector<OwnPtr<blink::WebAnimation> >& animations) { - return CompositorAnimationsImpl::getAnimationOnCompositor(timing, effect, animations); + return CompositorAnimationsImpl::getAnimationOnCompositor(timing, std::numeric_limits<double>::quiet_NaN(), effect, animations); } - bool isCandidateHelperForSingleKeyframe(Keyframe* frame) + bool duplicateSingleKeyframeAndTestIsCandidateOnResult(AnimatableValueKeyframe* frame) { EXPECT_EQ(frame->offset(), 0); - KeyframeAnimationEffect::KeyframeVector frames; + AnimatableValueKeyframeVector frames; + RefPtrWillBeRawPtr<Keyframe> second = frame->cloneWithOffset(1); + frames.append(frame); - EXPECT_EQ(m_keyframeVector2[1]->offset(), 1.0); - frames.append(m_keyframeVector2[1]); - return isCandidateForAnimationOnCompositor(m_timing, *KeyframeAnimationEffect::create(frames).get()); + frames.append(toAnimatableValueKeyframe(second.get())); + return isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(frames).get()); } // ------------------------------------------------------------------- @@ -132,7 +131,6 @@ public: timing.fillMode = Timing::FillModeNone; timing.iterationStart = 0; timing.iterationCount = 1; - timing.hasIterationDuration = true; timing.iterationDuration = 1.0; timing.playbackRate = 1.0; timing.direction = Timing::PlaybackDirectionNormal; @@ -141,29 +139,30 @@ public: return timing; } - PassRefPtr<Keyframe> createReplaceOpKeyframe(CSSPropertyID id, AnimatableValue* value, double offset = 0) + PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> createReplaceOpKeyframe(CSSPropertyID id, AnimatableValue* value, double offset = 0) { - RefPtr<Keyframe> keyframe = Keyframe::create(); + RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKeyframe::create(); keyframe->setPropertyValue(id, value); keyframe->setComposite(AnimationEffect::CompositeReplace); keyframe->setOffset(offset); + keyframe->setEasing(LinearTimingFunction::shared()); return keyframe; } - PassRefPtr<Keyframe> createDefaultKeyframe(CSSPropertyID id, AnimationEffect::CompositeOperation op, double offset = 0) + PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> createDefaultKeyframe(CSSPropertyID id, AnimationEffect::CompositeOperation op, double offset = 0) { - RefPtr<AnimatableValue> value; - if (id == CSSPropertyWebkitTransform) + RefPtrWillBeRawPtr<AnimatableValue> value = nullptr; + if (id == CSSPropertyTransform) value = AnimatableTransform::create(TransformOperations()); else value = AnimatableDouble::create(10.0); - RefPtr<Keyframe> keyframe = createReplaceOpKeyframe(id, value.get(), offset); + RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = createReplaceOpKeyframe(id, value.get(), offset); keyframe->setComposite(op); return keyframe; } - KeyframeAnimationEffect::KeyframeVector createCompositableFloatKeyframeVector(size_t n) + PassOwnPtrWillBeRawPtr<AnimatableValueKeyframeVector> createCompositableFloatKeyframeVector(size_t n) { Vector<double> values; for (size_t i = 0; i < n; i++) { @@ -172,26 +171,26 @@ public: return createCompositableFloatKeyframeVector(values); } - KeyframeAnimationEffect::KeyframeVector createCompositableFloatKeyframeVector(Vector<double>& values) + PassOwnPtrWillBeRawPtr<AnimatableValueKeyframeVector> createCompositableFloatKeyframeVector(Vector<double>& values) { - KeyframeAnimationEffect::KeyframeVector frames; + OwnPtrWillBeRawPtr<AnimatableValueKeyframeVector> frames = adoptPtrWillBeNoop(new AnimatableValueKeyframeVector); for (size_t i = 0; i < values.size(); i++) { double offset = 1.0 / (values.size() - 1) * i; - RefPtr<AnimatableDouble> value = AnimatableDouble::create(values[i]); - frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, value.get(), offset).get()); + RefPtrWillBeRawPtr<AnimatableDouble> value = AnimatableDouble::create(values[i]); + frames->append(createReplaceOpKeyframe(CSSPropertyOpacity, value.get(), offset).get()); } - return frames; + return frames.release(); } - PassRefPtr<KeyframeAnimationEffect> createKeyframeAnimationEffect(PassRefPtr<Keyframe> prpFrom, PassRefPtr<Keyframe> prpTo, PassRefPtr<Keyframe> prpC = 0, PassRefPtr<Keyframe> prpD = 0) + PassRefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> createKeyframeEffectModel(PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> prpFrom, PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> prpTo, PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> prpC = nullptr, PassRefPtrWillBeRawPtr<AnimatableValueKeyframe> prpD = nullptr) { - RefPtr<Keyframe> from = prpFrom; - RefPtr<Keyframe> to = prpTo; - RefPtr<Keyframe> c = prpC; - RefPtr<Keyframe> d = prpD; + RefPtrWillBeRawPtr<AnimatableValueKeyframe> from = prpFrom; + RefPtrWillBeRawPtr<AnimatableValueKeyframe> to = prpTo; + RefPtrWillBeRawPtr<AnimatableValueKeyframe> c = prpC; + RefPtrWillBeRawPtr<AnimatableValueKeyframe> d = prpD; EXPECT_EQ(from->offset(), 0); - KeyframeAnimationEffect::KeyframeVector frames; + AnimatableValueKeyframeVector frames; frames.append(from); EXPECT_LE(from->offset(), to->offset()); frames.append(to); @@ -207,31 +206,11 @@ public: EXPECT_EQ(to->offset(), 1.0); } if (!HasFatalFailure()) { - return KeyframeAnimationEffect::create(frames); + return AnimatableValueKeyframeEffectModel::create(frames); } - return PassRefPtr<KeyframeAnimationEffect>(); - } - -}; - -class CustomFilterOperationMock : public FilterOperation { -public: - virtual bool operator==(const FilterOperation&) const OVERRIDE FINAL { - ASSERT_NOT_REACHED(); - return false; - } - - MOCK_CONST_METHOD2(blend, PassRefPtr<FilterOperation>(const FilterOperation*, double)); - - static PassRefPtr<CustomFilterOperationMock> create() - { - return adoptRef(new CustomFilterOperationMock()); + return nullptr; } - CustomFilterOperationMock() - : FilterOperation(FilterOperation::CUSTOM) - { - } }; // ----------------------------------------------------------------------- @@ -239,88 +218,57 @@ public: TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeMultipleCSSProperties) { - RefPtr<Keyframe> keyframeGoodMultiple = createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace); - keyframeGoodMultiple->setPropertyValue(CSSPropertyWebkitTransform, AnimatableTransform::create(TransformOperations()).get()); - EXPECT_TRUE(isCandidateHelperForSingleKeyframe(keyframeGoodMultiple.get())); - - RefPtr<Keyframe> keyframeBadMultipleOp = createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeAdd); - keyframeBadMultipleOp->setPropertyValue(CSSPropertyWebkitTransform, AnimatableDouble::create(10.0).get()); - EXPECT_FALSE(isCandidateHelperForSingleKeyframe(keyframeBadMultipleOp.get())); - - // Check both an unsupported property which hashes before and after the - // supported property. - typedef DefaultHash<CSSPropertyID>::Hash HashFunctions; - - RefPtr<Keyframe> keyframeBadMultiple1ID = createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace); - keyframeBadMultiple1ID->setPropertyValue(CSSPropertyOpacity, AnimatableDouble::create(10.0).get()); - EXPECT_FALSE(isCandidateHelperForSingleKeyframe(keyframeBadMultiple1ID.get())); - EXPECT_LT(HashFunctions::hash(CSSPropertyColor), HashFunctions::hash(CSSPropertyOpacity)); - - RefPtr<Keyframe> keyframeBadMultiple2ID = createDefaultKeyframe(CSSPropertyWebkitTransform, AnimationEffect::CompositeReplace); - keyframeBadMultiple2ID->setPropertyValue(CSSPropertyWidth, AnimatableDouble::create(10.0).get()); - EXPECT_FALSE(isCandidateHelperForSingleKeyframe(keyframeBadMultiple2ID.get())); - EXPECT_GT(HashFunctions::hash(CSSPropertyWebkitTransform), HashFunctions::hash(CSSPropertyWidth)); + RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframeGoodMultiple = createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace); + keyframeGoodMultiple->setPropertyValue(CSSPropertyTransform, AnimatableTransform::create(TransformOperations()).get()); + EXPECT_TRUE(duplicateSingleKeyframeAndTestIsCandidateOnResult(keyframeGoodMultiple.get())); + + RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframeBadMultipleID = createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace); + keyframeBadMultipleID->setPropertyValue(CSSPropertyOpacity, AnimatableDouble::create(10.0).get()); + EXPECT_FALSE(duplicateSingleKeyframeAndTestIsCandidateOnResult(keyframeBadMultipleID.get())); } TEST_F(AnimationCompositorAnimationsTest, isNotCandidateForCompositorAnimationTransformDependsOnBoxSize) { TransformOperations ops; ops.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(2, WebCore::Fixed), TransformOperation::TranslateX)); - RefPtr<Keyframe> goodKeyframe = createReplaceOpKeyframe(CSSPropertyWebkitTransform, AnimatableTransform::create(ops).get()); - EXPECT_TRUE(isCandidateHelperForSingleKeyframe(goodKeyframe.get())); + RefPtrWillBeRawPtr<AnimatableValueKeyframe> goodKeyframe = createReplaceOpKeyframe(CSSPropertyTransform, AnimatableTransform::create(ops).get()); + EXPECT_TRUE(duplicateSingleKeyframeAndTestIsCandidateOnResult(goodKeyframe.get())); ops.operations().append(TranslateTransformOperation::create(Length(50, WebCore::Percent), Length(2, WebCore::Fixed), TransformOperation::TranslateX)); - RefPtr<Keyframe> badKeyframe = createReplaceOpKeyframe(CSSPropertyWebkitTransform, AnimatableTransform::create(ops).get()); - EXPECT_FALSE(isCandidateHelperForSingleKeyframe(badKeyframe.get())); + RefPtrWillBeRawPtr<AnimatableValueKeyframe> badKeyframe = createReplaceOpKeyframe(CSSPropertyTransform, AnimatableTransform::create(ops).get()); + EXPECT_FALSE(duplicateSingleKeyframeAndTestIsCandidateOnResult(badKeyframe.get())); TransformOperations ops2; Length calcLength = Length(100, WebCore::Percent).blend(Length(100, WebCore::Fixed), 0.5, WebCore::ValueRangeAll); ops2.operations().append(TranslateTransformOperation::create(calcLength, Length(0, WebCore::Fixed), TransformOperation::TranslateX)); - RefPtr<Keyframe> badKeyframe2 = createReplaceOpKeyframe(CSSPropertyWebkitTransform, AnimatableTransform::create(ops2).get()); - EXPECT_FALSE(isCandidateHelperForSingleKeyframe(badKeyframe2.get())); -} - -TEST_F(AnimationCompositorAnimationsTest, isNotCandidateForCompositorAnimationCustomFilter) -{ - FilterOperations ops; - ops.operations().append(BasicColorMatrixFilterOperation::create(0.5, FilterOperation::SATURATE)); - RefPtr<Keyframe> goodKeyframe = createReplaceOpKeyframe(CSSPropertyWebkitFilter, AnimatableFilterOperations::create(ops).get()); - EXPECT_TRUE(isCandidateHelperForSingleKeyframe(goodKeyframe.get())); - - ops.operations().append(CustomFilterOperationMock::create()); - RefPtr<Keyframe> badKeyframe = createReplaceOpKeyframe(CSSPropertyFilter, AnimatableFilterOperations::create(ops).get()); - EXPECT_FALSE(isCandidateHelperForSingleKeyframe(badKeyframe.get())); + RefPtrWillBeRawPtr<AnimatableValueKeyframe> badKeyframe2 = createReplaceOpKeyframe(CSSPropertyTransform, AnimatableTransform::create(ops2).get()); + EXPECT_FALSE(duplicateSingleKeyframeAndTestIsCandidateOnResult(badKeyframe2.get())); } -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeEffectMultipleFramesOkay) +TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeEffectModelMultipleFramesOkay) { - KeyframeAnimationEffect::KeyframeVector framesSame; + AnimatableValueKeyframeVector framesSame; framesSame.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); framesSame.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 1.0).get()); - EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *KeyframeAnimationEffect::create(framesSame).get())); + EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesSame).get())); - KeyframeAnimationEffect::KeyframeVector framesMixed; + AnimatableValueKeyframeVector framesMixed; framesMixed.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); - framesMixed.append(createDefaultKeyframe(CSSPropertyWebkitTransform, AnimationEffect::CompositeReplace, 1.0).get()); - EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *KeyframeAnimationEffect::create(framesMixed).get())); + framesMixed.append(createDefaultKeyframe(CSSPropertyTransform, AnimationEffect::CompositeReplace, 1.0).get()); + EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesMixed).get())); } -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeEffectMultipleFramesNotOkay) +TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeEffectModel) { - KeyframeAnimationEffect::KeyframeVector framesSame; + AnimatableValueKeyframeVector framesSame; framesSame.append(createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace, 0.0).get()); framesSame.append(createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace, 1.0).get()); - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *KeyframeAnimationEffect::create(framesSame).get())); + EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesSame).get())); - KeyframeAnimationEffect::KeyframeVector framesMixedProperties; + AnimatableValueKeyframeVector framesMixedProperties; framesMixedProperties.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); framesMixedProperties.append(createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace, 1.0).get()); - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *KeyframeAnimationEffect::create(framesMixedProperties).get())); - - KeyframeAnimationEffect::KeyframeVector framesMixedOps; - framesMixedOps.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); - framesMixedOps.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeAdd, 1.0).get()); - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *KeyframeAnimationEffect::create(framesMixedOps).get())); + EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesMixedProperties).get())); } TEST_F(AnimationCompositorAnimationsTest, ConvertTimingForCompositorStartDelay) @@ -477,13 +425,12 @@ TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTim TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionCubic) { - // Cubic bezier are okay if we only have two keyframes m_timing.timingFunction = m_cubicEaseTimingFunction; - EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); + EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); m_timing.timingFunction = m_cubicCustomTimingFunction; - EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); + EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); } @@ -494,191 +441,65 @@ TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTim EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); } -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionChainedEmpty) -{ - RefPtr<ChainedTimingFunction> chainedEmpty = ChainedTimingFunction::create(); - m_timing.timingFunction = chainedEmpty; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); -} - TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionChainedLinear) { - RefPtr<ChainedTimingFunction> chainedLinearSingle = ChainedTimingFunction::create(); - chainedLinearSingle->appendSegment(1.0, m_linearTimingFunction.get()); - m_timing.timingFunction = chainedLinearSingle; EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - - RefPtr<ChainedTimingFunction> chainedLinearMultiple = ChainedTimingFunction::create(); - chainedLinearMultiple->appendSegment(0.25, m_linearTimingFunction.get()); - chainedLinearMultiple->appendSegment(0.5, m_linearTimingFunction.get()); - chainedLinearMultiple->appendSegment(0.75, m_linearTimingFunction.get()); - chainedLinearMultiple->appendSegment(1.0, m_linearTimingFunction.get()); - m_timing.timingFunction = chainedLinearMultiple; EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - - // FIXME: Technically a chained timing function of linear functions don't - // have to be aligned to keyframes. We don't support that currently as - // nothing generates that yet. } TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionChainedCubicMatchingOffsets) { - RefPtr<ChainedTimingFunction> chainedSingleAGood = ChainedTimingFunction::create(); - chainedSingleAGood->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chainedSingleAGood; + (*m_keyframeVector2)[0]->setEasing(m_cubicEaseTimingFunction.get()); + m_keyframeAnimationEffect2 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector2); EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - RefPtr<ChainedTimingFunction> chainedSingleBGood = ChainedTimingFunction::create(); - chainedSingleBGood->appendSegment(1.0, m_cubicCustomTimingFunction.get()); - m_timing.timingFunction = chainedSingleBGood; + (*m_keyframeVector2)[0]->setEasing(m_cubicCustomTimingFunction.get()); + m_keyframeAnimationEffect2 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector2); EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - RefPtr<ChainedTimingFunction> chainedMultipleGood = ChainedTimingFunction::create(); - chainedMultipleGood->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chainedMultipleGood->appendSegment(0.5, m_cubicCustomTimingFunction.get()); - chainedMultipleGood->appendSegment(0.75, m_cubicCustomTimingFunction.get()); - chainedMultipleGood->appendSegment(1.0, m_cubicCustomTimingFunction.get()); - m_timing.timingFunction = chainedMultipleGood; + (*m_keyframeVector5)[0]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[1]->setEasing(m_cubicCustomTimingFunction.get()); + (*m_keyframeVector5)[2]->setEasing(m_cubicCustomTimingFunction.get()); + (*m_keyframeVector5)[3]->setEasing(m_cubicCustomTimingFunction.get()); + m_keyframeAnimationEffect5 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector5); EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); } -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionChainedCubicNonMatchingOffsets) -{ - RefPtr<ChainedTimingFunction> chained0 = ChainedTimingFunction::create(); - chained0->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained0; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - - RefPtr<ChainedTimingFunction> chained1 = ChainedTimingFunction::create(); - chained1->appendSegment(0.24, m_cubicEaseTimingFunction.get()); - chained1->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chained1->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained1->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained1; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - - RefPtr<ChainedTimingFunction> chained2 = ChainedTimingFunction::create(); - chained2->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(0.51, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained2; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - - RefPtr<ChainedTimingFunction> chained3 = ChainedTimingFunction::create(); - chained3->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chained3->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chained3->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained3->appendSegment(0.8, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained3; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - - RefPtr<ChainedTimingFunction> chained4 = ChainedTimingFunction::create(); - chained4->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chained4->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chained4->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained4->appendSegment(1.1, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained4; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); -} - -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionMissingFrames) -{ - // Missing first - RefPtr<ChainedTimingFunction> chained1 = ChainedTimingFunction::create(); - chained1->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chained1->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained1->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained1; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - - // Missing middle - RefPtr<ChainedTimingFunction> chained2 = ChainedTimingFunction::create(); - chained2->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained2; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - - // Missing last - RefPtr<ChainedTimingFunction> chained3 = ChainedTimingFunction::create(); - chained3->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chained3->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chained3->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained3; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); -} - -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionToManyFrames) -{ - RefPtr<ChainedTimingFunction> chained1 = ChainedTimingFunction::create(); - chained1->appendSegment(0.1, m_cubicEaseTimingFunction.get()); - chained1->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained1; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - - RefPtr<ChainedTimingFunction> chained2 = ChainedTimingFunction::create(); - chained2->appendSegment(0.1, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chained2->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chained2; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); -} - TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionMixedGood) { - RefPtr<ChainedTimingFunction> chainedMixed = ChainedTimingFunction::create(); - chainedMixed->appendSegment(0.25, m_linearTimingFunction.get()); - chainedMixed->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chainedMixed->appendSegment(0.75, m_cubicEaseTimingFunction.get()); - chainedMixed->appendSegment(1.0, m_linearTimingFunction.get()); - m_timing.timingFunction = chainedMixed; + (*m_keyframeVector5)[0]->setEasing(m_linearTimingFunction.get()); + (*m_keyframeVector5)[1]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[2]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[3]->setEasing(m_linearTimingFunction.get()); + m_keyframeAnimationEffect5 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector5); EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); } TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionWithStepNotOkay) { - RefPtr<ChainedTimingFunction> chainedStepSingle = ChainedTimingFunction::create(); - chainedStepSingle->appendSegment(1.0, m_stepTimingFunction.get()); - m_timing.timingFunction = chainedStepSingle; + (*m_keyframeVector2)[0]->setEasing(m_stepTimingFunction.get()); + m_keyframeAnimationEffect2 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector2); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect2.get())); - RefPtr<ChainedTimingFunction> chainedStepMixedA = ChainedTimingFunction::create(); - chainedStepMixedA->appendSegment(0.25, m_stepTimingFunction.get()); - chainedStepMixedA->appendSegment(0.5, m_linearTimingFunction.get()); - chainedStepMixedA->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chainedStepMixedA; + (*m_keyframeVector5)[0]->setEasing(m_stepTimingFunction.get()); + (*m_keyframeVector5)[1]->setEasing(m_linearTimingFunction.get()); + (*m_keyframeVector5)[2]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[3]->setEasing(m_linearTimingFunction.get()); + m_keyframeAnimationEffect5 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector5); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - RefPtr<ChainedTimingFunction> chainedStepMixedB = ChainedTimingFunction::create(); - chainedStepMixedB->appendSegment(0.25, m_linearTimingFunction.get()); - chainedStepMixedB->appendSegment(0.5, m_stepTimingFunction.get()); - chainedStepMixedB->appendSegment(1.0, m_cubicEaseTimingFunction.get()); - m_timing.timingFunction = chainedStepMixedB; + (*m_keyframeVector5)[0]->setEasing(m_linearTimingFunction.get()); + (*m_keyframeVector5)[1]->setEasing(m_stepTimingFunction.get()); + (*m_keyframeVector5)[2]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[3]->setEasing(m_linearTimingFunction.get()); + m_keyframeAnimationEffect5 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector5); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); - RefPtr<ChainedTimingFunction> chainedStepMixedC = ChainedTimingFunction::create(); - chainedStepMixedC->appendSegment(0.25, m_linearTimingFunction.get()); - chainedStepMixedC->appendSegment(0.5, m_cubicEaseTimingFunction.get()); - chainedStepMixedC->appendSegment(1.0, m_stepTimingFunction.get()); - m_timing.timingFunction = chainedStepMixedC; - EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); -} - -TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorTimingFunctionNestedNotOkay) -{ - RefPtr<ChainedTimingFunction> chainedChild = ChainedTimingFunction::create(); - chainedChild->appendSegment(1.0, m_linearTimingFunction.get()); - - RefPtr<ChainedTimingFunction> chainedParent = ChainedTimingFunction::create(); - chainedParent->appendSegment(0.25, m_linearTimingFunction.get()); - chainedParent->appendSegment(0.5, chainedChild.get()); - chainedParent->appendSegment(0.75, m_linearTimingFunction.get()); - chainedParent->appendSegment(1.0, m_linearTimingFunction.get()); - m_timing.timingFunction = chainedParent; + (*m_keyframeVector5)[0]->setEasing(m_linearTimingFunction.get()); + (*m_keyframeVector5)[1]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[2]->setEasing(m_cubicEaseTimingFunction.get()); + (*m_keyframeVector5)[3]->setEasing(m_stepTimingFunction.get()); + m_keyframeAnimationEffect5 = AnimatableValueKeyframeEffectModel::create(*m_keyframeVector5); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *m_keyframeAnimationEffect5.get())); } @@ -686,35 +507,27 @@ TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositor) { Timing linearTiming(createCompositableTiming()); - RefPtr<TimingFunction> cubicTimingFunc = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - Timing cubicTiming(createCompositableTiming()); - cubicTiming.timingFunction = cubicTimingFunc; - - RefPtr<ChainedTimingFunction> chainedTimingFunc = ChainedTimingFunction::create(); - chainedTimingFunc->appendSegment(0.5, m_linearTimingFunction.get()); - chainedTimingFunc->appendSegment(1.0, cubicTimingFunc.get()); - Timing chainedTiming(createCompositableTiming()); - chainedTiming.timingFunction = chainedTimingFunc; - - KeyframeAnimationEffect::KeyframeVector basicFramesVector; + AnimatableValueKeyframeVector basicFramesVector; basicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); basicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 1.0).get()); - RefPtr<KeyframeAnimationEffect> basicFrames = KeyframeAnimationEffect::create(basicFramesVector).get(); - - EXPECT_TRUE(isCandidateForAnimationOnCompositor(linearTiming, *basicFrames.get())); - EXPECT_TRUE(isCandidateForAnimationOnCompositor(cubicTiming, *basicFrames.get())); - // number of timing function and keyframes don't match - EXPECT_FALSE(isCandidateForAnimationOnCompositor(chainedTiming, *basicFrames.get())); - KeyframeAnimationEffect::KeyframeVector nonBasicFramesVector; + AnimatableValueKeyframeVector nonBasicFramesVector; nonBasicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); nonBasicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.5).get()); nonBasicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 1.0).get()); - RefPtr<KeyframeAnimationEffect> nonBasicFrames = KeyframeAnimationEffect::create(nonBasicFramesVector).get(); + basicFramesVector[0]->setEasing(m_linearTimingFunction.get()); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> basicFrames = AnimatableValueKeyframeEffectModel::create(basicFramesVector).get(); + EXPECT_TRUE(isCandidateForAnimationOnCompositor(linearTiming, *basicFrames.get())); + + basicFramesVector[0]->setEasing(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); + basicFrames = AnimatableValueKeyframeEffectModel::create(basicFramesVector).get(); + EXPECT_TRUE(isCandidateForAnimationOnCompositor(linearTiming, *basicFrames.get())); + + nonBasicFramesVector[0]->setEasing(m_linearTimingFunction.get()); + nonBasicFramesVector[1]->setEasing(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> nonBasicFrames = AnimatableValueKeyframeEffectModel::create(nonBasicFramesVector).get(); EXPECT_TRUE(CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(linearTiming, *nonBasicFrames.get())); - EXPECT_FALSE(CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(cubicTiming, *nonBasicFrames.get())); - EXPECT_TRUE(CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(chainedTiming, *nonBasicFrames.get())); } // ----------------------------------------------------------------------- @@ -723,7 +536,7 @@ TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositor) TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimation) { // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = createKeyframeEffectModel( createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); // -- @@ -768,7 +581,7 @@ TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimation) TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimationDuration) { // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = createKeyframeEffectModel( createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); @@ -815,7 +628,7 @@ TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimationDuration) TEST_F(AnimationCompositorAnimationsTest, createMultipleKeyframeOpacityAnimationLinear) { // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = createKeyframeEffectModel( createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(-1.0).get(), 0.25), createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(20.0).get(), 0.5), @@ -868,7 +681,7 @@ TEST_F(AnimationCompositorAnimationsTest, createMultipleKeyframeOpacityAnimation TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimationStartDelay) { // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = createKeyframeEffectModel( createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); @@ -917,18 +730,17 @@ TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimationStartDelay TEST_F(AnimationCompositorAnimationsTest, createMultipleKeyframeOpacityAnimationChained) { // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(-1.0).get(), 0.25), - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(20.0).get(), 0.5), - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); - - RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction::create(); - chainedTimingFunction->appendSegment(0.25, m_cubicEaseTimingFunction.get()); - chainedTimingFunction->appendSegment(0.5, m_linearTimingFunction.get()); - chainedTimingFunction->appendSegment(1.0, m_cubicCustomTimingFunction.get()); - - m_timing.timingFunction = chainedTimingFunction; + AnimatableValueKeyframeVector frames; + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0)); + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(-1.0).get(), 0.25)); + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(20.0).get(), 0.5)); + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); + frames[0]->setEasing(m_cubicEaseTimingFunction.get()); + frames[1]->setEasing(m_linearTimingFunction.get()); + frames[2]->setEasing(m_cubicCustomTimingFunction.get()); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(frames); + + m_timing.timingFunction = m_linearTimingFunction.get(); m_timing.iterationDuration = 2.0; m_timing.iterationCount = 10; m_timing.direction = Timing::PlaybackDirectionAlternate; @@ -976,20 +788,20 @@ TEST_F(AnimationCompositorAnimationsTest, createMultipleKeyframeOpacityAnimation TEST_F(AnimationCompositorAnimationsTest, createReversedOpacityAnimation) { - // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(-1.0).get(), 0.25), - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(20.0).get(), 0.5), - createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); - RefPtr<TimingFunction> cubicEasyFlipTimingFunction = CubicBezierTimingFunction::create(0.0, 0.0, 0.0, 1.0); - RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction::create(); - chainedTimingFunction->appendSegment(0.25, CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); - chainedTimingFunction->appendSegment(0.5, m_linearTimingFunction.get()); - chainedTimingFunction->appendSegment(1.0, cubicEasyFlipTimingFunction.get()); - m_timing.timingFunction = chainedTimingFunction; + // Animation to convert + AnimatableValueKeyframeVector frames; + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0)); + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(-1.0).get(), 0.25)); + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(20.0).get(), 0.5)); + frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); + frames[0]->setEasing(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); + frames[1]->setEasing(m_linearTimingFunction.get()); + frames[2]->setEasing(cubicEasyFlipTimingFunction.get()); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(frames); + + m_timing.timingFunction = m_linearTimingFunction.get(); m_timing.iterationCount = 10; m_timing.direction = Timing::PlaybackDirectionAlternateReverse; // -- @@ -1037,7 +849,7 @@ TEST_F(AnimationCompositorAnimationsTest, createReversedOpacityAnimation) TEST_F(AnimationCompositorAnimationsTest, createReversedOpacityAnimationNegativeStartDelay) { // Animation to convert - RefPtr<KeyframeAnimationEffect> effect = createKeyframeAnimationEffect( + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = createKeyframeEffectModel( createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0), createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); diff --git a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTestHelper.h b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTestHelper.h index 732d34662e5..bb2761f75fc 100644 --- a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTestHelper.h +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTestHelper.h @@ -131,7 +131,7 @@ private: virtual void cryptographicallyRandomValues(unsigned char* buffer, size_t length) { ASSERT_NOT_REACHED(); } private: WebCompositorSupportMock** m_compositor; - blink::WebCompositorSupport* compositorSupport() OVERRIDE { return *m_compositor; } + virtual blink::WebCompositorSupport* compositorSupport() OVERRIDE { return *m_compositor; } }; WebCompositorSupportMock* m_mockCompositor; diff --git a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTimingFunctionReverserTest.cpp b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTimingFunctionReverserTest.cpp index 550b7f562c2..a62173efe62 100644 --- a/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTimingFunctionReverserTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorAnimationsTimingFunctionReverserTest.cpp @@ -32,8 +32,6 @@ #include "core/animation/CompositorAnimations.h" -#include "core/platform/animation/TimingFunctionTestHelper.h" - #include "wtf/PassRefPtr.h" #include "wtf/RefPtr.h" @@ -60,7 +58,7 @@ public: TEST_F(AnimationCompositorAnimationsTimingFunctionReverserTest, LinearReverse) { - RefPtr<TimingFunction> linearTiming = LinearTimingFunction::create(); + RefPtr<TimingFunction> linearTiming = LinearTimingFunction::shared(); EXPECT_REFV_EQ(linearTiming, reverse(linearTiming)); } @@ -84,24 +82,4 @@ TEST_F(AnimationCompositorAnimationsTimingFunctionReverserTest, CubicReverse) EXPECT_REFV_EQ(cubicEaseTimingReversed, reverse(cubicEaseTiming)); } -TEST_F(AnimationCompositorAnimationsTimingFunctionReverserTest, ChainedReverse) -{ - RefPtr<TimingFunction> linearTiming = LinearTimingFunction::create(); - RefPtr<ChainedTimingFunction> chainedLinearSingle = ChainedTimingFunction::create(); - chainedLinearSingle->appendSegment(1.0, linearTiming.get()); - EXPECT_REFV_EQ(chainedLinearSingle, reverse(chainedLinearSingle)); - - RefPtr<TimingFunction> cubicEaseInTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - RefPtr<TimingFunction> cubicEaseOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); - - RefPtr<ChainedTimingFunction> chainedMixed = ChainedTimingFunction::create(); - chainedMixed->appendSegment(0.75, chainedLinearSingle.get()); - chainedMixed->appendSegment(1.0, cubicEaseInTiming.get()); - - RefPtr<ChainedTimingFunction> chainedMixedReversed = ChainedTimingFunction::create(); - chainedMixedReversed->appendSegment(0.25, cubicEaseOutTiming.get()); - chainedMixedReversed->appendSegment(1.0, chainedLinearSingle.get()); - EXPECT_REFV_EQ(chainedMixedReversed, reverse(chainedMixed)); -} - } // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSPendingAnimations.cpp b/chromium/third_party/WebKit/Source/core/animation/CompositorPendingAnimations.cpp index 3a4ec03d29e..803e98c9953 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSPendingAnimations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorPendingAnimations.cpp @@ -29,45 +29,53 @@ */ #include "config.h" -#include "core/animation/css/CSSPendingAnimations.h" +#include "core/animation/CompositorPendingAnimations.h" #include "core/animation/Animation.h" -#include "core/animation/DocumentTimeline.h" +#include "core/animation/AnimationTimeline.h" #include "core/frame/FrameView.h" +#include "core/page/Page.h" +#include "core/rendering/RenderLayer.h" namespace WebCore { -void CSSPendingAnimations::add(Player* player) +void CompositorPendingAnimations::add(AnimationPlayer* player) { - ASSERT(player->source()->isAnimation()); - // The actual start time is either this value, or the time that - // this animation, or an animation that it is synchronized with - // is started on the compositor. - const double defaultStartTime = player->timeline().currentTime(); - m_pending.append(std::make_pair(player, defaultStartTime)); + Page* page = player->timeline()->document()->page(); + bool visible = page && page->visibilityState() == PageVisibilityStateVisible; + if (!player->hasStartTime() && !visible) + player->setStartTimeInternal(player->timeline()->currentTimeInternal(), true); + + m_pending.append(player); } -bool CSSPendingAnimations::startPendingAnimations() +bool CompositorPendingAnimations::startPendingAnimations() { - bool startedOnCompositor = false; + bool startedSynchronizedOnCompositor = false; for (size_t i = 0; i < m_pending.size(); ++i) { - if (m_pending[i].first->maybeStartAnimationOnCompositor()) - startedOnCompositor = true; + if (!m_pending[i]->hasActiveAnimationsOnCompositor() && m_pending[i]->maybeStartAnimationOnCompositor() && !m_pending[i]->hasStartTime()) + startedSynchronizedOnCompositor = true; } - // If any animations were started on the compositor, all remaining - // need to wait for a synchronized start time. Otherwise they may - // start immediately. - if (startedOnCompositor) { - for (size_t i = 0; i < m_pending.size(); ++i) - m_waitingForCompositorAnimationStart.append(m_pending[i].first); + // If any synchronized animations were started on the compositor, all + // remaning synchronized animations need to wait for the synchronized + // start time. Otherwise they may start immediately. + if (startedSynchronizedOnCompositor) { + for (size_t i = 0; i < m_pending.size(); ++i) { + if (!m_pending[i]->hasStartTime()) { + m_waitingForCompositorAnimationStart.append(m_pending[i]); + } + } } else { - for (size_t i = 0; i < m_pending.size(); ++i) - m_pending[i].first->setStartTime(m_pending[i].second); + for (size_t i = 0; i < m_pending.size(); ++i) { + if (!m_pending[i]->hasStartTime()) { + m_pending[i]->setStartTimeInternal(m_pending[i]->timeline()->currentTimeInternal(), true); + } + } } m_pending.clear(); - if (startedOnCompositor || m_waitingForCompositorAnimationStart.isEmpty()) + if (startedSynchronizedOnCompositor || m_waitingForCompositorAnimationStart.isEmpty()) return !m_waitingForCompositorAnimationStart.isEmpty(); // Check if we're still waiting for any compositor animations to start. @@ -81,14 +89,20 @@ bool CSSPendingAnimations::startPendingAnimations() return false; } -void CSSPendingAnimations::notifyCompositorAnimationStarted(double monotonicAnimationStartTime) +void CompositorPendingAnimations::notifyCompositorAnimationStarted(double monotonicAnimationStartTime) { for (size_t i = 0; i < m_waitingForCompositorAnimationStart.size(); ++i) { - Player* player = m_waitingForCompositorAnimationStart[i].get(); - player->setStartTime(monotonicAnimationStartTime - player->timeline().zeroTime()); + AnimationPlayer* player = m_waitingForCompositorAnimationStart[i].get(); + player->setStartTimeInternal(monotonicAnimationStartTime - player->timeline()->zeroTime(), true); } m_waitingForCompositorAnimationStart.clear(); } +void CompositorPendingAnimations::trace(Visitor* visitor) +{ + visitor->trace(m_pending); + visitor->trace(m_waitingForCompositorAnimationStart); +} + } // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSPendingAnimations.h b/chromium/third_party/WebKit/Source/core/animation/CompositorPendingAnimations.h index 1bb3c28d4c0..179c255a917 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSPendingAnimations.h +++ b/chromium/third_party/WebKit/Source/core/animation/CompositorPendingAnimations.h @@ -28,27 +28,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CSSPendingAnimations_h -#define CSSPendingAnimations_h +#ifndef CompositorPendingAnimations_h +#define CompositorPendingAnimations_h -#include "core/animation/Player.h" +#include "platform/heap/Handle.h" #include "wtf/Vector.h" namespace WebCore { -// Used to synchronize the start of main-thread animations with compositor -// animations when both classes of CSS Animations are triggered by the same recalc -class CSSPendingAnimations FINAL { +class AnimationPlayer; + +// Manages the starting of pending animations on the compositor following a +// compositing update. +// For CSS Animations, used to synchronize the start of main-thread animations +// with compositor animations when both classes of CSS Animations are triggered +// by the same recalc +class CompositorPendingAnimations FINAL { + DISALLOW_ALLOCATION(); public: - void add(Player*); + void add(AnimationPlayer*); // Returns whether we are waiting for an animation to start and should // service again on the next frame. bool startPendingAnimations(); void notifyCompositorAnimationStarted(double monotonicAnimationStartTime); + void trace(Visitor*); + private: - Vector<std::pair<RefPtr<Player>, double> > m_pending; - Vector<RefPtr<Player> > m_waitingForCompositorAnimationStart; + WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> > m_pending; + WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> > m_waitingForCompositorAnimationStart; }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentAnimation.h b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimation.h new file mode 100644 index 00000000000..f1f57265ec0 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimation.h @@ -0,0 +1,19 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DocumentAnimation_h +#define DocumentAnimation_h + +#include "core/dom/Document.h" + +namespace WebCore { + +class DocumentAnimation { +public: + static AnimationTimeline* timeline(Document& document) { return &document.timeline(); } +}; + +} // namespace WebCore + +#endif // DocumentAnimation_h diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentAnimation.idl b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimation.idl new file mode 100644 index 00000000000..d2c2861c8b1 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimation.idl @@ -0,0 +1,9 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + RuntimeEnabled=WebAnimationsAPI, +] partial interface Document { + readonly attribute AnimationTimeline timeline; +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.cpp b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.cpp index 34ea5ca4546..df35c02d312 100644 --- a/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.cpp @@ -31,78 +31,66 @@ #include "config.h" #include "core/animation/DocumentAnimations.h" -#include "core/animation/ActiveAnimations.h" #include "core/animation/AnimationClock.h" -#include "core/animation/DocumentTimeline.h" +#include "core/animation/AnimationTimeline.h" #include "core/dom/Document.h" #include "core/dom/Element.h" #include "core/dom/Node.h" -#include "core/frame/Frame.h" +#include "core/dom/NodeRenderStyle.h" #include "core/frame/FrameView.h" -#include "core/rendering/RenderLayerCompositor.h" +#include "core/frame/LocalFrame.h" #include "core/rendering/RenderView.h" +#include "core/rendering/compositing/RenderLayerCompositor.h" namespace WebCore { namespace { -void updateAnimationTiming(Document& document, double monotonicAnimationStartTime) +void updateAnimationTiming(Document& document, TimingUpdateReason reason) { - document.animationClock().updateTime(monotonicAnimationStartTime); - bool didTriggerStyleRecalc = document.timeline()->serviceAnimations(); - didTriggerStyleRecalc |= document.transitionTimeline()->serviceAnimations(); - if (!didTriggerStyleRecalc) - document.animationClock().unfreeze(); + document.timeline().serviceAnimations(reason); } -void dispatchAnimationEvents(Document& document) -{ - document.timeline()->dispatchEvents(); - document.transitionTimeline()->dispatchEvents(); -} +} // namespace -void dispatchAnimationEventsAsync(Document& document) +void DocumentAnimations::updateAnimationTimingForAnimationFrame(Document& document, double monotonicAnimationStartTime) { - document.timeline()->dispatchEventsAsync(); - document.transitionTimeline()->dispatchEventsAsync(); + document.animationClock().updateTime(monotonicAnimationStartTime); + updateAnimationTiming(document, TimingUpdateForAnimationFrame); } -} // namespace - -void DocumentAnimations::serviceOnAnimationFrame(Document& document, double monotonicAnimationStartTime) +void DocumentAnimations::updateOutdatedAnimationPlayersIfNeeded(Document& document) { - if (!RuntimeEnabledFeatures::webAnimationsCSSEnabled()) - return; - - updateAnimationTiming(document, monotonicAnimationStartTime); - dispatchAnimationEvents(document); + if (needsOutdatedAnimationPlayerUpdate(document)) + updateAnimationTiming(document, TimingUpdateOnDemand); } -void DocumentAnimations::serviceBeforeGetComputedStyle(Node& node, CSSPropertyID property) +void DocumentAnimations::updateAnimationTimingForGetComputedStyle(Node& node, CSSPropertyID property) { - if (!RuntimeEnabledFeatures::webAnimationsCSSEnabled()) + if (!node.isElementNode()) return; - - if (node.isElementNode()) { - const Element& element = toElement(node); - if (const ActiveAnimations* activeAnimations = element.activeAnimations()) { - if (activeAnimations->hasActiveAnimationsOnCompositor(property)) - updateAnimationTiming(element.document(), monotonicallyIncreasingTime()); + const Element& element = toElement(node); + if (RenderStyle* style = element.renderStyle()) { + if ((property == CSSPropertyOpacity && style->isRunningOpacityAnimationOnCompositor()) + || ((property == CSSPropertyTransform || property == CSSPropertyWebkitTransform) && style->isRunningTransformAnimationOnCompositor()) + || (property == CSSPropertyWebkitFilter && style->isRunningFilterAnimationOnCompositor())) { + updateAnimationTiming(element.document(), TimingUpdateOnDemand); } } - } -void DocumentAnimations::serviceAfterStyleRecalc(Document& document) +bool DocumentAnimations::needsOutdatedAnimationPlayerUpdate(const Document& document) { - if (!RuntimeEnabledFeatures::webAnimationsCSSEnabled()) - return; + return document.timeline().hasOutdatedAnimationPlayer(); +} - if (document.cssPendingAnimations().startPendingAnimations() && document.view()) +void DocumentAnimations::startPendingAnimations(Document& document) +{ + ASSERT(document.lifecycle().state() == DocumentLifecycle::CompositingClean); + if (document.compositorPendingAnimations().startPendingAnimations()) { + ASSERT(document.view()); document.view()->scheduleAnimation(); - - document.animationClock().unfreeze(); - dispatchAnimationEventsAsync(document); + } } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.h b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.h index b952c532ec1..f92f9942b49 100644 --- a/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.h +++ b/chromium/third_party/WebKit/Source/core/animation/DocumentAnimations.h @@ -31,7 +31,7 @@ #ifndef DocumentAnimations_h #define DocumentAnimations_h -#include "CSSPropertyNames.h" +#include "core/CSSPropertyNames.h" namespace WebCore { @@ -39,11 +39,13 @@ class Document; class FrameView; class Node; -class DocumentAnimations { +class DocumentAnimations { public: - static void serviceOnAnimationFrame(Document&, double monotonicAnimationStartTime); - static void serviceBeforeGetComputedStyle(Node&, CSSPropertyID); - static void serviceAfterStyleRecalc(Document&); + static void updateAnimationTimingForAnimationFrame(Document&, double monotonicAnimationStartTime); + static bool needsOutdatedAnimationPlayerUpdate(const Document&); + static void updateOutdatedAnimationPlayersIfNeeded(Document&); + static void updateAnimationTimingForGetComputedStyle(Node&, CSSPropertyID); + static void startPendingAnimations(Document&); private: DocumentAnimations() { } diff --git a/chromium/third_party/WebKit/Source/core/animation/DocumentTimeline.cpp b/chromium/third_party/WebKit/Source/core/animation/DocumentTimeline.cpp deleted file mode 100644 index 21027f8b073..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/DocumentTimeline.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/DocumentTimeline.h" - -#include "core/animation/ActiveAnimations.h" -#include "core/animation/AnimationClock.h" -#include "core/dom/Document.h" -#include "core/frame/FrameView.h" - -namespace WebCore { - -// This value represents 1 frame at 30Hz plus a little bit of wiggle room. -// TODO: Plumb a nominal framerate through and derive this value from that. -const double DocumentTimeline::s_minimumDelay = 0.04; - - -PassRefPtr<DocumentTimeline> DocumentTimeline::create(Document* document, PassOwnPtr<PlatformTiming> timing) -{ - return adoptRef(new DocumentTimeline(document, timing)); -} - -DocumentTimeline::DocumentTimeline(Document* document, PassOwnPtr<PlatformTiming> timing) - : m_zeroTime(nullValue()) - , m_document(document) - , m_eventDistpachTimer(this, &DocumentTimeline::eventDispatchTimerFired) -{ - if (!timing) - m_timing = adoptPtr(new DocumentTimelineTiming(this)); - else - m_timing = timing; - - ASSERT(document); -} - -Player* DocumentTimeline::createPlayer(TimedItem* child) -{ - RefPtr<Player> player = Player::create(*this, child); - Player* result = player.get(); - m_players.append(player.release()); - if (m_document->view()) - m_timing->serviceOnNextFrame(); - return result; -} - -Player* DocumentTimeline::play(TimedItem* child) -{ - Player* player = createPlayer(child); - player->setStartTime(currentTime()); - return player; -} - -void DocumentTimeline::wake() -{ - m_timing->serviceOnNextFrame(); -} - -bool DocumentTimeline::serviceAnimations() -{ - TRACE_EVENT0("webkit", "DocumentTimeline::serviceAnimations"); - - m_timing->cancelWake(); - - double timeToNextEffect = std::numeric_limits<double>::infinity(); - bool didTriggerStyleRecalc = false; - for (int i = m_players.size() - 1; i >= 0; --i) { - double playerNextEffect; - bool playerDidTriggerStyleRecalc; - if (!m_players[i]->update(&playerNextEffect, &playerDidTriggerStyleRecalc)) - m_players.remove(i); - didTriggerStyleRecalc |= playerDidTriggerStyleRecalc; - if (playerNextEffect < timeToNextEffect) - timeToNextEffect = playerNextEffect; - } - - if (!m_players.isEmpty()) { - if (timeToNextEffect < s_minimumDelay) - m_timing->serviceOnNextFrame(); - else if (timeToNextEffect != std::numeric_limits<double>::infinity()) - m_timing->wakeAfter(timeToNextEffect - s_minimumDelay); - } - - return didTriggerStyleRecalc; -} - -void DocumentTimeline::setZeroTime(double zeroTime) -{ - ASSERT(isNull(m_zeroTime)); - m_zeroTime = zeroTime; - ASSERT(!isNull(m_zeroTime)); -} - -void DocumentTimeline::DocumentTimelineTiming::wakeAfter(double duration) -{ - m_timer.startOneShot(duration); -} - -void DocumentTimeline::DocumentTimelineTiming::cancelWake() -{ - m_timer.stop(); -} - -void DocumentTimeline::DocumentTimelineTiming::serviceOnNextFrame() -{ - if (m_timeline->m_document->view()) - m_timeline->m_document->view()->scheduleAnimation(); -} - -double DocumentTimeline::currentTime() -{ - return m_document->animationClock().currentTime() - m_zeroTime; -} - -void DocumentTimeline::pauseAnimationsForTesting(double pauseTime) -{ - for (size_t i = 0; i < m_players.size(); i++) { - m_players[i]->pauseForTesting(); - m_players[i]->setCurrentTime(pauseTime); - } -} - -void DocumentTimeline::dispatchEvents() -{ - Vector<EventToDispatch> events = m_events; - m_events.clear(); - for (size_t i = 0; i < events.size(); i++) - events[i].target->dispatchEvent(events[i].event.release()); -} - -void DocumentTimeline::dispatchEventsAsync() -{ - if (m_events.isEmpty() || m_eventDistpachTimer.isActive()) - return; - m_eventDistpachTimer.startOneShot(0); -} - -void DocumentTimeline::eventDispatchTimerFired(Timer<DocumentTimeline>*) -{ - dispatchEvents(); -} - -size_t DocumentTimeline::numberOfActiveAnimationsForTesting() const -{ - if (isNull(m_zeroTime)) - return 0; - // Includes all players whose directly associated timed items - // are current or in effect. - if (isNull(m_zeroTime)) - return 0; - size_t count = 0; - for (size_t i = 0; i < m_players.size(); ++i) { - const TimedItem* timedItem = m_players[i]->source(); - if (m_players[i]->hasStartTime()) - count += (timedItem && (timedItem->isCurrent() || timedItem->isInEffect())); - } - return count; -} - -} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/EffectInput.cpp b/chromium/third_party/WebKit/Source/core/animation/EffectInput.cpp new file mode 100644 index 00000000000..466052c1346 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/EffectInput.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/EffectInput.h" + +#include "bindings/v8/Dictionary.h" +#include "core/animation/AnimationHelpers.h" +#include "core/animation/KeyframeEffectModel.h" +#include "core/animation/StringKeyframe.h" +#include "core/css/parser/BisonCSSParser.h" +#include "core/css/resolver/CSSToStyleMap.h" +#include "core/css/resolver/StyleResolver.h" +#include "core/dom/Element.h" +#include "wtf/NonCopyingSort.h" + +namespace WebCore { + +PassRefPtrWillBeRawPtr<AnimationEffect> EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState) +{ + // FIXME: Remove the dependency on element. + if (!element) + return nullptr; + + StyleSheetContents* styleSheetContents = element->document().elementSheet().contents(); + StringKeyframeVector keyframes; + bool everyFrameHasOffset = true; + bool looselySortedByOffset = true; + double lastOffset = -std::numeric_limits<double>::infinity(); + + for (size_t i = 0; i < keyframeDictionaryVector.size(); ++i) { + RefPtrWillBeRawPtr<StringKeyframe> keyframe = StringKeyframe::create(); + + bool frameHasOffset = false; + double offset; + if (keyframeDictionaryVector[i].get("offset", offset)) { + // Keyframes with offsets outside the range [0.0, 1.0] are ignored. + if (std::isnan(offset) || offset < 0 || offset > 1) + continue; + + frameHasOffset = true; + // The JS value null gets converted to 0 so we need to check whether the original value is null. + if (offset == 0) { + ScriptValue scriptValue; + if (keyframeDictionaryVector[i].get("offset", scriptValue) && scriptValue.isNull()) + frameHasOffset = false; + } + if (frameHasOffset) { + keyframe->setOffset(offset); + if (offset < lastOffset) + looselySortedByOffset = false; + lastOffset = offset; + } + } + everyFrameHasOffset = everyFrameHasOffset && frameHasOffset; + + keyframes.append(keyframe); + + String compositeString; + keyframeDictionaryVector[i].get("composite", compositeString); + if (compositeString == "add") + keyframe->setComposite(AnimationEffect::CompositeAdd); + + String timingFunctionString; + if (keyframeDictionaryVector[i].get("easing", timingFunctionString)) { + if (RefPtrWillBeRawPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString)) + keyframe->setEasing(CSSToStyleMap::mapAnimationTimingFunction(timingFunctionValue.get(), true)); + } + + Vector<String> keyframeProperties; + keyframeDictionaryVector[i].getOwnPropertyNames(keyframeProperties); + for (size_t j = 0; j < keyframeProperties.size(); ++j) { + String property = keyframeProperties[j]; + CSSPropertyID id = camelCaseCSSPropertyNameToID(property); + if (id == CSSPropertyInvalid) + continue; + String value; + keyframeDictionaryVector[i].get(property, value); + keyframe->setPropertyValue(id, value, styleSheetContents); + } + } + + if (!looselySortedByOffset) { + if (!everyFrameHasOffset) { + exceptionState.throwDOMException(InvalidModificationError, "Keyframes are not loosely sorted by offset."); + return nullptr; + } + nonCopyingSort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets); + } + + RefPtrWillBeRawPtr<StringKeyframeEffectModel> keyframeEffectModel = StringKeyframeEffectModel::create(keyframes); + if (!keyframeEffectModel->isReplaceOnly()) { + exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported."); + return nullptr; + } + keyframeEffectModel->forceConversionsToAnimatableValues(element); + + return keyframeEffectModel; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/EffectInput.h b/chromium/third_party/WebKit/Source/core/animation/EffectInput.h new file mode 100644 index 00000000000..e8948637813 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/EffectInput.h @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EffectInput_h +#define EffectInput_h + +#include "core/animation/AnimationEffect.h" +#include "wtf/Vector.h" + +namespace WebCore { + +class AnimationEffect; +class Dictionary; +class Element; +class ExceptionState; + +class EffectInput { +public: + static PassRefPtrWillBeRawPtr<AnimationEffect> convert(Element*, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState&); +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/EffectInputTest.cpp b/chromium/third_party/WebKit/Source/core/animation/EffectInputTest.cpp new file mode 100644 index 00000000000..d8e174ce2a0 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/EffectInputTest.cpp @@ -0,0 +1,125 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/EffectInput.h" + +#include "bindings/v8/Dictionary.h" +#include "core/animation/AnimationTestHelper.h" +#include "core/animation/KeyframeEffectModel.h" +#include "core/dom/Document.h" +#include "core/dom/Element.h" +#include <gtest/gtest.h> +#include <v8.h> + +using namespace WebCore; + +namespace { + +class AnimationEffectInputTest : public ::testing::Test { +protected: + AnimationEffectInputTest() + : document(Document::create()) + , element(document->createElement("foo", ASSERT_NO_EXCEPTION)) + , m_isolate(v8::Isolate::GetCurrent()) + , m_scope(m_isolate) + { + } + + RefPtrWillBePersistent<Document> document; + RefPtrWillBePersistent<Element> element; + TrackExceptionState exceptionState; + v8::Isolate* m_isolate; + +private: + V8TestingScope m_scope; +}; + +TEST_F(AnimationEffectInputTest, SortedOffsets) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0"); + setV8ObjectPropertyAsString(keyframe2, "width", "0px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "1"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + RefPtrWillBeRawPtr<AnimationEffect> animationEffect = EffectInput::convert(element.get(), jsKeyframes, exceptionState); + EXPECT_FALSE(exceptionState.hadException()); + const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(animationEffect.get()); + EXPECT_EQ(1.0, keyframeEffect.getFrames()[1]->offset()); +} + +TEST_F(AnimationEffectInputTest, UnsortedOffsets) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "0px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "1"); + setV8ObjectPropertyAsString(keyframe2, "width", "100px"); + setV8ObjectPropertyAsString(keyframe2, "offset", "0"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + + RefPtrWillBeRawPtr<AnimationEffect> animationEffect = EffectInput::convert(element.get(), jsKeyframes, exceptionState); + EXPECT_FALSE(exceptionState.hadException()); + const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(animationEffect.get()); + EXPECT_EQ(1.0, keyframeEffect.getFrames()[1]->offset()); +} + +TEST_F(AnimationEffectInputTest, LooslySorted) +{ + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe3 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "100px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "0"); + setV8ObjectPropertyAsString(keyframe2, "width", "200px"); + setV8ObjectPropertyAsString(keyframe3, "width", "0px"); + setV8ObjectPropertyAsString(keyframe3, "offset", "1"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + jsKeyframes.append(Dictionary(keyframe3, m_isolate)); + + RefPtrWillBeRawPtr<AnimationEffect> animationEffect = EffectInput::convert(element.get(), jsKeyframes, exceptionState); + EXPECT_FALSE(exceptionState.hadException()); + const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(animationEffect.get()); + EXPECT_EQ(1, keyframeEffect.getFrames()[2]->offset()); +} + +TEST_F(AnimationEffectInputTest, Invalid) +{ + // Not loosely sorted by offset, and there exists a keyframe with null offset. + Vector<Dictionary> jsKeyframes; + v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); + v8::Handle<v8::Object> keyframe3 = v8::Object::New(m_isolate); + + setV8ObjectPropertyAsString(keyframe1, "width", "0px"); + setV8ObjectPropertyAsString(keyframe1, "offset", "1"); + setV8ObjectPropertyAsString(keyframe2, "width", "200px"); + setV8ObjectPropertyAsString(keyframe3, "width", "100px"); + setV8ObjectPropertyAsString(keyframe3, "offset", "0"); + + jsKeyframes.append(Dictionary(keyframe1, m_isolate)); + jsKeyframes.append(Dictionary(keyframe2, m_isolate)); + jsKeyframes.append(Dictionary(keyframe3, m_isolate)); + + RefPtrWillBeRawPtr<AnimationEffect> animationEffect ALLOW_UNUSED = EffectInput::convert(element.get(), jsKeyframes, exceptionState); + EXPECT_TRUE(exceptionState.hadException()); + EXPECT_EQ(InvalidModificationError, exceptionState.code()); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.cpp b/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.cpp deleted file mode 100644 index 0beb346f7d6..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/ElementAnimation.h" - -#include "core/animation/DocumentTimeline.h" -#include "core/css/RuntimeCSSEnabled.h" -#include "core/css/resolver/StyleResolver.h" -#include "wtf/text/StringBuilder.h" -#include <algorithm> - -namespace WebCore { - -CSSPropertyID ElementAnimation::camelCaseCSSPropertyNameToID(const String& propertyName) -{ - if (propertyName.find('-') != kNotFound) - return CSSPropertyInvalid; - - StringBuilder builder; - size_t position = 0; - size_t end; - while ((end = propertyName.find(isASCIIUpper, position)) != kNotFound) { - builder.append(propertyName.substring(position, end - position) + "-" + toASCIILower((propertyName)[end])); - position = end + 1; - } - builder.append(propertyName.substring(position)); - // Doesn't handle prefixed properties. - CSSPropertyID id = cssPropertyID(builder.toString()); - return id; -} - -void ElementAnimation::animate(Element* element, Vector<Dictionary> keyframeDictionaryVector, double duration) -{ - ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); - - // FIXME: This test will not be neccessary once resolution of keyframe values occurs at - // animation application time. - if (!element->inActiveDocument()) - return; - element->document().updateStyleIfNeeded(); - if (!element->renderer()) - return; - - startAnimation(element, keyframeDictionaryVector, duration); -} - -void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, double duration) -{ - KeyframeAnimationEffect::KeyframeVector keyframes; - Vector<RefPtr<MutableStylePropertySet> > propertySetVector; - - for (size_t i = 0; i < keyframeDictionaryVector.size(); ++i) { - RefPtr<MutableStylePropertySet> propertySet = MutableStylePropertySet::create(); - propertySetVector.append(propertySet); - - RefPtr<Keyframe> keyframe = Keyframe::create(); - keyframes.append(keyframe); - - double offset; - if (keyframeDictionaryVector[i].get("offset", offset)) { - keyframe->setOffset(offset); - } else { - // FIXME: Web Animations CSS engine does not yet implement handling of - // keyframes without specified offsets. This check can be removed when - // that funcitonality is implemented. - ASSERT_NOT_REACHED(); - return; - } - - String compositeString; - keyframeDictionaryVector[i].get("composite", compositeString); - if (compositeString == "add") - keyframe->setComposite(AnimationEffect::CompositeAdd); - - Vector<String> keyframeProperties; - keyframeDictionaryVector[i].getOwnPropertyNames(keyframeProperties); - - for (size_t j = 0; j < keyframeProperties.size(); ++j) { - String property = keyframeProperties[j]; - CSSPropertyID id = camelCaseCSSPropertyNameToID(property); - - // FIXME: There is no way to store invalid properties or invalid values - // in a Keyframe object, so for now I just skip over them. Eventually we - // will need to support getFrames(), which should return exactly the - // keyframes that were input through the API. We will add a layer to wrap - // KeyframeAnimationEffect, store input keyframes and implement getFrames. - if (id == CSSPropertyInvalid || !CSSAnimations::isAnimatableProperty(id)) - continue; - - String value; - keyframeDictionaryVector[i].get(property, value); - propertySet->setProperty(id, value); - } - } - - // FIXME: Replace this with code that just parses, when that code is available. - RefPtr<KeyframeAnimationEffect> effect = StyleResolver::createKeyframeAnimationEffect(*element, propertySetVector, keyframes); - - // FIXME: Totally hardcoded Timing for now. Will handle timing parameters later. - Timing timing; - // FIXME: Currently there is no way to tell whether or not an iterationDuration - // has been specified (becauser the default argument is 0). So any animation - // created using Element.animate() will have a timing with hasIterationDuration() - // == true. - timing.hasIterationDuration = true; - timing.iterationDuration = std::max<double>(duration, 0); - - RefPtr<Animation> animation = Animation::create(element, effect, timing); - DocumentTimeline* timeline = element->document().timeline(); - ASSERT(timeline); - timeline->play(animation.get()); -} - -} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.h b/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.h index cfc8f6999fa..5f60e2e79ee 100644 --- a/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.h +++ b/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.h @@ -31,22 +31,71 @@ #ifndef ElementAnimation_h #define ElementAnimation_h -#include "bindings/v8/Dictionary.h" -#include "core/css/CSSParser.h" +#include "core/animation/Animation.h" +#include "core/animation/AnimationTimeline.h" +#include "core/animation/EffectInput.h" +#include "core/animation/TimingInput.h" +#include "core/dom/Document.h" +#include "core/dom/Element.h" +#include "platform/RuntimeEnabledFeatures.h" namespace WebCore { -class Element; +class Dictionary; class ElementAnimation { public: - static CSSPropertyID camelCaseCSSPropertyNameToID(const String& propertyName); - static void animate(Element*, Vector<Dictionary> keyframesDictionaryVector, double duration = 0); + static AnimationPlayer* animate(Element& element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Dictionary& timingInputDictionary) + { + return animateInternal(element, effect, TimingInput::convert(timingInputDictionary)); + } -private: - static void startAnimation(Element*, Vector<Dictionary> keyframesDictionaryVector, double duration = 0); + static AnimationPlayer* animate(Element& element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, double duration) + { + return animateInternal(element, effect, TimingInput::convert(duration)); + } + + static AnimationPlayer* animate(Element& element, PassRefPtrWillBeRawPtr<AnimationEffect> effect) + { + return animateInternal(element, effect, Timing()); + } + + static AnimationPlayer* animate(Element& element, const Vector<Dictionary>& keyframeDictionaryVector, const Dictionary& timingInputDictionary, ExceptionState& exceptionState) + { + RefPtrWillBeRawPtr<AnimationEffect> effect = EffectInput::convert(&element, keyframeDictionaryVector, exceptionState); + if (exceptionState.hadException()) + return 0; + ASSERT(effect); + return animateInternal(element, effect.release(), TimingInput::convert(timingInputDictionary)); + } - friend class AnimationElementAnimationTest; + static AnimationPlayer* animate(Element& element, const Vector<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState& exceptionState) + { + RefPtrWillBeRawPtr<AnimationEffect> effect = EffectInput::convert(&element, keyframeDictionaryVector, exceptionState); + if (exceptionState.hadException()) + return 0; + ASSERT(effect); + return animateInternal(element, effect.release(), TimingInput::convert(duration)); + } + + static AnimationPlayer* animate(Element& element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState) + { + RefPtrWillBeRawPtr<AnimationEffect> effect = EffectInput::convert(&element, keyframeDictionaryVector, exceptionState); + if (exceptionState.hadException()) + return 0; + ASSERT(effect); + return animateInternal(element, effect.release(), Timing()); + } + +private: + static AnimationPlayer* animateInternal(Element& element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing) + { + if (RuntimeEnabledFeatures::webAnimationsElementAnimateEnabled()) { + RefPtrWillBeRawPtr<Animation> animation = Animation::create(&element, effect, timing); + return element.document().timeline().play(animation.get()); + } + return 0; + } }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.idl b/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.idl index e191d950bc0..f524939ba45 100644 --- a/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.idl +++ b/chromium/third_party/WebKit/Source/core/animation/ElementAnimation.idl @@ -28,6 +28,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// http://www.w3.org/TR/web-animations/#idl-def-Element +// FIXME: move to Animatable +// http://dev.w3.org/fxtf/web-animations/#idl-def-Animatable + partial interface Element { - [RuntimeEnabled=WebAnimationsAPI] void animate(sequence<Dictionary> keyframes, optional double duration); -};
\ No newline at end of file + // FIXME: needs support for union types http://crbug.com/240176 + // AnimationPlayer animate((AnimationEffect or sequence<Dictionary>)? effect, optional (double or Dictionary) timing); + [Custom, RaisesException] AnimationPlayer animate(object effect, optional object timing); +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/ElementAnimationTest.cpp b/chromium/third_party/WebKit/Source/core/animation/ElementAnimationTest.cpp deleted file mode 100644 index bb3f0d4f0ad..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/ElementAnimationTest.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2013, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/ElementAnimation.h" - -#include "core/animation/AnimatableLength.h" -#include "core/animation/Animation.h" -#include "core/animation/AnimationClock.h" -#include "core/animation/DocumentTimeline.h" -#include "core/animation/KeyframeAnimationEffect.h" -#include "core/dom/Document.h" -#include "core/dom/Element.h" - -#include <gtest/gtest.h> - -namespace WebCore { - -namespace { - -v8::Handle<v8::Value> stringToV8Value(String string) -{ - return v8::Handle<v8::Value>::Cast(v8String(v8::Isolate::GetCurrent(), string)); -} - -void setV8ObjectProperty(v8::Handle<v8::Object> object, String name, String value) -{ - object->Set(stringToV8Value(name), stringToV8Value(value)); -} - -} // namespace - -class AnimationElementAnimationTest : public ::testing::Test { -protected: - virtual void SetUp() - { - document = Document::create(); - document->animationClock().resetTimeForTesting(); - element = document->createElement("foo", ASSERT_NO_EXCEPTION); - document->timeline()->setZeroTime(0); - ASSERT_EQ(0, document->timeline()->currentTime()); - } - - RefPtr<Document> document; - RefPtr<Element> element; - - void startAnimation(Element* element, Vector<Dictionary> keyframesDictionaryVector) - { - ElementAnimation::startAnimation(element, keyframesDictionaryVector); - } - - void startAnimationWithSpecifiedDuration(Element* element, Vector<Dictionary> keyframesDictionaryVector, double duration) - { - ElementAnimation::startAnimation(element, keyframesDictionaryVector, duration); - } -}; - -TEST_F(AnimationElementAnimationTest, CanStartAnAnimation) -{ - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - v8::Local<v8::Context> context = v8::Context::New(isolate); - v8::Context::Scope contextScope(context); - - Vector<Dictionary> jsKeyframes; - v8::Handle<v8::Object> keyframe1 = v8::Object::New(); - v8::Handle<v8::Object> keyframe2 = v8::Object::New(); - - setV8ObjectProperty(keyframe1, "width", "100px"); - setV8ObjectProperty(keyframe1, "offset", "0"); - setV8ObjectProperty(keyframe2, "width", "0px"); - setV8ObjectProperty(keyframe2, "offset", "1"); - - jsKeyframes.append(Dictionary(keyframe1, isolate)); - jsKeyframes.append(Dictionary(keyframe2, isolate)); - - String value1; - ASSERT_TRUE(jsKeyframes[0].get("width", value1)); - ASSERT_EQ("100px", value1); - - String value2; - ASSERT_TRUE(jsKeyframes[1].get("width", value2)); - ASSERT_EQ("0px", value2); - - startAnimationWithSpecifiedDuration(element.get(), jsKeyframes, 0); - - Player* player = document->timeline()->players().at(0).get(); - - Animation* animation = toAnimation(player->source()); - - Element* target = animation->target(); - EXPECT_EQ(*element.get(), *target); - - const KeyframeAnimationEffect::KeyframeVector keyframes = - toKeyframeAnimationEffect(animation->effect())->getFrames(); - - EXPECT_EQ(0, keyframes[0]->offset()); - EXPECT_EQ(1, keyframes[1]->offset()); - - const AnimatableValue* keyframe1Width = keyframes[0]->propertyValue(CSSPropertyWidth); - const AnimatableValue* keyframe2Width = keyframes[1]->propertyValue(CSSPropertyWidth); - ASSERT(keyframe1Width); - ASSERT(keyframe2Width); - - EXPECT_TRUE(keyframe1Width->isLength()); - EXPECT_TRUE(keyframe2Width->isLength()); - - EXPECT_EQ("100px", toAnimatableLength(keyframe1Width)->toCSSValue()->cssText()); - EXPECT_EQ("0px", toAnimatableLength(keyframe2Width)->toCSSValue()->cssText()); -} - -TEST_F(AnimationElementAnimationTest, ParseCamelCasePropertyNames) -{ - EXPECT_EQ(CSSPropertyInvalid, ElementAnimation::camelCaseCSSPropertyNameToID(String("line-height"))); - EXPECT_EQ(CSSPropertyLineHeight, ElementAnimation::camelCaseCSSPropertyNameToID(String("lineHeight"))); - EXPECT_EQ(CSSPropertyBorderTopWidth, ElementAnimation::camelCaseCSSPropertyNameToID(String("borderTopWidth"))); - EXPECT_EQ(CSSPropertyWidth, ElementAnimation::camelCaseCSSPropertyNameToID(String("width"))); - EXPECT_EQ(CSSPropertyInvalid, ElementAnimation::camelCaseCSSPropertyNameToID(String("Width"))); - EXPECT_EQ(CSSPropertyInvalid, ElementAnimation::camelCaseCSSPropertyNameToID(String("-webkit-transform"))); - EXPECT_EQ(CSSPropertyInvalid, ElementAnimation::camelCaseCSSPropertyNameToID(String("webkitTransform"))); - EXPECT_EQ(CSSPropertyInvalid, ElementAnimation::camelCaseCSSPropertyNameToID(String("cssFloat"))); -} - -TEST_F(AnimationElementAnimationTest, CanSetDuration) -{ - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - v8::Local<v8::Context> context = v8::Context::New(isolate); - v8::Context::Scope contextScope(context); - - Vector<Dictionary, 0> jsKeyframes; - double duration = 2; - - startAnimationWithSpecifiedDuration(element.get(), jsKeyframes, duration); - - Player* player = document->timeline()->players().at(0).get(); - - EXPECT_TRUE(player->source()->specified().hasIterationDuration); - EXPECT_EQ(duration, player->source()->specified().iterationDuration); -} - -TEST_F(AnimationElementAnimationTest, CanOmitSpecifiedDuration) -{ - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - v8::Local<v8::Context> context = v8::Context::New(isolate); - v8::Context::Scope contextScope(context); - - Vector<Dictionary, 0> jsKeyframes; - - startAnimation(element.get(), jsKeyframes); - - Player* player = document->timeline()->players().at(0).get(); - - // FIXME: This is correct for the moment, as using c++ default arguments means - // there is no way to tell whether a duration has been specified by the user. - // Once we implment timing object arguments we should be able to tell, and this - // check should be changed to EXPECT_FALSE. - EXPECT_TRUE(player->source()->specified().hasIterationDuration); - EXPECT_EQ(0, player->source()->specified().iterationDuration); -} - -TEST_F(AnimationElementAnimationTest, ClipNegativeDurationToZero) -{ - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - v8::Local<v8::Context> context = v8::Context::New(isolate); - v8::Context::Scope contextScope(context); - - Vector<Dictionary, 0> jsKeyframes; - double duration = -2; - - startAnimationWithSpecifiedDuration(element.get(), jsKeyframes, duration); - - Player* player = document->timeline()->players().at(0).get(); - - EXPECT_TRUE(player->source()->specified().hasIterationDuration); - EXPECT_EQ(0, player->source()->specified().iterationDuration); -} - -} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/InertAnimation.cpp b/chromium/third_party/WebKit/Source/core/animation/InertAnimation.cpp index 3cb21505300..3ba61c06a8c 100644 --- a/chromium/third_party/WebKit/Source/core/animation/InertAnimation.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/InertAnimation.cpp @@ -30,37 +30,43 @@ #include "config.h" #include "core/animation/InertAnimation.h" +#include "core/animation/interpolation/Interpolation.h" namespace WebCore { -PassRefPtr<InertAnimation> InertAnimation::create(PassRefPtr<AnimationEffect> effect, const Timing& timing, bool paused) +PassRefPtrWillBeRawPtr<InertAnimation> InertAnimation::create(PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, bool paused) { - return adoptRef(new InertAnimation(effect, timing, paused)); + return adoptRefWillBeNoop(new InertAnimation(effect, timing, paused)); } -InertAnimation::InertAnimation(PassRefPtr<AnimationEffect> effect, const Timing& timing, bool paused) - : TimedItem(timing) +InertAnimation::InertAnimation(PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, bool paused) + : AnimationNode(timing) , m_effect(effect) , m_paused(paused) { } -PassOwnPtr<AnimationEffect::CompositableValueList> InertAnimation::sample() +PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > InertAnimation::sample(double inheritedTime) { - updateInheritedTime(0); + updateInheritedTime(inheritedTime, TimingUpdateOnDemand); if (!isInEffect()) return nullptr; double iteration = currentIteration(); ASSERT(iteration >= 0); // FIXME: Handle iteration values which overflow int. - return m_effect->sample(static_cast<int>(iteration), timeFraction()); + return m_effect->sample(static_cast<int>(iteration), timeFraction(), iterationDuration()); } - -double InertAnimation::calculateTimeToEffectChange(double, double) const +double InertAnimation::calculateTimeToEffectChange(bool, double, double) const { return std::numeric_limits<double>::infinity(); } +void InertAnimation::trace(Visitor* visitor) +{ + visitor->trace(m_effect); + AnimationNode::trace(visitor); +} + } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/InertAnimation.h b/chromium/third_party/WebKit/Source/core/animation/InertAnimation.h index a52ba532ec6..633ad5030ca 100644 --- a/chromium/third_party/WebKit/Source/core/animation/InertAnimation.h +++ b/chromium/third_party/WebKit/Source/core/animation/InertAnimation.h @@ -32,27 +32,27 @@ #define InertAnimation_h #include "core/animation/AnimationEffect.h" -#include "core/animation/TimedItem.h" +#include "core/animation/AnimationNode.h" #include "wtf/RefPtr.h" namespace WebCore { -class InertAnimation FINAL : public TimedItem { - +class InertAnimation FINAL : public AnimationNode { public: - static PassRefPtr<InertAnimation> create(PassRefPtr<AnimationEffect>, const Timing&, bool paused); - PassOwnPtr<AnimationEffect::CompositableValueList> sample(); + static PassRefPtrWillBeRawPtr<InertAnimation> create(PassRefPtrWillBeRawPtr<AnimationEffect>, const Timing&, bool paused); + PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample(double inheritedTime); AnimationEffect* effect() const { return m_effect.get(); } bool paused() const { return m_paused; } + virtual void trace(Visitor*); + protected: - virtual bool updateChildrenAndEffects() const OVERRIDE { return false; }; - virtual void willDetach() OVERRIDE { }; - virtual double calculateTimeToEffectChange(double inheritedTime, double timeToNextIteration) const OVERRIDE FINAL; + virtual void updateChildrenAndEffects() const OVERRIDE { } + virtual double calculateTimeToEffectChange(bool forwards, double inheritedTime, double timeToNextIteration) const OVERRIDE; private: - InertAnimation(PassRefPtr<AnimationEffect>, const Timing&, bool paused); - RefPtr<AnimationEffect> m_effect; + InertAnimation(PassRefPtrWillBeRawPtr<AnimationEffect>, const Timing&, bool paused); + RefPtrWillBeMember<AnimationEffect> m_effect; bool m_paused; }; diff --git a/chromium/third_party/WebKit/Source/core/animation/InterpolableValue.cpp b/chromium/third_party/WebKit/Source/core/animation/InterpolableValue.cpp new file mode 100644 index 00000000000..e1b43a9345c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/InterpolableValue.cpp @@ -0,0 +1,75 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/InterpolableValue.h" + +namespace WebCore { + +DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(InterpolableValue); + +PassOwnPtrWillBeRawPtr<InterpolableValue> InterpolableNumber::interpolate(const InterpolableValue &to, const double progress) const +{ + const InterpolableNumber* toNumber = toInterpolableNumber(&to); + if (!progress) + return create(m_value); + if (progress == 1) + return create(toNumber->m_value); + return create(m_value * (1 - progress) + toNumber->m_value * progress); +} + +PassOwnPtrWillBeRawPtr<InterpolableValue> InterpolableBool::interpolate(const InterpolableValue &to, const double progress) const +{ + if (progress < 0.5) { + return clone(); + } + return to.clone(); +} + +PassOwnPtrWillBeRawPtr<InterpolableValue> InterpolableList::interpolate(const InterpolableValue &to, const double progress) const +{ + const InterpolableList* toList = toInterpolableList(&to); + ASSERT(toList->m_size == m_size); + + if (!progress) { + return create(*this); + } + if (progress == 1) { + return InterpolableList::create(*toList); + } + + OwnPtrWillBeRawPtr<InterpolableList> result = create(m_size); + for (size_t i = 0; i < m_size; i++) { + ASSERT(m_values[i]); + ASSERT(toList->m_values[i]); + result->set(i, m_values[i]->interpolate(*(toList->m_values[i]), progress)); + } + return result.release(); +} + +void InterpolableList::trace(Visitor* visitor) +{ +#if ENABLE_OILPAN + visitor->trace(m_values); +#endif + InterpolableValue::trace(visitor); +} + +PassOwnPtrWillBeRawPtr<InterpolableValue> InterpolableAnimatableValue::interpolate(const InterpolableValue &other, const double percentage) const +{ + const InterpolableAnimatableValue *otherValue = toInterpolableAnimatableValue(&other); + if (!percentage) + return create(m_value); + if (percentage == 1) + return create(otherValue->m_value); + return create(AnimatableValue::interpolate(m_value.get(), otherValue->m_value.get(), percentage)); +} + +void InterpolableAnimatableValue::trace(Visitor* visitor) +{ + visitor->trace(m_value); + InterpolableValue::trace(visitor); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/InterpolableValue.h b/chromium/third_party/WebKit/Source/core/animation/InterpolableValue.h new file mode 100644 index 00000000000..75484cf12b1 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/InterpolableValue.h @@ -0,0 +1,167 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef InterpolableValue_h +#define InterpolableValue_h + +#include "core/animation/AnimatableValue.h" +#include "wtf/OwnPtr.h" +#include "wtf/PassOwnPtr.h" +#include "wtf/Vector.h" + +namespace WebCore { + +class InterpolableValue : public NoBaseWillBeGarbageCollected<InterpolableValue> { + DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(InterpolableValue); +public: + virtual bool isNumber() const { return false; } + virtual bool isBool() const { return false; } + virtual bool isList() const { return false; } + virtual bool isAnimatableValue() const { return false; } + + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> clone() const = 0; + + virtual void trace(Visitor*) { } + +private: + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> interpolate(const InterpolableValue &to, const double progress) const = 0; + + friend class Interpolation; + + // Keep interpolate private, but allow calls within the hierarchy without + // knowledge of type. + friend class DeferredLegacyStyleInterpolation; + friend class InterpolableNumber; + friend class InterpolableBool; + friend class InterpolableList; +}; + +class InterpolableNumber : public InterpolableValue { +public: + static PassOwnPtrWillBeRawPtr<InterpolableNumber> create(double value) + { + return adoptPtrWillBeNoop(new InterpolableNumber(value)); + } + + virtual bool isNumber() const OVERRIDE FINAL { return true; } + double value() const { return m_value; } + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> clone() const OVERRIDE FINAL { return create(m_value); } + + virtual void trace(Visitor* visitor) OVERRIDE { InterpolableValue::trace(visitor); } + +private: + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> interpolate(const InterpolableValue &to, const double progress) const OVERRIDE FINAL; + double m_value; + + explicit InterpolableNumber(double value) + : m_value(value) + { + } + +}; + +class InterpolableBool : public InterpolableValue { +public: + static PassOwnPtrWillBeRawPtr<InterpolableBool> create(bool value) + { + return adoptPtrWillBeNoop(new InterpolableBool(value)); + } + + virtual bool isBool() const OVERRIDE FINAL { return true; } + bool value() const { return m_value; } + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> clone() const OVERRIDE FINAL { return create(m_value); } + + virtual void trace(Visitor* visitor) OVERRIDE { InterpolableValue::trace(visitor); } + +private: + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> interpolate(const InterpolableValue &to, const double progress) const OVERRIDE FINAL; + bool m_value; + + explicit InterpolableBool(bool value) + : m_value(value) + { + } + +}; + +class InterpolableList : public InterpolableValue { +public: + static PassOwnPtrWillBeRawPtr<InterpolableList> create(const InterpolableList &other) + { + return adoptPtrWillBeNoop(new InterpolableList(other)); + } + + static PassOwnPtrWillBeRawPtr<InterpolableList> create(size_t size) + { + return adoptPtrWillBeNoop(new InterpolableList(size)); + } + + virtual bool isList() const OVERRIDE FINAL { return true; } + void set(size_t position, PassOwnPtrWillBeRawPtr<InterpolableValue> value) + { + ASSERT(position < m_size); + m_values[position] = value; + } + const InterpolableValue* get(size_t position) const + { + ASSERT(position < m_size); + return m_values[position].get(); + } + size_t length() const { return m_size; } + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> clone() const OVERRIDE FINAL { return create(*this); } + + virtual void trace(Visitor*) OVERRIDE; + +private: + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> interpolate(const InterpolableValue &other, const double progress) const OVERRIDE FINAL; + explicit InterpolableList(size_t size) + : m_size(size) + , m_values(m_size) + { + } + + InterpolableList(const InterpolableList& other) + : m_size(other.m_size) + , m_values(m_size) + { + for (size_t i = 0; i < m_size; i++) + set(i, other.m_values[i]->clone()); + } + + size_t m_size; + WillBeHeapVector<OwnPtrWillBeMember<InterpolableValue> > m_values; +}; + +// FIXME: Remove this when we can. +class InterpolableAnimatableValue : public InterpolableValue { +public: + static PassOwnPtrWillBeRawPtr<InterpolableAnimatableValue> create(PassRefPtrWillBeRawPtr<AnimatableValue> value) + { + return adoptPtrWillBeNoop(new InterpolableAnimatableValue(value)); + } + + virtual bool isAnimatableValue() const OVERRIDE FINAL { return true; } + AnimatableValue* value() const { return m_value.get(); } + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> clone() const OVERRIDE FINAL { return create(m_value); } + + virtual void trace(Visitor*) OVERRIDE; + +private: + virtual PassOwnPtrWillBeRawPtr<InterpolableValue> interpolate(const InterpolableValue &other, const double progress) const OVERRIDE FINAL; + RefPtrWillBeMember<AnimatableValue> m_value; + + InterpolableAnimatableValue(PassRefPtrWillBeRawPtr<AnimatableValue> value) + : m_value(value) + { + } +}; + +DEFINE_TYPE_CASTS(InterpolableNumber, InterpolableValue, value, value->isNumber(), value.isNumber()); +DEFINE_TYPE_CASTS(InterpolableBool, InterpolableValue, value, value->isBool(), value.isBool()); +DEFINE_TYPE_CASTS(InterpolableList, InterpolableValue, value, value->isList(), value.isList()); +DEFINE_TYPE_CASTS(InterpolableAnimatableValue, InterpolableValue, value, value->isAnimatableValue(), value.isAnimatableValue()); + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/InterpolableValueTest.cpp b/chromium/third_party/WebKit/Source/core/animation/InterpolableValueTest.cpp new file mode 100644 index 00000000000..69ed8f6084f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/InterpolableValueTest.cpp @@ -0,0 +1,105 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/InterpolableValue.h" + +#include "core/animation/interpolation/Interpolation.h" + +#include <gtest/gtest.h> + +namespace WebCore { + +class AnimationInterpolableValueTest : public ::testing::Test { +protected: + InterpolableValue* interpolationValue(Interpolation& interpolation) + { + return interpolation.getCachedValueForTesting(); + } + + double interpolateNumbers(double a, double b, double progress) + { + RefPtrWillBeRawPtr<Interpolation> i = Interpolation::create(InterpolableNumber::create(a), InterpolableNumber::create(b)); + i->interpolate(0, progress); + return toInterpolableNumber(interpolationValue(*i.get()))->value(); + } + + bool interpolateBools(bool a, bool b, double progress) + { + RefPtrWillBeRawPtr<Interpolation> i = Interpolation::create(InterpolableBool::create(a), InterpolableBool::create(b)); + i->interpolate(0, progress); + return toInterpolableBool(interpolationValue(*i.get()))->value(); + } + + PassRefPtrWillBeRawPtr<Interpolation> interpolateLists(PassOwnPtrWillBeRawPtr<InterpolableList> listA, PassOwnPtrWillBeRawPtr<InterpolableList> listB, double progress) + { + RefPtrWillBeRawPtr<Interpolation> i = Interpolation::create(listA, listB); + i->interpolate(0, progress); + return i; + } +}; + +TEST_F(AnimationInterpolableValueTest, InterpolateNumbers) +{ + EXPECT_FLOAT_EQ(126, interpolateNumbers(42, 0, -2)); + EXPECT_FLOAT_EQ(42, interpolateNumbers(42, 0, 0)); + EXPECT_FLOAT_EQ(29.4f, interpolateNumbers(42, 0, 0.3)); + EXPECT_FLOAT_EQ(21, interpolateNumbers(42, 0, 0.5)); + EXPECT_FLOAT_EQ(0, interpolateNumbers(42, 0, 1)); + EXPECT_FLOAT_EQ(-21, interpolateNumbers(42, 0, 1.5)); +} + +TEST_F(AnimationInterpolableValueTest, InterpolateBools) +{ + EXPECT_FALSE(interpolateBools(false, true, -1)); + EXPECT_FALSE(interpolateBools(false, true, 0)); + EXPECT_FALSE(interpolateBools(false, true, 0.3)); + EXPECT_TRUE(interpolateBools(false, true, 0.5)); + EXPECT_TRUE(interpolateBools(false, true, 1)); + EXPECT_TRUE(interpolateBools(false, true, 2)); +} + +TEST_F(AnimationInterpolableValueTest, SimpleList) +{ + OwnPtrWillBeRawPtr<InterpolableList> listA = InterpolableList::create(3); + listA->set(0, InterpolableNumber::create(0)); + listA->set(1, InterpolableNumber::create(42)); + listA->set(2, InterpolableNumber::create(20.5)); + + OwnPtrWillBeRawPtr<InterpolableList> listB = InterpolableList::create(3); + listB->set(0, InterpolableNumber::create(100)); + listB->set(1, InterpolableNumber::create(-200)); + listB->set(2, InterpolableNumber::create(300)); + + RefPtrWillBeRawPtr<Interpolation> i = interpolateLists(listA.release(), listB.release(), 0.3); + InterpolableList* outList = toInterpolableList(interpolationValue(*i.get())); + EXPECT_FLOAT_EQ(30, toInterpolableNumber(outList->get(0))->value()); + EXPECT_FLOAT_EQ(-30.6f, toInterpolableNumber(outList->get(1))->value()); + EXPECT_FLOAT_EQ(104.35f, toInterpolableNumber(outList->get(2))->value()); +} + +TEST_F(AnimationInterpolableValueTest, NestedList) +{ + OwnPtrWillBeRawPtr<InterpolableList> listA = InterpolableList::create(3); + listA->set(0, InterpolableNumber::create(0)); + OwnPtrWillBeRawPtr<InterpolableList> subListA = InterpolableList::create(1); + subListA->set(0, InterpolableNumber::create(100)); + listA->set(1, subListA.release()); + listA->set(2, InterpolableBool::create(false)); + + OwnPtrWillBeRawPtr<InterpolableList> listB = InterpolableList::create(3); + listB->set(0, InterpolableNumber::create(100)); + OwnPtrWillBeRawPtr<InterpolableList> subListB = InterpolableList::create(1); + subListB->set(0, InterpolableNumber::create(50)); + listB->set(1, subListB.release()); + listB->set(2, InterpolableBool::create(true)); + + RefPtrWillBeRawPtr<Interpolation> i = interpolateLists(listA.release(), listB.release(), 0.5); + InterpolableList* outList = toInterpolableList(interpolationValue(*i.get())); + EXPECT_FLOAT_EQ(50, toInterpolableNumber(outList->get(0))->value()); + EXPECT_FLOAT_EQ(75, toInterpolableNumber(toInterpolableList(outList->get(1))->get(0))->value()); + EXPECT_TRUE(toInterpolableBool(outList->get(2))->value()); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp b/chromium/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp new file mode 100644 index 00000000000..c03fbab782c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp @@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/InterpolationEffect.h" + +namespace WebCore { + +PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > InterpolationEffect::getActiveInterpolations(double fraction, double iterationDuration) const +{ + + WillBeHeapVector<RefPtrWillBeMember<Interpolation> >* result = new WillBeHeapVector<RefPtrWillBeMember<Interpolation> >(); + + for (size_t i = 0; i < m_interpolations.size(); ++i) { + const InterpolationRecord* record = m_interpolations[i].get(); + if (fraction >= record->m_applyFrom && fraction < record->m_applyTo) { + RefPtrWillBeRawPtr<Interpolation> interpolation = record->m_interpolation; + double localFraction = (fraction - record->m_start) / (record->m_end - record->m_start); + if (record->m_easing) + localFraction = record->m_easing->evaluate(localFraction, accuracyForDuration(iterationDuration)); + interpolation->interpolate(0, localFraction); + result->append(interpolation); + } + } + + return adoptPtrWillBeNoop(result); +} + +void InterpolationEffect::InterpolationRecord::trace(Visitor* visitor) +{ + visitor->trace(m_interpolation); +} + +void InterpolationEffect::trace(Visitor* visitor) +{ +#if ENABLE_OILPAN + visitor->trace(m_interpolations); +#endif +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/InterpolationEffect.h b/chromium/third_party/WebKit/Source/core/animation/InterpolationEffect.h new file mode 100644 index 00000000000..c189f86deca --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/InterpolationEffect.h @@ -0,0 +1,66 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef InterpolationEffect_h +#define InterpolationEffect_h + +#include "core/animation/interpolation/Interpolation.h" +#include "platform/animation/TimingFunction.h" +#include "wtf/PassOwnPtr.h" +#include "wtf/RefCounted.h" + +namespace WebCore { + +class InterpolationEffect : public RefCountedWillBeGarbageCollected<InterpolationEffect> { +public: + static PassRefPtrWillBeRawPtr<InterpolationEffect> create() { return adoptRefWillBeNoop(new InterpolationEffect()); } + + PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > getActiveInterpolations(double fraction, double iterationDuration) const; + + void addInterpolation(PassRefPtrWillBeRawPtr<Interpolation> interpolation, PassRefPtr<TimingFunction> easing, double start, double end, double applyFrom, double applyTo) + { + m_interpolations.append(InterpolationRecord::create(interpolation, easing, start, end, applyFrom, applyTo)); + } + + void trace(Visitor*); + +private: + InterpolationEffect() + { + } + + class InterpolationRecord : public NoBaseWillBeGarbageCollectedFinalized<InterpolationRecord> { + public: + RefPtrWillBeMember<Interpolation> m_interpolation; + RefPtr<TimingFunction> m_easing; + double m_start; + double m_end; + double m_applyFrom; + double m_applyTo; + + static PassOwnPtrWillBeRawPtr<InterpolationRecord> create(PassRefPtrWillBeRawPtr<Interpolation> interpolation, PassRefPtr<TimingFunction> easing, double start, double end, double applyFrom, double applyTo) + { + return adoptPtrWillBeNoop(new InterpolationRecord(interpolation, easing, start, end, applyFrom, applyTo)); + } + + void trace(Visitor*); + + private: + InterpolationRecord(PassRefPtrWillBeRawPtr<Interpolation> interpolation, PassRefPtr<TimingFunction> easing, double start, double end, double applyFrom, double applyTo) + : m_interpolation(interpolation) + , m_easing(easing) + , m_start(start) + , m_end(end) + , m_applyFrom(applyFrom) + , m_applyTo(applyTo) + { + } + }; + + WillBeHeapVector<OwnPtrWillBeMember<InterpolationRecord> > m_interpolations; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/InterpolationEffectTest.cpp b/chromium/third_party/WebKit/Source/core/animation/InterpolationEffectTest.cpp new file mode 100644 index 00000000000..8b84b755d05 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/InterpolationEffectTest.cpp @@ -0,0 +1,98 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/InterpolationEffect.h" + +#include <gtest/gtest.h> + +namespace { + +const double duration = 1.0; + +} // namespace + +namespace WebCore { + +class AnimationInterpolationEffectTest : public ::testing::Test { +protected: + InterpolableValue* interpolationValue(Interpolation& interpolation) + { + return interpolation.getCachedValueForTesting(); + } + + double getInterpolableNumber(PassRefPtrWillBeRawPtr<Interpolation> value) + { + return toInterpolableNumber(interpolationValue(*value.get()))->value(); + } +}; + +TEST_F(AnimationInterpolationEffectTest, SingleInterpolation) +{ + RefPtrWillBeRawPtr<InterpolationEffect> interpolationEffect = InterpolationEffect::create(); + interpolationEffect->addInterpolation(Interpolation::create(InterpolableNumber::create(0), InterpolableNumber::create(10)), + RefPtr<TimingFunction>(), 0, 1, -1, 2); + + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > activeInterpolations = interpolationEffect->getActiveInterpolations(-2, duration); + EXPECT_EQ(0ul, activeInterpolations->size()); + + activeInterpolations = interpolationEffect->getActiveInterpolations(-0.5, duration); + EXPECT_EQ(1ul, activeInterpolations->size()); + EXPECT_EQ(-5, getInterpolableNumber(activeInterpolations->at(0))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(0.5, duration); + EXPECT_EQ(1ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(5, getInterpolableNumber(activeInterpolations->at(0))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(1.5, duration); + EXPECT_EQ(1ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(15, getInterpolableNumber(activeInterpolations->at(0))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(3, duration); + EXPECT_EQ(0ul, activeInterpolations->size()); +} + +TEST_F(AnimationInterpolationEffectTest, MultipleInterpolations) +{ + RefPtrWillBeRawPtr<InterpolationEffect> interpolationEffect = InterpolationEffect::create(); + interpolationEffect->addInterpolation(Interpolation::create(InterpolableNumber::create(10), InterpolableNumber::create(15)), + RefPtr<TimingFunction>(), 1, 2, 1, 3); + interpolationEffect->addInterpolation(Interpolation::create(InterpolableNumber::create(0), InterpolableNumber::create(1)), + LinearTimingFunction::shared(), 0, 1, 0, 1); + interpolationEffect->addInterpolation(Interpolation::create(InterpolableNumber::create(1), InterpolableNumber::create(6)), + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease), 0.5, 1.5, 0.5, 1.5); + + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > activeInterpolations = interpolationEffect->getActiveInterpolations(-0.5, duration); + EXPECT_EQ(0ul, activeInterpolations->size()); + + activeInterpolations = interpolationEffect->getActiveInterpolations(0, duration); + EXPECT_EQ(1ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(0, getInterpolableNumber(activeInterpolations->at(0))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(0.5, duration); + EXPECT_EQ(2ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(0.5f, getInterpolableNumber(activeInterpolations->at(0))); + EXPECT_FLOAT_EQ(1, getInterpolableNumber(activeInterpolations->at(1))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(1, duration); + EXPECT_EQ(2ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(10, getInterpolableNumber(activeInterpolations->at(0))); + EXPECT_FLOAT_EQ(5.0282884f, getInterpolableNumber(activeInterpolations->at(1))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(1, duration * 1000); + EXPECT_EQ(2ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(10, getInterpolableNumber(activeInterpolations->at(0))); + EXPECT_FLOAT_EQ(5.0120168f, getInterpolableNumber(activeInterpolations->at(1))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(1.5, duration); + EXPECT_EQ(1ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(12.5f, getInterpolableNumber(activeInterpolations->at(0))); + + activeInterpolations = interpolationEffect->getActiveInterpolations(2, duration); + EXPECT_EQ(1ul, activeInterpolations->size()); + EXPECT_FLOAT_EQ(15, getInterpolableNumber(activeInterpolations->at(0))); +} + +} + diff --git a/chromium/third_party/WebKit/Source/core/animation/Keyframe.h b/chromium/third_party/WebKit/Source/core/animation/Keyframe.h new file mode 100644 index 00000000000..1f47c906117 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/Keyframe.h @@ -0,0 +1,102 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Keyframe_h +#define Keyframe_h + +#include "core/CSSPropertyNames.h" +#include "core/animation/AnimatableValue.h" +#include "core/animation/AnimationEffect.h" +#include "core/animation/AnimationNode.h" + +namespace WebCore { + +typedef HashSet<CSSPropertyID> PropertySet; + +class Element; + +// FIXME: Make Keyframe immutable +class Keyframe : public RefCountedWillBeGarbageCollectedFinalized<Keyframe> { +public: + virtual ~Keyframe() { } + + void setOffset(double offset) { m_offset = offset; } + double offset() const { return m_offset; } + + void setComposite(AnimationEffect::CompositeOperation composite) { m_composite = composite; } + AnimationEffect::CompositeOperation composite() const { return m_composite; } + + void setEasing(PassRefPtr<TimingFunction> easing) { m_easing = easing; } + TimingFunction* easing() const { return m_easing.get(); } + + static bool compareOffsets(const RefPtrWillBeMember<Keyframe>& a, const RefPtrWillBeMember<Keyframe>& b) + { + return a->offset() < b->offset(); + } + + virtual PropertySet properties() const = 0; + + virtual PassRefPtrWillBeRawPtr<Keyframe> clone() const = 0; + PassRefPtrWillBeRawPtr<Keyframe> cloneWithOffset(double offset) const + { + RefPtrWillBeRawPtr<Keyframe> theClone = clone(); + theClone->setOffset(offset); + return theClone.release(); + } + + virtual bool isAnimatableValueKeyframe() const { return false; } + virtual bool isStringKeyframe() const { return false; } + + virtual void trace(Visitor*) { } + + class PropertySpecificKeyframe : public NoBaseWillBeGarbageCollectedFinalized<PropertySpecificKeyframe> { + public: + virtual ~PropertySpecificKeyframe() { } + double offset() const { return m_offset; } + TimingFunction* easing() const { return m_easing.get(); } + AnimationEffect::CompositeOperation composite() const { return m_composite; } + virtual PassOwnPtrWillBeRawPtr<PropertySpecificKeyframe> cloneWithOffset(double offset) const = 0; + + virtual const PassRefPtrWillBeRawPtr<AnimatableValue> getAnimatableValue() const = 0; + + virtual bool isAnimatableValuePropertySpecificKeyframe() const { return false; } + virtual bool isStringPropertySpecificKeyframe() const { return false; } + + virtual PassOwnPtrWillBeRawPtr<PropertySpecificKeyframe> neutralKeyframe(double offset, PassRefPtr<TimingFunction> easing) const = 0; + virtual PassRefPtrWillBeRawPtr<Interpolation> createInterpolation(CSSPropertyID, WebCore::Keyframe::PropertySpecificKeyframe* end, Element*) const = 0; + + virtual void trace(Visitor*) { } + + protected: + PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, AnimationEffect::CompositeOperation); + + double m_offset; + RefPtr<TimingFunction> m_easing; + AnimationEffect::CompositeOperation m_composite; + }; + + virtual PassOwnPtrWillBeRawPtr<PropertySpecificKeyframe> createPropertySpecificKeyframe(CSSPropertyID) const = 0; + +protected: + Keyframe() + : m_offset(nullValue()) + , m_composite(AnimationEffect::CompositeReplace) + , m_easing(LinearTimingFunction::shared()) + { + } + Keyframe(double offset, AnimationEffect::CompositeOperation composite, PassRefPtr<TimingFunction> easing) + : m_offset(offset) + , m_composite(composite) + , m_easing(easing) + { + } + + double m_offset; + AnimationEffect::CompositeOperation m_composite; + RefPtr<TimingFunction> m_easing; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffect.cpp b/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffect.cpp deleted file mode 100644 index dd6ffa3a832..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffect.cpp +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/KeyframeAnimationEffect.h" - -#include "core/animation/TimedItem.h" -#include "wtf/text/StringHash.h" - -namespace { - -using namespace WebCore; - -class ReplaceCompositableValue : public AnimationEffect::CompositableValue { -public: - static PassRefPtr<ReplaceCompositableValue> create(const AnimatableValue* value) - { - return adoptRef(new ReplaceCompositableValue(value)); - } - virtual bool dependsOnUnderlyingValue() const - { - return false; - } - virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const - { - return PassRefPtr<AnimatableValue>(m_value); - } -private: - ReplaceCompositableValue(const AnimatableValue* value) - : m_value(const_cast<AnimatableValue*>(value)) - { - } - RefPtr<AnimatableValue> m_value; -}; - -class AddCompositableValue : public AnimationEffect::CompositableValue { -public: - static PassRefPtr<AddCompositableValue> create(const AnimatableValue* value) - { - return adoptRef(new AddCompositableValue(value)); - } - virtual bool dependsOnUnderlyingValue() const - { - return true; - } - virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const - { - return AnimatableValue::add(underlyingValue, m_value.get()); - } -private: - AddCompositableValue(const AnimatableValue* value) - : m_value(const_cast<AnimatableValue*>(value)) - { - } - RefPtr<AnimatableValue> m_value; -}; - -class BlendedCompositableValue : public AnimationEffect::CompositableValue { -public: - static PassRefPtr<BlendedCompositableValue> create(const AnimationEffect::CompositableValue* before, const AnimationEffect::CompositableValue* after, double fraction) - { - return adoptRef(new BlendedCompositableValue(before, after, fraction)); - } - virtual bool dependsOnUnderlyingValue() const - { - return m_dependsOnUnderlyingValue; - } - virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const - { - return AnimatableValue::interpolate(m_before->compositeOnto(underlyingValue).get(), m_after->compositeOnto(underlyingValue).get(), m_fraction); - } -private: - BlendedCompositableValue(const AnimationEffect::CompositableValue* before, const AnimationEffect::CompositableValue* after, double fraction) - : m_before(const_cast<AnimationEffect::CompositableValue*>(before)) - , m_after(const_cast<AnimationEffect::CompositableValue*>(after)) - , m_fraction(fraction) - , m_dependsOnUnderlyingValue(before->dependsOnUnderlyingValue() || after->dependsOnUnderlyingValue()) - { } - RefPtr<AnimationEffect::CompositableValue> m_before; - RefPtr<AnimationEffect::CompositableValue> m_after; - double m_fraction; - bool m_dependsOnUnderlyingValue; -}; - -} // namespace - - -namespace WebCore { - -Keyframe::Keyframe() - : m_offset(nullValue()) - , m_composite(AnimationEffect::CompositeReplace) -{ } - -Keyframe::Keyframe(const Keyframe& copyFrom) - : m_offset(copyFrom.m_offset) - , m_composite(copyFrom.m_composite) -{ - for (PropertyValueMap::const_iterator iter = copyFrom.m_propertyValues.begin(); iter != copyFrom.m_propertyValues.end(); ++iter) - setPropertyValue(iter->key, iter->value.get()); -} - -void Keyframe::setPropertyValue(CSSPropertyID property, const AnimatableValue* value) -{ - m_propertyValues.add(property, const_cast<AnimatableValue*>(value)); -} - -void Keyframe::clearPropertyValue(CSSPropertyID property) -{ - m_propertyValues.remove(property); -} - -const AnimatableValue* Keyframe::propertyValue(CSSPropertyID property) const -{ - ASSERT(m_propertyValues.contains(property)); - return m_propertyValues.get(property); -} - -PropertySet Keyframe::properties() const -{ - // This is not used in time-critical code, so we probably don't need to - // worry about caching this result. - PropertySet properties; - for (PropertyValueMap::const_iterator iter = m_propertyValues.begin(); iter != m_propertyValues.end(); ++iter) - properties.add(*iter.keys()); - return properties; -} - -PassRefPtr<Keyframe> Keyframe::cloneWithOffset(double offset) const -{ - RefPtr<Keyframe> theClone = clone(); - theClone->setOffset(offset); - return theClone.release(); -} - -KeyframeAnimationEffect::KeyframeAnimationEffect(const KeyframeVector& keyframes) - : m_keyframes(keyframes) -{ -} - -PropertySet KeyframeAnimationEffect::properties() const -{ - PropertySet result; - const KeyframeVector& frames = getFrames(); - if (!frames.size()) { - return result; - } - result = frames[0]->properties(); - for (size_t i = 1; i < frames.size(); i++) { - PropertySet extras = frames[i]->properties(); - for (PropertySet::const_iterator it = extras.begin(); it != extras.end(); ++it) { - result.add(*it); - } - } - return result; -} - -PassOwnPtr<AnimationEffect::CompositableValueList> KeyframeAnimationEffect::sample(int iteration, double fraction) const -{ - ASSERT(iteration >= 0); - ASSERT(!isNull(fraction)); - const_cast<KeyframeAnimationEffect*>(this)->ensureKeyframeGroups(); - OwnPtr<CompositableValueList> map = adoptPtr(new CompositableValueList()); - for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) - map->append(std::make_pair(iter->key, iter->value->sample(iteration, fraction))); - return map.release(); -} - -KeyframeAnimationEffect::KeyframeVector KeyframeAnimationEffect::normalizedKeyframes() const -{ - KeyframeVector keyframes = m_keyframes; - - // Set offsets at 0.0 and 1.0 at ends if unset. - if (keyframes.size() >= 2) { - Keyframe* firstKeyframe = keyframes.first().get(); - if (isNull(firstKeyframe->offset())) - firstKeyframe->setOffset(0.0); - } - if (keyframes.size() >= 1) { - Keyframe* lastKeyframe = keyframes.last().get(); - if (lastKeyframe && isNull(lastKeyframe->offset())) - lastKeyframe->setOffset(1.0); - } - - // FIXME: Distribute offsets where missing. - for (KeyframeVector::iterator iter = keyframes.begin(); iter != keyframes.end(); ++iter) - ASSERT(!isNull((*iter)->offset())); - - // Sort by offset. - std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets); - return keyframes; -} - -void KeyframeAnimationEffect::ensureKeyframeGroups() const -{ - if (m_keyframeGroups) - return; - - m_keyframeGroups = adoptPtr(new KeyframeGroupMap); - const KeyframeVector& keyframes = normalizedKeyframes(); - for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyframeIter != keyframes.end(); ++keyframeIter) { - const Keyframe* keyframe = keyframeIter->get(); - PropertySet keyframeProperties = keyframe->properties(); - for (PropertySet::const_iterator propertyIter = keyframeProperties.begin(); propertyIter != keyframeProperties.end(); ++propertyIter) { - CSSPropertyID property = *propertyIter; - KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(property); - if (groupIter == m_keyframeGroups->end()) { - KeyframeGroupMap::AddResult result = m_keyframeGroups->add(property, adoptPtr(new PropertySpecificKeyframeGroup)); - ASSERT(result.isNewEntry); - groupIter = result.iterator; - } - groupIter->value->appendKeyframe(adoptPtr( - new PropertySpecificKeyframe(keyframe->offset(), keyframe->propertyValue(property), keyframe->composite()))); - } - } - - // Add synthetic keyframes. - for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { - iter->value->addSyntheticKeyframeIfRequired(); - iter->value->removeRedundantKeyframes(); - } -} - - -KeyframeAnimationEffect::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, const AnimatableValue* value, CompositeOperation composite) - : m_offset(offset) - , m_value(composite == AnimationEffect::CompositeReplace ? - static_cast<PassRefPtr<CompositableValue> >(ReplaceCompositableValue::create(value)) : - static_cast<PassRefPtr<CompositableValue> >(AddCompositableValue::create(value))) -{ -} - -KeyframeAnimationEffect::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<CompositableValue> value) - : m_offset(offset) - , m_value(value) -{ - ASSERT(!isNull(m_offset)); -} - -PassOwnPtr<KeyframeAnimationEffect::PropertySpecificKeyframe> KeyframeAnimationEffect::PropertySpecificKeyframe::cloneWithOffset(double offset) const -{ - return adoptPtr(new PropertySpecificKeyframe(offset, PassRefPtr<CompositableValue>(m_value))); -} - - -void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnPtr<PropertySpecificKeyframe> keyframe) -{ - ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->offset()); - m_keyframes.append(keyframe); -} - -void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::removeRedundantKeyframes() -{ - // As an optimization, removes keyframes in the following categories, as - // they will never be used by sample(). - // - End keyframes with the same offset as their neighbor - // - Interior keyframes with the same offset as both their neighbors - // Note that synthetic keyframes must be added before this method is - // called. - ASSERT(m_keyframes.size() >= 2); - for (int i = m_keyframes.size() - 1; i >= 0; --i) { - double offset = m_keyframes[i]->offset(); - bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset() == offset; - bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.size() - 1) || m_keyframes[i + 1]->offset() == offset; - if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor) - m_keyframes.remove(i); - } - ASSERT(m_keyframes.size() >= 2); -} - -void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfRequired() -{ - ASSERT(!m_keyframes.isEmpty()); - double offset = m_keyframes.first()->offset(); - bool allOffsetsEqual = true; - for (PropertySpecificKeyframeVector::const_iterator iter = m_keyframes.begin() + 1; iter != m_keyframes.end(); ++iter) { - if ((*iter)->offset() != offset) { - allOffsetsEqual = false; - break; - } - } - if (!allOffsetsEqual) - return; - - if (!offset) - appendKeyframe(m_keyframes.first()->cloneWithOffset(1.0)); - else - m_keyframes.insert(0, adoptPtr(new PropertySpecificKeyframe(0.0, AnimatableValue::neutralValue(), CompositeAdd))); -} - -PassRefPtr<AnimationEffect::CompositableValue> KeyframeAnimationEffect::PropertySpecificKeyframeGroup::sample(int iteration, double offset) const -{ - // FIXME: Implement accumulation. - ASSERT_UNUSED(iteration, iteration >= 0); - ASSERT(!isNull(offset)); - - // Bail if offset is null, as this can lead to buffer overflow below. - if (isNull(offset)) - return const_cast<CompositableValue*>(m_keyframes.first()->value()); - - double minimumOffset = m_keyframes.first()->offset(); - double maximumOffset = m_keyframes.last()->offset(); - ASSERT(minimumOffset != maximumOffset); - - PropertySpecificKeyframeVector::const_iterator before; - PropertySpecificKeyframeVector::const_iterator after; - - // Note that this algorithm is simpler than that in the spec because we - // have removed keyframes with equal offsets in - // removeRedundantKeyframes(). - if (offset < minimumOffset) { - before = m_keyframes.begin(); - after = before + 1; - ASSERT((*before)->offset() > offset); - ASSERT((*after)->offset() > offset); - } else if (offset >= maximumOffset) { - after = m_keyframes.end() - 1; - before = after - 1; - ASSERT((*before)->offset() < offset); - ASSERT((*after)->offset() <= offset); - } else { - // FIXME: This is inefficient for large numbers of keyframes. Consider - // using binary search. - after = m_keyframes.begin(); - while ((*after)->offset() <= offset) - ++after; - before = after - 1; - ASSERT((*before)->offset() <= offset); - ASSERT((*after)->offset() > offset); - } - - if ((*before)->offset() == offset) - return const_cast<CompositableValue*>((*before)->value()); - if ((*after)->offset() == offset) - return const_cast<CompositableValue*>((*after)->value()); - return BlendedCompositableValue::create((*before)->value(), (*after)->value(), - (offset - (*before)->offset()) / ((*after)->offset() - (*before)->offset())); -} - -} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffect.h b/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffect.h deleted file mode 100644 index ee784299dc4..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffect.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef KeyframeAnimationEffect_h -#define KeyframeAnimationEffect_h - -#include "core/animation/AnimatableValue.h" -#include "core/animation/AnimationEffect.h" -#include "wtf/HashMap.h" -#include "wtf/HashSet.h" -#include "wtf/PassOwnPtr.h" -#include "wtf/PassRefPtr.h" -#include "wtf/RefCounted.h" -#include "wtf/Vector.h" - -namespace WebCore { - -typedef HashSet<CSSPropertyID> PropertySet; - -// Represents the keyframes set through the API. -class Keyframe : public RefCounted<Keyframe> { -public: - static PassRefPtr<Keyframe> create() - { - return adoptRef(new Keyframe); - } - static bool compareOffsets(const RefPtr<Keyframe>& a, const RefPtr<Keyframe>& b) - { - return a->offset() < b->offset(); - } - void setOffset(double offset) { m_offset = offset; } - double offset() const { return m_offset; } - void setComposite(AnimationEffect::CompositeOperation composite) { m_composite = composite; } - AnimationEffect::CompositeOperation composite() const { return m_composite; } - void setPropertyValue(CSSPropertyID, const AnimatableValue*); - void clearPropertyValue(CSSPropertyID); - const AnimatableValue* propertyValue(CSSPropertyID) const; - PropertySet properties() const; - PassRefPtr<Keyframe> clone() const { return adoptRef(new Keyframe(*this)); } - PassRefPtr<Keyframe> cloneWithOffset(double offset) const; -private: - Keyframe(); - Keyframe(const Keyframe&); - double m_offset; - AnimationEffect::CompositeOperation m_composite; - typedef HashMap<CSSPropertyID, RefPtr<AnimatableValue> > PropertyValueMap; - PropertyValueMap m_propertyValues; -}; - -class KeyframeAnimationEffect : public AnimationEffect { -public: - class PropertySpecificKeyframe; - typedef Vector<RefPtr<Keyframe> > KeyframeVector; - typedef Vector<OwnPtr<KeyframeAnimationEffect::PropertySpecificKeyframe> > PropertySpecificKeyframeVector; - // FIXME: Implement accumulation. - static PassRefPtr<KeyframeAnimationEffect> create(const KeyframeVector& keyframes) - { - return adoptRef(new KeyframeAnimationEffect(keyframes)); - } - - virtual bool affects(CSSPropertyID property) OVERRIDE - { - ensureKeyframeGroups(); - return m_keyframeGroups->contains(property); - } - - // AnimationEffect implementation. - virtual PassOwnPtr<CompositableValueList> sample(int iteration, double fraction) const OVERRIDE; - - // FIXME: Implement setFrames() - const KeyframeVector& getFrames() const { return m_keyframes; } - - virtual bool isKeyframeAnimationEffect() const OVERRIDE { return true; } - - PropertySet properties() const; - - class PropertySpecificKeyframe { - public: - PropertySpecificKeyframe(double offset, const AnimatableValue*, CompositeOperation); - double offset() const { return m_offset; } - const CompositableValue* value() const { return m_value.get(); } - PassOwnPtr<PropertySpecificKeyframe> cloneWithOffset(double offset) const; - private: - // Used by cloneWithOffset(). - PropertySpecificKeyframe(double offset, PassRefPtr<CompositableValue>); - double m_offset; - RefPtr<CompositableValue> m_value; - }; - - class PropertySpecificKeyframeGroup { - public: - void appendKeyframe(PassOwnPtr<PropertySpecificKeyframe>); - PassRefPtr<CompositableValue> sample(int iteration, double offset) const; - const PropertySpecificKeyframeVector& keyframes() const { return m_keyframes; } - private: - PropertySpecificKeyframeVector m_keyframes; - void removeRedundantKeyframes(); - void addSyntheticKeyframeIfRequired(); - - friend class KeyframeAnimationEffect; - }; - - const PropertySpecificKeyframeVector& getPropertySpecificKeyframes(CSSPropertyID id) const - { - ensureKeyframeGroups(); - return m_keyframeGroups->get(id)->keyframes(); - } - -private: - KeyframeAnimationEffect(const KeyframeVector& keyframes); - - KeyframeVector normalizedKeyframes() const; - // Lazily computes the groups of property-specific keyframes. - void ensureKeyframeGroups() const; - - KeyframeVector m_keyframes; - // The spec describes filtering the normalized keyframes at sampling time - // to get the 'property-specific keyframes'. For efficiency, we cache the - // property-specific lists. - typedef HashMap<CSSPropertyID, OwnPtr<PropertySpecificKeyframeGroup> > KeyframeGroupMap; - mutable OwnPtr<KeyframeGroupMap> m_keyframeGroups; -}; - -DEFINE_TYPE_CASTS(KeyframeAnimationEffect, AnimationEffect, value, value->isKeyframeAnimationEffect(), value.isKeyframeAnimationEffect()); - -} // namespace WebCore - -#endif // KeyframeAnimationEffect_h diff --git a/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffectTest.cpp b/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffectTest.cpp deleted file mode 100644 index 4a493bd4f27..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/KeyframeAnimationEffectTest.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/KeyframeAnimationEffect.h" - -#include "core/animation/AnimatableLength.h" -#include "core/animation/AnimatableUnknown.h" -#include "core/css/CSSPrimitiveValue.h" -#include <gtest/gtest.h> - -using namespace WebCore; - -namespace { - -AnimatableValue* unknownAnimatableValue(double n) -{ - return AnimatableUnknown::create(CSSPrimitiveValue::create(n, CSSPrimitiveValue::CSS_UNKNOWN).get()).leakRef(); -} - -AnimatableValue* pixelAnimatableValue(double n) -{ - return AnimatableLength::create(CSSPrimitiveValue::create(n, CSSPrimitiveValue::CSS_PX).get()).leakRef(); -} - -KeyframeAnimationEffect::KeyframeVector keyframesAtZeroAndOne(AnimatableValue* zeroValue, AnimatableValue* oneValue) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(2); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, zeroValue); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(1.0); - keyframes[1]->setPropertyValue(CSSPropertyLeft, oneValue); - return keyframes; -} - -void expectDoubleValue(double expectedValue, PassRefPtr<AnimatableValue> value) -{ - ASSERT_TRUE(value->isLength() || value->isUnknown()); - - double actualValue; - if (value->isLength()) - actualValue = toCSSPrimitiveValue(toAnimatableLength(value.get())->toCSSValue().get())->getDoubleValue(); - else - actualValue = toCSSPrimitiveValue(toAnimatableUnknown(value.get())->toCSSValue().get())->getDoubleValue(); - - EXPECT_FLOAT_EQ(static_cast<float>(expectedValue), actualValue); -} - - -TEST(AnimationKeyframeAnimationEffectTest, BasicOperation) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(unknownAnimatableValue(3.0), unknownAnimatableValue(5.0)); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - OwnPtr<AnimationEffect::CompositableValueList> values = effect->sample(0, 0.6); - ASSERT_EQ(1UL, values->size()); - EXPECT_EQ(CSSPropertyLeft, values->at(0).first); - expectDoubleValue(5.0, values->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, CompositeReplaceNonInterpolable) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(unknownAnimatableValue(3.0), unknownAnimatableValue(5.0)); - keyframes[0]->setComposite(AnimationEffect::CompositeReplace); - keyframes[1]->setComposite(AnimationEffect::CompositeReplace); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(5.0, effect->sample(0, 0.6)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, CompositeReplace) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); - keyframes[0]->setComposite(AnimationEffect::CompositeReplace); - keyframes[1]->setComposite(AnimationEffect::CompositeReplace); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(3.0 * 0.4 + 5.0 * 0.6, effect->sample(0, 0.6)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, CompositeAdd) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); - keyframes[0]->setComposite(AnimationEffect::CompositeAdd); - keyframes[1]->setComposite(AnimationEffect::CompositeAdd); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue((7.0 + 3.0) * 0.4 + (7.0 + 5.0) * 0.6, effect->sample(0, 0.6)->at(0).second->compositeOnto(pixelAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, ExtrapolateReplaceNonInterpolable) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(unknownAnimatableValue(3.0), unknownAnimatableValue(5.0)); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - keyframes[0]->setComposite(AnimationEffect::CompositeReplace); - keyframes[1]->setComposite(AnimationEffect::CompositeReplace); - expectDoubleValue(5.0, effect->sample(0, 1.6)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, ExtrapolateReplace) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - keyframes[0]->setComposite(AnimationEffect::CompositeReplace); - keyframes[1]->setComposite(AnimationEffect::CompositeReplace); - expectDoubleValue(3.0 * -0.6 + 5.0 * 1.6, effect->sample(0, 1.6)->at(0).second->compositeOnto(pixelAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, ExtrapolateAdd) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); - keyframes[0]->setComposite(AnimationEffect::CompositeAdd); - keyframes[1]->setComposite(AnimationEffect::CompositeAdd); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue((7.0 + 3.0) * -0.6 + (7.0 + 5.0) * 1.6, effect->sample(0, 1.6)->at(0).second->compositeOnto(pixelAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, ZeroKeyframes) -{ - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(KeyframeAnimationEffect::KeyframeVector()); - EXPECT_TRUE(effect->sample(0, 0.5)->isEmpty()); -} - -TEST(AnimationKeyframeAnimationEffectTest, SingleKeyframeAtOffsetZero) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(1); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(3.0, effect->sample(0, 0.6)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, SingleKeyframeAtOffsetOne) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(1); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(1.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(5.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(7.0 * 0.4 + 5.0 * 0.6, effect->sample(0, 0.6)->at(0).second->compositeOnto(pixelAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, MoreThanTwoKeyframes) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(3); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0)); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(0.5); - keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0)); - keyframes[2] = Keyframe::create(); - keyframes[2]->setOffset(1.0); - keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(4.0, effect->sample(0, 0.3)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - expectDoubleValue(5.0, effect->sample(0, 0.8)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, EndKeyframeOffsetsUnspecified) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(3); - keyframes[0] = Keyframe::create(); - keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0)); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(0.5); - keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0)); - keyframes[2] = Keyframe::create(); - keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(3.0, effect->sample(0, 0.1)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - expectDoubleValue(4.0, effect->sample(0, 0.6)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - expectDoubleValue(5.0, effect->sample(0, 0.9)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, SampleOnKeyframe) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(3); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0)); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(0.5); - keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0)); - keyframes[2] = Keyframe::create(); - keyframes[2]->setOffset(1.0); - keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(3.0, effect->sample(0, 0.0)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - expectDoubleValue(4.0, effect->sample(0, 0.5)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - expectDoubleValue(5.0, effect->sample(0, 1.0)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -// Note that this tests an implementation detail, not behaviour defined by the spec. -TEST(AnimationKeyframeAnimationEffectTest, SampleReturnsSameAnimatableValueInstance) -{ - AnimatableValue* threePixelsValue = unknownAnimatableValue(3.0); - AnimatableValue* fourPixelsValue = unknownAnimatableValue(4.0); - AnimatableValue* fivePixelsValue = unknownAnimatableValue(5.0); - - KeyframeAnimationEffect::KeyframeVector keyframes(3); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, threePixelsValue); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(0.5); - keyframes[1]->setPropertyValue(CSSPropertyLeft, fourPixelsValue); - keyframes[2] = Keyframe::create(); - keyframes[2]->setOffset(1.0); - keyframes[2]->setPropertyValue(CSSPropertyLeft, fivePixelsValue); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - EXPECT_EQ(threePixelsValue, effect->sample(0, 0.0)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_EQ(threePixelsValue, effect->sample(0, 0.1)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_EQ(fourPixelsValue, effect->sample(0, 0.4)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_EQ(fourPixelsValue, effect->sample(0, 0.5)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_EQ(fourPixelsValue, effect->sample(0, 0.6)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_EQ(fivePixelsValue, effect->sample(0, 0.9)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_EQ(fivePixelsValue, effect->sample(0, 1.0)->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, MultipleKeyframesWithSameOffset) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(7); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.1); - keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(1.0)); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(0.1); - keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(2.0)); - keyframes[2] = Keyframe::create(); - keyframes[2]->setOffset(0.5); - keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0)); - keyframes[3] = Keyframe::create(); - keyframes[3]->setOffset(0.5); - keyframes[3]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0)); - keyframes[4] = Keyframe::create(); - keyframes[4]->setOffset(0.5); - keyframes[4]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0)); - keyframes[5] = Keyframe::create(); - keyframes[5]->setOffset(0.9); - keyframes[5]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(6.0)); - keyframes[6] = Keyframe::create(); - keyframes[6]->setOffset(0.9); - keyframes[6]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(7.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(2.0, effect->sample(0, 0.0)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); - expectDoubleValue(2.0, effect->sample(0, 0.2)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); - expectDoubleValue(3.0, effect->sample(0, 0.4)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); - expectDoubleValue(5.0, effect->sample(0, 0.5)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); - expectDoubleValue(5.0, effect->sample(0, 0.6)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); - expectDoubleValue(6.0, effect->sample(0, 0.8)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); - expectDoubleValue(6.0, effect->sample(0, 1.0)->at(0).second->compositeOnto(unknownAnimatableValue(8.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, PerKeyframeComposite) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(2); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(3.0)); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(1.0); - keyframes[1]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(5.0)); - keyframes[1]->setComposite(AnimationEffect::CompositeAdd); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(3.0 * 0.4 + (7.0 + 5.0) * 0.6, effect->sample(0, 0.6)->at(0).second->compositeOnto(pixelAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, MultipleProperties) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(2); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0)); - keyframes[0]->setPropertyValue(CSSPropertyRight, unknownAnimatableValue(4.0)); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(1.0); - keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0)); - keyframes[1]->setPropertyValue(CSSPropertyRight, unknownAnimatableValue(6.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - OwnPtr<AnimationEffect::CompositableValueList> values = effect->sample(0, 0.6); - ASSERT_EQ(2UL, values->size()); - EXPECT_TRUE(values->at(0).first == CSSPropertyLeft); - expectDoubleValue(5.0, values->at(0).second->compositeOnto(unknownAnimatableValue(7.0))); - EXPECT_TRUE(values->at(1).first == CSSPropertyRight); - expectDoubleValue(6.0, values->at(1).second->compositeOnto(unknownAnimatableValue(7.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, RecompositeCompositableValue) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); - keyframes[0]->setComposite(AnimationEffect::CompositeAdd); - keyframes[1]->setComposite(AnimationEffect::CompositeAdd); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - OwnPtr<AnimationEffect::CompositableValueList> values = effect->sample(0, 0.6); - expectDoubleValue((7.0 + 3.0) * 0.4 + (7.0 + 5.0) * 0.6, values->at(0).second->compositeOnto(pixelAnimatableValue(7.0))); - expectDoubleValue((9.0 + 3.0) * 0.4 + (9.0 + 5.0) * 0.6, values->at(0).second->compositeOnto(pixelAnimatableValue(9.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, MultipleIterations) -{ - KeyframeAnimationEffect::KeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(1.0), pixelAnimatableValue(3.0)); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - expectDoubleValue(2.0, effect->sample(0, 0.5)->at(0).second->compositeOnto(unknownAnimatableValue(0.0))); - expectDoubleValue(2.0, effect->sample(1, 0.5)->at(0).second->compositeOnto(unknownAnimatableValue(0.0))); - expectDoubleValue(2.0, effect->sample(2, 0.5)->at(0).second->compositeOnto(unknownAnimatableValue(0.0))); -} - -TEST(AnimationKeyframeAnimationEffectTest, DependsOnUnderlyingValue) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(3); - keyframes[0] = Keyframe::create(); - keyframes[0]->setOffset(0.0); - keyframes[0]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(1.0)); - keyframes[0]->setComposite(AnimationEffect::CompositeAdd); - keyframes[1] = Keyframe::create(); - keyframes[1]->setOffset(0.5); - keyframes[1]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(1.0)); - keyframes[2] = Keyframe::create(); - keyframes[2]->setOffset(1.0); - keyframes[2]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(1.0)); - - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - EXPECT_TRUE(effect->sample(0, 0)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_TRUE(effect->sample(0, 0.1)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_TRUE(effect->sample(0, 0.25)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_TRUE(effect->sample(0, 0.4)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_FALSE(effect->sample(0, 0.5)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_FALSE(effect->sample(0, 0.6)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_FALSE(effect->sample(0, 0.75)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_FALSE(effect->sample(0, 0.8)->at(0).second->dependsOnUnderlyingValue()); - EXPECT_FALSE(effect->sample(0, 1)->at(0).second->dependsOnUnderlyingValue()); -} - -TEST(AnimationKeyframeAnimationEffectTest, ToKeyframeAnimationEffect) -{ - KeyframeAnimationEffect::KeyframeVector keyframes(0); - RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes); - - AnimationEffect* baseEffect = effect.get(); - EXPECT_TRUE(toKeyframeAnimationEffect(baseEffect)); -} - -} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModel.cpp b/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModel.cpp new file mode 100644 index 00000000000..c9f2a3fbbe9 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModel.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/KeyframeEffectModel.h" + +#include "core/StylePropertyShorthand.h" +#include "core/animation/AnimationNode.h" +#include "wtf/text/StringHash.h" + +namespace WebCore { + +PropertySet KeyframeEffectModelBase::properties() const +{ + PropertySet result; + if (!m_keyframes.size()) { + return result; + } + result = m_keyframes[0]->properties(); + for (size_t i = 1; i < m_keyframes.size(); i++) { + PropertySet extras = m_keyframes[i]->properties(); + for (PropertySet::const_iterator it = extras.begin(); it != extras.end(); ++it) { + result.add(*it); + } + } + return result; +} + +PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > KeyframeEffectModelBase::sample(int iteration, double fraction, double iterationDuration) const +{ + ASSERT(iteration >= 0); + ASSERT(!isNull(fraction)); + ensureKeyframeGroups(); + ensureInterpolationEffect(); + + return m_interpolationEffect->getActiveInterpolations(fraction, iterationDuration); +} + +KeyframeEffectModelBase::KeyframeVector KeyframeEffectModelBase::normalizedKeyframes(const KeyframeVector& keyframes) +{ + // keyframes [beginIndex, endIndex) will remain after removing all keyframes if they are not + // loosely sorted by offset, and after removing keyframes with positional offset outide [0, 1]. + size_t beginIndex = 0; + size_t endIndex = keyframes.size(); + + // Becomes the most recent keyframe with an explicit offset. + size_t lastIndex = endIndex; + double lastOffset = std::numeric_limits<double>::quiet_NaN(); + + for (size_t i = 0; i < keyframes.size(); ++i) { + double offset = keyframes[i]->offset(); + if (!isNull(offset)) { + if (lastIndex < i && offset < lastOffset) { + // The keyframes are not loosely sorted by offset. Exclude all. + endIndex = beginIndex; + break; + } + + if (offset < 0) { + // Remove all keyframes up to and including this keyframe. + beginIndex = i + 1; + } else if (offset > 1) { + // Remove all keyframes from this keyframe onwards. Note we must complete our checking + // that the keyframes are loosely sorted by offset, so we can't exit the loop early. + endIndex = std::min(i, endIndex); + } + + lastIndex = i; + lastOffset = offset; + } + } + + KeyframeVector result; + if (beginIndex != endIndex) { + result.reserveCapacity(endIndex - beginIndex); + for (size_t i = beginIndex; i < endIndex; ++i) { + result.append(keyframes[i]->clone()); + } + + if (isNull(result[result.size() - 1]->offset())) + result[result.size() - 1]->setOffset(1); + + if (result.size() > 1 && isNull(result[0]->offset())) + result[0]->setOffset(0); + + lastIndex = 0; + lastOffset = result[0]->offset(); + for (size_t i = 1; i < result.size(); ++i) { + double offset = result[i]->offset(); + if (!isNull(offset)) { + if (lastIndex + 1 < i) { + for (size_t j = 1; j < i - lastIndex; ++j) + result[lastIndex + j]->setOffset(lastOffset + (offset - lastOffset) * j / (i - lastIndex)); + } + lastIndex = i; + lastOffset = offset; + } + } + } + return result; +} + + +void KeyframeEffectModelBase::ensureKeyframeGroups() const +{ + if (m_keyframeGroups) + return; + + m_keyframeGroups = adoptPtrWillBeNoop(new KeyframeGroupMap); + const KeyframeVector keyframes = normalizedKeyframes(getFrames()); + for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyframeIter != keyframes.end(); ++keyframeIter) { + const Keyframe* keyframe = keyframeIter->get(); + PropertySet keyframeProperties = keyframe->properties(); + for (PropertySet::const_iterator propertyIter = keyframeProperties.begin(); propertyIter != keyframeProperties.end(); ++propertyIter) { + CSSPropertyID property = *propertyIter; + ASSERT_WITH_MESSAGE(!isExpandedShorthand(property), "Web Animations: Encountered shorthand CSS property (%d) in normalized keyframes.", property); + KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(property); + PropertySpecificKeyframeGroup* group; + if (groupIter == m_keyframeGroups->end()) + group = m_keyframeGroups->add(property, adoptPtrWillBeNoop(new PropertySpecificKeyframeGroup)).storedValue->value.get(); + else + group = groupIter->value.get(); + + group->appendKeyframe(keyframe->createPropertySpecificKeyframe(property)); + } + } + + // Add synthetic keyframes. + for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { + iter->value->addSyntheticKeyframeIfRequired(this); + iter->value->removeRedundantKeyframes(); + } +} + +void KeyframeEffectModelBase::ensureInterpolationEffect(Element* element) const +{ + if (m_interpolationEffect) + return; + m_interpolationEffect = InterpolationEffect::create(); + + for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { + const PropertySpecificKeyframeVector& keyframes = iter->value->keyframes(); + ASSERT(keyframes[0]->composite() == AnimationEffect::CompositeReplace); + for (size_t i = 0; i < keyframes.size() - 1; i++) { + ASSERT(keyframes[i + 1]->composite() == AnimationEffect::CompositeReplace); + double applyFrom = i ? keyframes[i]->offset() : (-std::numeric_limits<double>::infinity()); + double applyTo = i == keyframes.size() - 2 ? std::numeric_limits<double>::infinity() : keyframes[i + 1]->offset(); + if (applyTo == 1) + applyTo = std::numeric_limits<double>::infinity(); + + m_interpolationEffect->addInterpolation(keyframes[i]->createInterpolation(iter->key, keyframes[i + 1].get(), element), + keyframes[i]->easing(), keyframes[i]->offset(), keyframes[i + 1]->offset(), applyFrom, applyTo); + } + } +} + +bool KeyframeEffectModelBase::isReplaceOnly() +{ + ensureKeyframeGroups(); + for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { + const PropertySpecificKeyframeVector& keyframeVector = iter->value->keyframes(); + for (size_t i = 0; i < keyframeVector.size(); ++i) { + if (keyframeVector[i]->composite() != AnimationEffect::CompositeReplace) + return false; + } + } + return true; +} + +void KeyframeEffectModelBase::trace(Visitor* visitor) +{ + visitor->trace(m_keyframes); + visitor->trace(m_interpolationEffect); +#if ENABLE_OILPAN + visitor->trace(m_keyframeGroups); +#endif + AnimationEffect::trace(visitor); +} + +Keyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, AnimationEffect::CompositeOperation composite) + : m_offset(offset) + , m_easing(easing) + , m_composite(composite) +{ +} + +void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> keyframe) +{ + ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->offset()); + m_keyframes.append(keyframe); +} + +void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::removeRedundantKeyframes() +{ + // As an optimization, removes keyframes in the following categories, as + // they will never be used by sample(). + // - End keyframes with the same offset as their neighbor + // - Interior keyframes with the same offset as both their neighbors + // Note that synthetic keyframes must be added before this method is + // called. + ASSERT(m_keyframes.size() >= 2); + for (int i = m_keyframes.size() - 1; i >= 0; --i) { + double offset = m_keyframes[i]->offset(); + bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset() == offset; + bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.size() - 1) || m_keyframes[i + 1]->offset() == offset; + if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor) + m_keyframes.remove(i); + } + ASSERT(m_keyframes.size() >= 2); +} + +void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfRequired(const KeyframeEffectModelBase* context) +{ + ASSERT(!m_keyframes.isEmpty()); + if (m_keyframes.first()->offset() != 0.0) + m_keyframes.insert(0, m_keyframes.first()->neutralKeyframe(0, nullptr)); + if (m_keyframes.last()->offset() != 1.0) + appendKeyframe(m_keyframes.last()->neutralKeyframe(1, nullptr)); +} + +void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::trace(Visitor* visitor) +{ +#if ENABLE(OILPAN) + visitor->trace(m_keyframes); +#endif +} + +} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h b/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h new file mode 100644 index 00000000000..34feab36d91 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KeyframeEffectModel_h +#define KeyframeEffectModel_h + +#include "core/animation/AnimatableValueKeyframe.h" +#include "core/animation/AnimationEffect.h" +#include "core/animation/AnimationNode.h" +#include "core/animation/InterpolationEffect.h" +#include "core/animation/StringKeyframe.h" +#include "platform/animation/TimingFunction.h" +#include "platform/heap/Handle.h" +#include "wtf/HashMap.h" +#include "wtf/HashSet.h" +#include "wtf/PassOwnPtr.h" +#include "wtf/PassRefPtr.h" +#include "wtf/RefCounted.h" +#include "wtf/Vector.h" + +namespace WebCore { + +class Element; +class KeyframeEffectModelTest; + +class KeyframeEffectModelBase : public AnimationEffect { +public: + // FIXME: Implement accumulation. + + typedef WillBeHeapVector<OwnPtrWillBeMember<Keyframe::PropertySpecificKeyframe> > PropertySpecificKeyframeVector; + class PropertySpecificKeyframeGroup : public NoBaseWillBeGarbageCollected<PropertySpecificKeyframeGroup> { + public: + void appendKeyframe(PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe>); + const PropertySpecificKeyframeVector& keyframes() const { return m_keyframes; } + + void trace(Visitor*); + + private: + void removeRedundantKeyframes(); + void addSyntheticKeyframeIfRequired(const KeyframeEffectModelBase* context); + + PropertySpecificKeyframeVector m_keyframes; + + friend class KeyframeEffectModelBase; + }; + + bool isReplaceOnly(); + + PropertySet properties() const; + + typedef WillBeHeapVector<RefPtrWillBeMember<Keyframe> > KeyframeVector; + const KeyframeVector& getFrames() const { return m_keyframes; } + // FIXME: Implement setFrames() + + const PropertySpecificKeyframeVector& getPropertySpecificKeyframes(CSSPropertyID id) const + { + ensureKeyframeGroups(); + return m_keyframeGroups->get(id)->keyframes(); + } + + // AnimationEffect implementation. + virtual PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample(int iteration, double fraction, double iterationDuration) const OVERRIDE; + + virtual bool isKeyframeEffectModel() const OVERRIDE { return true; } + + virtual bool isAnimatableValueKeyframeEffectModel() const { return false; } + virtual bool isStringKeyframeEffectModel() const { return false; } + + virtual void trace(Visitor*) OVERRIDE; + + // FIXME: This is a hack used to resolve CSSValues to AnimatableValues while we have a valid handle on an element. + // This should be removed once StringKeyframes no longer uses InterpolableAnimatableValues. + void forceConversionsToAnimatableValues(Element* element) + { + ensureKeyframeGroups(); + ensureInterpolationEffect(element); + } + +protected: + static KeyframeVector normalizedKeyframes(const KeyframeVector& keyframes); + + // Lazily computes the groups of property-specific keyframes. + void ensureKeyframeGroups() const; + void ensureInterpolationEffect(Element* = 0) const; + + KeyframeVector m_keyframes; + // The spec describes filtering the normalized keyframes at sampling time + // to get the 'property-specific keyframes'. For efficiency, we cache the + // property-specific lists. + typedef WillBeHeapHashMap<CSSPropertyID, OwnPtrWillBeMember<PropertySpecificKeyframeGroup> > KeyframeGroupMap; + mutable OwnPtrWillBeMember<KeyframeGroupMap> m_keyframeGroups; + mutable RefPtrWillBeMember<InterpolationEffect> m_interpolationEffect; + + friend class KeyframeEffectModelTest; + + bool affects(CSSPropertyID property) + { + ensureKeyframeGroups(); + return m_keyframeGroups->contains(property); + } +}; + +template <class Keyframe> +class KeyframeEffectModel FINAL : public KeyframeEffectModelBase { +public: + typedef WillBeHeapVector<RefPtrWillBeMember<Keyframe> > KeyframeVector; + static PassRefPtrWillBeRawPtr<KeyframeEffectModel<Keyframe> > create(const KeyframeVector& keyframes) { return adoptRefWillBeNoop(new KeyframeEffectModel(keyframes)); } + +private: + KeyframeEffectModel(const KeyframeVector& keyframes) + { + m_keyframes.appendVector(keyframes); + } + + virtual bool isAnimatableValueKeyframeEffectModel() const { return false; } + virtual bool isStringKeyframeEffectModel() const { return false; } + +}; + +typedef KeyframeEffectModelBase::KeyframeVector KeyframeVector; +typedef KeyframeEffectModelBase::PropertySpecificKeyframeVector PropertySpecificKeyframeVector; + +typedef KeyframeEffectModel<AnimatableValueKeyframe> AnimatableValueKeyframeEffectModel; +typedef AnimatableValueKeyframeEffectModel::KeyframeVector AnimatableValueKeyframeVector; +typedef AnimatableValueKeyframeEffectModel::PropertySpecificKeyframeVector AnimatableValuePropertySpecificKeyframeVector; + +typedef KeyframeEffectModel<StringKeyframe> StringKeyframeEffectModel; +typedef StringKeyframeEffectModel::KeyframeVector StringKeyframeVector; +typedef StringKeyframeEffectModel::PropertySpecificKeyframeVector StringPropertySpecificKeyframeVector; + +DEFINE_TYPE_CASTS(KeyframeEffectModelBase, AnimationEffect, value, value->isKeyframeEffectModel(), value.isKeyframeEffectModel()); +DEFINE_TYPE_CASTS(AnimatableValueKeyframeEffectModel, KeyframeEffectModelBase, value, value->isAnimatableValueKeyframeEffectModel(), value.isAnimatableValueKeyframeEffectModel()); + +inline const AnimatableValueKeyframeEffectModel* toAnimatableValueKeyframeEffectModel(const AnimationEffect* base) +{ + return toAnimatableValueKeyframeEffectModel(toKeyframeEffectModelBase(base)); +} + +inline AnimatableValueKeyframeEffectModel* toAnimatableValueKeyframeEffectModel(AnimationEffect* base) +{ + return toAnimatableValueKeyframeEffectModel(toKeyframeEffectModelBase(base)); +} + +template <> +inline bool KeyframeEffectModel<AnimatableValueKeyframe>::isAnimatableValueKeyframeEffectModel() const { return true; } + +template <> +inline bool KeyframeEffectModel<StringKeyframe>::isStringKeyframeEffectModel() const { return true; } + +} // namespace WebCore + +#endif // KeyframeEffectModel_h diff --git a/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModelTest.cpp b/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModelTest.cpp new file mode 100644 index 00000000000..7287f9ac16f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/KeyframeEffectModelTest.cpp @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "core/animation/KeyframeEffectModel.h" + +#include "core/animation/AnimatableLength.h" +#include "core/animation/AnimatableUnknown.h" +#include "core/animation/interpolation/LegacyStyleInterpolation.h" +#include "core/css/CSSPrimitiveValue.h" +#include "core/css/parser/BisonCSSParser.h" +#include "core/css/resolver/CSSToStyleMap.h" +#include <gtest/gtest.h> + +using namespace WebCore; + +namespace { + +const double duration = 1.0; + +PassRefPtrWillBeRawPtr<AnimatableValue> unknownAnimatableValue(double n) +{ + return AnimatableUnknown::create(CSSPrimitiveValue::create(n, CSSPrimitiveValue::CSS_UNKNOWN).get()); +} + +PassRefPtrWillBeRawPtr<AnimatableValue> pixelAnimatableValue(double n) +{ + return AnimatableLength::create(Length(n, Fixed), 1); +} + +AnimatableValueKeyframeVector keyframesAtZeroAndOne(PassRefPtrWillBeRawPtr<AnimatableValue> zeroValue, PassRefPtrWillBeRawPtr<AnimatableValue> oneValue) +{ + AnimatableValueKeyframeVector keyframes(2); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, zeroValue.get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(1.0); + keyframes[1]->setPropertyValue(CSSPropertyLeft, oneValue.get()); + return keyframes; +} + +void expectProperty(CSSPropertyID property, PassRefPtrWillBeRawPtr<Interpolation> interpolationValue) +{ + LegacyStyleInterpolation* interpolation = toLegacyStyleInterpolation(interpolationValue.get()); + ASSERT_EQ(property, interpolation->id()); +} + +void expectDoubleValue(double expectedValue, PassRefPtrWillBeRawPtr<Interpolation> interpolationValue) +{ + LegacyStyleInterpolation* interpolation = toLegacyStyleInterpolation(interpolationValue.get()); + RefPtrWillBeRawPtr<AnimatableValue> value = interpolation->currentValue(); + + ASSERT_TRUE(value->isLength() || value->isUnknown()); + + double actualValue; + if (value->isLength()) + actualValue = toAnimatableLength(value.get())->length(1, ValueRangeAll).value(); + else + actualValue = toCSSPrimitiveValue(toAnimatableUnknown(value.get())->toCSSValue().get())->getDoubleValue(); + + EXPECT_FLOAT_EQ(static_cast<float>(expectedValue), actualValue); +} + +Interpolation* findValue(WillBeHeapVector<RefPtrWillBeMember<Interpolation> >& values, CSSPropertyID id) +{ + for (size_t i = 0; i < values.size(); ++i) { + LegacyStyleInterpolation* value = toLegacyStyleInterpolation(values.at(i).get()); + if (value->id() == id) + return value; + } + return 0; +} + + +TEST(AnimationKeyframeEffectModel, BasicOperation) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(unknownAnimatableValue(3.0), unknownAnimatableValue(5.0)); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > values = effect->sample(0, 0.6, duration); + ASSERT_EQ(1UL, values->size()); + expectProperty(CSSPropertyLeft, values->at(0)); + expectDoubleValue(5.0, values->at(0)); +} + +TEST(AnimationKeyframeEffectModel, CompositeReplaceNonInterpolable) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(unknownAnimatableValue(3.0), unknownAnimatableValue(5.0)); + keyframes[0]->setComposite(AnimationEffect::CompositeReplace); + keyframes[1]->setComposite(AnimationEffect::CompositeReplace); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(5.0, effect->sample(0, 0.6, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, CompositeReplace) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + keyframes[0]->setComposite(AnimationEffect::CompositeReplace); + keyframes[1]->setComposite(AnimationEffect::CompositeReplace); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(3.0 * 0.4 + 5.0 * 0.6, effect->sample(0, 0.6, duration)->at(0)); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_CompositeAdd) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + keyframes[0]->setComposite(AnimationEffect::CompositeAdd); + keyframes[1]->setComposite(AnimationEffect::CompositeAdd); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue((7.0 + 3.0) * 0.4 + (7.0 + 5.0) * 0.6, effect->sample(0, 0.6, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, CompositeEaseIn) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + RefPtrWillBeRawPtr<CSSValue> timingFunction = BisonCSSParser::parseAnimationTimingFunctionValue("ease-in"); + keyframes[0]->setComposite(AnimationEffect::CompositeReplace); + keyframes[0]->setEasing(CSSToStyleMap::mapAnimationTimingFunction(timingFunction.get(), true)); + keyframes[1]->setComposite(AnimationEffect::CompositeReplace); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(3.8579516, effect->sample(0, 0.6, duration)->at(0)); + expectDoubleValue(3.8582394, effect->sample(0, 0.6, duration * 100)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, CompositeCubicBezier) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + RefPtrWillBeRawPtr<CSSValue> timingFunction = BisonCSSParser::parseAnimationTimingFunctionValue("cubic-bezier(0.42, 0, 0.58, 1)"); + keyframes[0]->setComposite(AnimationEffect::CompositeReplace); + keyframes[0]->setEasing(CSSToStyleMap::mapAnimationTimingFunction(timingFunction.get(), true)); + keyframes[1]->setComposite(AnimationEffect::CompositeReplace); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(4.3363357, effect->sample(0, 0.6, duration)->at(0)); + expectDoubleValue(4.3362322, effect->sample(0, 0.6, duration * 1000)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, ExtrapolateReplaceNonInterpolable) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(unknownAnimatableValue(3.0), unknownAnimatableValue(5.0)); + keyframes[0]->setComposite(AnimationEffect::CompositeReplace); + keyframes[1]->setComposite(AnimationEffect::CompositeReplace); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(5.0, effect->sample(0, 1.6, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, ExtrapolateReplace) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + keyframes[0]->setComposite(AnimationEffect::CompositeReplace); + keyframes[1]->setComposite(AnimationEffect::CompositeReplace); + expectDoubleValue(3.0 * -0.6 + 5.0 * 1.6, effect->sample(0, 1.6, duration)->at(0)); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_ExtrapolateAdd) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + keyframes[0]->setComposite(AnimationEffect::CompositeAdd); + keyframes[1]->setComposite(AnimationEffect::CompositeAdd); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue((7.0 + 3.0) * -0.6 + (7.0 + 5.0) * 1.6, effect->sample(0, 1.6, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, ZeroKeyframes) +{ + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(AnimatableValueKeyframeVector()); + EXPECT_TRUE(effect->sample(0, 0.5, duration)->isEmpty()); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_SingleKeyframeAtOffsetZero) +{ + AnimatableValueKeyframeVector keyframes(1); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(3.0, effect->sample(0, 0.6, duration)->at(0)); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_SingleKeyframeAtOffsetOne) +{ + AnimatableValueKeyframeVector keyframes(1); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(1.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(5.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(7.0 * 0.4 + 5.0 * 0.6, effect->sample(0, 0.6, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, MoreThanTwoKeyframes) +{ + AnimatableValueKeyframeVector keyframes(3); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0).get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(0.5); + keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0).get()); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setOffset(1.0); + keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(4.0, effect->sample(0, 0.3, duration)->at(0)); + expectDoubleValue(5.0, effect->sample(0, 0.8, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, EndKeyframeOffsetsUnspecified) +{ + AnimatableValueKeyframeVector keyframes(3); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0).get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(0.5); + keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0).get()); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(3.0, effect->sample(0, 0.1, duration)->at(0)); + expectDoubleValue(4.0, effect->sample(0, 0.6, duration)->at(0)); + expectDoubleValue(5.0, effect->sample(0, 0.9, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, SampleOnKeyframe) +{ + AnimatableValueKeyframeVector keyframes(3); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0).get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(0.5); + keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0).get()); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setOffset(1.0); + keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(3.0, effect->sample(0, 0.0, duration)->at(0)); + expectDoubleValue(4.0, effect->sample(0, 0.5, duration)->at(0)); + expectDoubleValue(5.0, effect->sample(0, 1.0, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, MultipleKeyframesWithSameOffset) +{ + AnimatableValueKeyframeVector keyframes(9); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(0.0).get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(0.1); + keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(1.0).get()); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setOffset(0.1); + keyframes[2]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(2.0).get()); + keyframes[3] = AnimatableValueKeyframe::create(); + keyframes[3]->setOffset(0.5); + keyframes[3]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0).get()); + keyframes[4] = AnimatableValueKeyframe::create(); + keyframes[4]->setOffset(0.5); + keyframes[4]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0).get()); + keyframes[5] = AnimatableValueKeyframe::create(); + keyframes[5]->setOffset(0.5); + keyframes[5]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0).get()); + keyframes[6] = AnimatableValueKeyframe::create(); + keyframes[6]->setOffset(0.9); + keyframes[6]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(6.0).get()); + keyframes[7] = AnimatableValueKeyframe::create(); + keyframes[7]->setOffset(0.9); + keyframes[7]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(7.0).get()); + keyframes[8] = AnimatableValueKeyframe::create(); + keyframes[8]->setOffset(1.0); + keyframes[8]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(7.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(0.0, effect->sample(0, 0.0, duration)->at(0)); + expectDoubleValue(2.0, effect->sample(0, 0.2, duration)->at(0)); + expectDoubleValue(3.0, effect->sample(0, 0.4, duration)->at(0)); + expectDoubleValue(5.0, effect->sample(0, 0.5, duration)->at(0)); + expectDoubleValue(5.0, effect->sample(0, 0.6, duration)->at(0)); + expectDoubleValue(6.0, effect->sample(0, 0.8, duration)->at(0)); + expectDoubleValue(7.0, effect->sample(0, 1.0, duration)->at(0)); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_PerKeyframeComposite) +{ + AnimatableValueKeyframeVector keyframes(2); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(3.0).get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(1.0); + keyframes[1]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(5.0).get()); + keyframes[1]->setComposite(AnimationEffect::CompositeAdd); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(3.0 * 0.4 + (7.0 + 5.0) * 0.6, effect->sample(0, 0.6, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, MultipleProperties) +{ + AnimatableValueKeyframeVector keyframes(2); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(3.0).get()); + keyframes[0]->setPropertyValue(CSSPropertyRight, unknownAnimatableValue(4.0).get()); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(1.0); + keyframes[1]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(5.0).get()); + keyframes[1]->setPropertyValue(CSSPropertyRight, unknownAnimatableValue(6.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > values = effect->sample(0, 0.6, duration); + EXPECT_EQ(2UL, values->size()); + Interpolation* leftValue = findValue(*values.get(), CSSPropertyLeft); + ASSERT_TRUE(leftValue); + expectDoubleValue(5.0, leftValue); + Interpolation* rightValue = findValue(*values.get(), CSSPropertyRight); + ASSERT_TRUE(rightValue); + expectDoubleValue(6.0, rightValue); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_RecompositeCompositableValue) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(3.0), pixelAnimatableValue(5.0)); + keyframes[0]->setComposite(AnimationEffect::CompositeAdd); + keyframes[1]->setComposite(AnimationEffect::CompositeAdd); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > values = effect->sample(0, 0.6, duration); + expectDoubleValue((7.0 + 3.0) * 0.4 + (7.0 + 5.0) * 0.6, values->at(0)); + expectDoubleValue((9.0 + 3.0) * 0.4 + (9.0 + 5.0) * 0.6, values->at(0)); +} + +TEST(AnimationKeyframeEffectModel, MultipleIterations) +{ + AnimatableValueKeyframeVector keyframes = keyframesAtZeroAndOne(pixelAnimatableValue(1.0), pixelAnimatableValue(3.0)); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + expectDoubleValue(2.0, effect->sample(0, 0.5, duration)->at(0)); + expectDoubleValue(2.0, effect->sample(1, 0.5, duration)->at(0)); + expectDoubleValue(2.0, effect->sample(2, 0.5, duration)->at(0)); +} + +// FIXME: Re-enable this test once compositing of CompositeAdd is supported. +TEST(AnimationKeyframeEffectModel, DISABLED_DependsOnUnderlyingValue) +{ + AnimatableValueKeyframeVector keyframes(3); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.0); + keyframes[0]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(1.0).get()); + keyframes[0]->setComposite(AnimationEffect::CompositeAdd); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(0.5); + keyframes[1]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(1.0).get()); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setOffset(1.0); + keyframes[2]->setPropertyValue(CSSPropertyLeft, pixelAnimatableValue(1.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + EXPECT_TRUE(effect->sample(0, 0, duration)->at(0)); + EXPECT_TRUE(effect->sample(0, 0.1, duration)->at(0)); + EXPECT_TRUE(effect->sample(0, 0.25, duration)->at(0)); + EXPECT_TRUE(effect->sample(0, 0.4, duration)->at(0)); + EXPECT_FALSE(effect->sample(0, 0.5, duration)->at(0)); + EXPECT_FALSE(effect->sample(0, 0.6, duration)->at(0)); + EXPECT_FALSE(effect->sample(0, 0.75, duration)->at(0)); + EXPECT_FALSE(effect->sample(0, 0.8, duration)->at(0)); + EXPECT_FALSE(effect->sample(0, 1, duration)->at(0)); +} + +TEST(AnimationKeyframeEffectModel, AddSyntheticKeyframes) +{ + AnimatableValueKeyframeVector keyframes(1); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.5); + keyframes[0]->setPropertyValue(CSSPropertyLeft, unknownAnimatableValue(4.0).get()); + + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + const AnimatableValuePropertySpecificKeyframeVector& propertySpecificKeyframes = effect->getPropertySpecificKeyframes(CSSPropertyLeft); + EXPECT_EQ(3U, propertySpecificKeyframes.size()); + EXPECT_DOUBLE_EQ(0.0, propertySpecificKeyframes[0]->offset()); + EXPECT_DOUBLE_EQ(0.5, propertySpecificKeyframes[1]->offset()); + EXPECT_DOUBLE_EQ(1.0, propertySpecificKeyframes[2]->offset()); +} + +TEST(AnimationKeyframeEffectModel, ToKeyframeEffectModel) +{ + AnimatableValueKeyframeVector keyframes(0); + RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes); + + AnimationEffect* baseEffect = effect.get(); + EXPECT_TRUE(toAnimatableValueKeyframeEffectModel(baseEffect)); +} + +} // namespace + +namespace WebCore { + +class KeyframeEffectModelTest : public ::testing::Test { +public: + static KeyframeVector normalizedKeyframes(const KeyframeVector& keyframes) + { + return KeyframeEffectModelBase::normalizedKeyframes(keyframes); + } +}; + +TEST_F(KeyframeEffectModelTest, NotLooselySorted) +{ + KeyframeVector keyframes(4); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[1]->setOffset(9); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[3] = AnimatableValueKeyframe::create(); + keyframes[3]->setOffset(1); + + const KeyframeVector result = normalizedKeyframes(keyframes); + EXPECT_EQ(0U, result.size()); +} + +TEST_F(KeyframeEffectModelTest, LastOne) +{ + KeyframeVector keyframes(3); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(-1); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setOffset(2); + + const KeyframeVector result = normalizedKeyframes(keyframes); + EXPECT_EQ(1U, result.size()); + EXPECT_DOUBLE_EQ(1.0, result[0]->offset()); +} + +TEST_F(KeyframeEffectModelTest, FirstZero) +{ + KeyframeVector keyframes(3); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(-1); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[2]->setOffset(0.25); + + const KeyframeVector result = normalizedKeyframes(keyframes); + EXPECT_EQ(2U, result.size()); + EXPECT_DOUBLE_EQ(0.0, result[0]->offset()); + EXPECT_DOUBLE_EQ(0.25, result[1]->offset()); +} + +TEST_F(KeyframeEffectModelTest, EvenlyDistributed1) +{ + KeyframeVector keyframes(5); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0.125); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[3] = AnimatableValueKeyframe::create(); + keyframes[4] = AnimatableValueKeyframe::create(); + keyframes[4]->setOffset(0.625); + + const KeyframeVector result = normalizedKeyframes(keyframes); + EXPECT_EQ(5U, result.size()); + EXPECT_DOUBLE_EQ(0.125, result[0]->offset()); + EXPECT_DOUBLE_EQ(0.25, result[1]->offset()); + EXPECT_DOUBLE_EQ(0.375, result[2]->offset()); + EXPECT_DOUBLE_EQ(0.5, result[3]->offset()); + EXPECT_DOUBLE_EQ(0.625, result[4]->offset()); +} + +TEST_F(KeyframeEffectModelTest, EvenlyDistributed2) +{ + KeyframeVector keyframes(8); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(-0.1); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[3] = AnimatableValueKeyframe::create(); + keyframes[4] = AnimatableValueKeyframe::create(); + keyframes[4]->setOffset(0.75); + keyframes[5] = AnimatableValueKeyframe::create(); + keyframes[6] = AnimatableValueKeyframe::create(); + keyframes[7] = AnimatableValueKeyframe::create(); + keyframes[7]->setOffset(1.1); + + const KeyframeVector result = normalizedKeyframes(keyframes); + EXPECT_EQ(6U, result.size()); + EXPECT_DOUBLE_EQ(0.0, result[0]->offset()); + EXPECT_DOUBLE_EQ(0.25, result[1]->offset()); + EXPECT_DOUBLE_EQ(0.5, result[2]->offset()); + EXPECT_DOUBLE_EQ(0.75, result[3]->offset()); + EXPECT_DOUBLE_EQ(0.875, result[4]->offset()); + EXPECT_DOUBLE_EQ(1.0, result[5]->offset()); +} + +TEST_F(KeyframeEffectModelTest, EvenlyDistributed3) +{ + KeyframeVector keyframes(12); + keyframes[0] = AnimatableValueKeyframe::create(); + keyframes[0]->setOffset(0); + keyframes[1] = AnimatableValueKeyframe::create(); + keyframes[2] = AnimatableValueKeyframe::create(); + keyframes[3] = AnimatableValueKeyframe::create(); + keyframes[4] = AnimatableValueKeyframe::create(); + keyframes[4]->setOffset(0.5); + keyframes[5] = AnimatableValueKeyframe::create(); + keyframes[6] = AnimatableValueKeyframe::create(); + keyframes[7] = AnimatableValueKeyframe::create(); + keyframes[7]->setOffset(0.8); + keyframes[8] = AnimatableValueKeyframe::create(); + keyframes[9] = AnimatableValueKeyframe::create(); + keyframes[10] = AnimatableValueKeyframe::create(); + keyframes[11] = AnimatableValueKeyframe::create(); + + const KeyframeVector result = normalizedKeyframes(keyframes); + EXPECT_EQ(12U, result.size()); + EXPECT_DOUBLE_EQ(0.0, result[0]->offset()); + EXPECT_DOUBLE_EQ(0.125, result[1]->offset()); + EXPECT_DOUBLE_EQ(0.25, result[2]->offset()); + EXPECT_DOUBLE_EQ(0.375, result[3]->offset()); + EXPECT_DOUBLE_EQ(0.5, result[4]->offset()); + EXPECT_DOUBLE_EQ(0.6, result[5]->offset()); + EXPECT_DOUBLE_EQ(0.7, result[6]->offset()); + EXPECT_DOUBLE_EQ(0.8, result[7]->offset()); + EXPECT_DOUBLE_EQ(0.85, result[8]->offset()); + EXPECT_DOUBLE_EQ(0.9, result[9]->offset()); + EXPECT_DOUBLE_EQ(0.95, result[10]->offset()); + EXPECT_DOUBLE_EQ(1.0, result[11]->offset()); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/Player.cpp b/chromium/third_party/WebKit/Source/core/animation/Player.cpp deleted file mode 100644 index 2fabdd15e17..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/Player.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#include "config.h" -#include "core/animation/Player.h" - -#include "core/animation/Animation.h" -#include "core/animation/DocumentTimeline.h" - -namespace WebCore { - -namespace { - -double effectiveTime(double time) { return isNull(time) ? 0 : time; } - -} // namespace - -PassRefPtr<Player> Player::create(DocumentTimeline& timeline, TimedItem* content) -{ - return adoptRef(new Player(timeline, content)); -} - -Player::Player(DocumentTimeline& timeline, TimedItem* content) - : m_pauseStartTime(nullValue()) - , m_playbackRate(1) - , m_timeDrift(0) - , m_startTime(nullValue()) - , m_content(content) - , m_timeline(timeline) - , m_isPausedForTesting(false) -{ - if (m_content) - m_content->attach(this); -} - -Player::~Player() -{ - if (m_content) - m_content->detach(); -} - -void Player::setStartTime(double startTime) -{ - ASSERT(!isNull(startTime)); - ASSERT(!hasStartTime()); - m_startTime = startTime; - update(); -} - -double Player::currentTimeBeforeDrift() const -{ - if (isNull(m_startTime)) - return 0; - return (effectiveTime(m_timeline.currentTime()) - startTime()) * m_playbackRate; -} - -bool Player::maybeStartAnimationOnCompositor() -{ - // FIXME: Support starting compositor animations that have a fixed - // start time. - ASSERT(!hasStartTime()); - if (!m_content || !m_content->isAnimation()) - return false; - - return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(); -} - -bool Player::hasActiveAnimationsOnCompositor() -{ - if (!m_content || !m_content->isAnimation()) - return false; - return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); -} - -void Player::cancelAnimationOnCompositor() -{ - if (hasActiveAnimationsOnCompositor()) - toAnimation(m_content.get())->cancelAnimationOnCompositor(); -} - -double Player::pausedTimeDrift() const -{ - ASSERT(pausedInternal()); - return currentTimeBeforeDrift() - m_pauseStartTime; -} - -double Player::timeDrift() const -{ - return pausedInternal() ? pausedTimeDrift() : m_timeDrift; -} - -double Player::currentTime() const -{ - return currentTimeBeforeDrift() - timeDrift(); -} - -bool Player::update(double* timeToEffectChange, bool* didTriggerStyleRecalc) -{ - if (!m_content) { - if (timeToEffectChange) - *timeToEffectChange = std::numeric_limits<double>::infinity(); - if (didTriggerStyleRecalc) - *didTriggerStyleRecalc = false; - return false; - } - - double inheritedTime = isNull(m_timeline.currentTime()) ? nullValue() : currentTime(); - bool didTriggerStyleRecalcLocal = m_content->updateInheritedTime(inheritedTime); - - if (timeToEffectChange) - *timeToEffectChange = m_content->timeToEffectChange(); - if (didTriggerStyleRecalc) - *didTriggerStyleRecalc = didTriggerStyleRecalcLocal; - return m_content->isCurrent() || m_content->isInEffect(); -} - -void Player::cancel() -{ - if (!m_content) - return; - - ASSERT(m_content->player() == this); - m_content->detach(); - m_content = 0; -} - -void Player::setCurrentTime(double seekTime) -{ - if (pausedInternal()) - m_pauseStartTime = seekTime; - else - m_timeDrift = currentTimeBeforeDrift() - seekTime; - - if (m_isPausedForTesting && hasActiveAnimationsOnCompositor()) - toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTime()); - update(); -} - -void Player::pauseForTesting() -{ - RELEASE_ASSERT(!paused()); - if (!m_isPausedForTesting && hasActiveAnimationsOnCompositor()) - toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTime()); - m_isPausedForTesting = true; - setPausedImpl(true); -} - -void Player::setPaused(bool newValue) -{ - ASSERT(!m_isPausedForTesting); - setPausedImpl(newValue); -} - -void Player::setPausedImpl(bool newValue) -{ - if (pausedInternal() == newValue) - return; - - if (newValue) { - // FIXME: resume compositor animation rather than pull back to main-thread - cancelAnimationOnCompositor(); - m_pauseStartTime = currentTime(); - } else { - m_timeDrift = pausedTimeDrift(); - m_pauseStartTime = nullValue(); - } -} - -void Player::setPlaybackRate(double newRate) -{ - double previousTime = currentTime(); - m_playbackRate = newRate; - setCurrentTime(previousTime); -} - -} // namespace diff --git a/chromium/third_party/WebKit/Source/core/animation/Player.h b/chromium/third_party/WebKit/Source/core/animation/Player.h deleted file mode 100644 index df80447d51e..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/Player.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef Player_h -#define Player_h - -#include "core/animation/TimedItem.h" -#include "wtf/RefPtr.h" - -namespace WebCore { - -class DocumentTimeline; - -class Player FINAL : public RefCounted<Player> { - -public: - ~Player(); - static PassRefPtr<Player> create(DocumentTimeline&, TimedItem*); - - // Returns whether this player is still current or in effect. - // timeToEffectChange returns: - // infinity - if this player is no longer in effect - // 0 - if this player requires an update on the next frame - // n - if this player requires an update after 'n' units of time - bool update(double* timeToEffectChange = 0, bool* didTriggerStyleRecalc = 0); - void cancel(); - - double currentTime() const; - void setCurrentTime(double); - - bool paused() const { return !m_isPausedForTesting && pausedInternal(); } - void setPaused(bool); - - double playbackRate() const { return m_playbackRate; } - void setPlaybackRate(double); - double timeDrift() const; - DocumentTimeline& timeline() { return m_timeline; } - - bool hasStartTime() const { return !isNull(m_startTime); } - double startTime() const { return m_startTime; } - void setStartTime(double); - - TimedItem* source() { return m_content.get(); } - - // Pausing via this method is not reflected in the value returned by - // paused() and must never overlap with pausing via setPaused(). - void pauseForTesting(); - - bool maybeStartAnimationOnCompositor(); - void cancelAnimationOnCompositor(); - bool hasActiveAnimationsOnCompositor(); - -private: - Player(DocumentTimeline&, TimedItem*); - inline double pausedTimeDrift() const; - inline double currentTimeBeforeDrift() const; - - - void setPausedImpl(bool); - // Reflects all pausing, including via pauseForTesting(). - bool pausedInternal() const { return !isNull(m_pauseStartTime); } - - double m_pauseStartTime; - double m_playbackRate; - double m_timeDrift; - double m_startTime; - - RefPtr<TimedItem> m_content; - DocumentTimeline& m_timeline; - bool m_isPausedForTesting; -}; - -} // namespace - -#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/PlayerTest.cpp b/chromium/third_party/WebKit/Source/core/animation/PlayerTest.cpp deleted file mode 100644 index 4ff5badddba..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/PlayerTest.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2013, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/Player.h" - -#include "core/animation/ActiveAnimations.h" -#include "core/animation/Animation.h" -#include "core/animation/AnimationClock.h" -#include "core/animation/DocumentTimeline.h" -#include "core/dom/Document.h" -#include "core/dom/QualifiedName.h" -#include "platform/weborigin/KURL.h" -#include <gtest/gtest.h> - -using namespace WebCore; - -namespace { - -class AnimationPlayerTest : public ::testing::Test { -protected: - virtual void SetUp() - { - document = Document::create(); - document->animationClock().resetTimeForTesting(); - timeline = DocumentTimeline::create(document.get()); - player = Player::create(*timeline, 0); - player->setStartTime(0); - timeline->setZeroTime(0); - } - - bool updateTimeline(double time, double* timeToEffectChange = 0) - { - document->animationClock().updateTime(time); - // The timeline does not know about our player, so we have to explicitly call update(). - return player->update(timeToEffectChange); - } - - RefPtr<Document> document; - RefPtr<DocumentTimeline> timeline; - RefPtr<Player> player; -}; - -TEST_F(AnimationPlayerTest, InitialState) -{ - EXPECT_EQ(0, timeline->currentTime()); - EXPECT_EQ(0, player->currentTime()); - EXPECT_FALSE(player->paused()); - EXPECT_EQ(1, player->playbackRate()); - EXPECT_EQ(0, player->startTime()); - EXPECT_EQ(0, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, PauseUnpause) -{ - updateTimeline(200); - player->setPaused(true); - EXPECT_TRUE(player->paused()); - EXPECT_EQ(200, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); - - updateTimeline(400); - player->setPaused(false); - EXPECT_FALSE(player->paused()); - EXPECT_EQ(200, player->currentTime()); - EXPECT_EQ(200, player->timeDrift()); - - updateTimeline(600); - EXPECT_EQ(400, player->currentTime()); - EXPECT_EQ(200, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, PauseBeforeTimelineStarted) -{ - player->setPaused(true); - EXPECT_TRUE(player->paused()); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); - - player->setPaused(false); - EXPECT_FALSE(player->paused()); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); - - player->setPaused(true); - updateTimeline(100); - EXPECT_TRUE(player->paused()); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(100, player->timeDrift()); - - player->setPaused(false); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(100, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, PauseBeforeStartTimeSet) -{ - player = Player::create(*timeline, 0); - updateTimeline(100); - EXPECT_EQ(0, player->currentTime()); - - player->setPaused(true); - updateTimeline(200); - EXPECT_EQ(0, player->currentTime()); - - player->setStartTime(150); - EXPECT_EQ(0, player->currentTime()); - - player->setPaused(false); - EXPECT_EQ(0, player->currentTime()); - - updateTimeline(300); - EXPECT_EQ(100, player->currentTime()); -} - -TEST_F(AnimationPlayerTest, SetCurrentTime) -{ - updateTimeline(0); - player->setCurrentTime(250); - EXPECT_EQ(250, player->currentTime()); - EXPECT_EQ(-250, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, SetStartTime) -{ - updateTimeline(0); - player = Player::create(*timeline, 0); - EXPECT_FALSE(player->hasStartTime()); - EXPECT_TRUE(isNull(player->startTime())); - EXPECT_EQ(0, player->currentTime()); - - updateTimeline(100); - player->setStartTime(50); - EXPECT_TRUE(player->hasStartTime()); - EXPECT_EQ(50, player->startTime()); - EXPECT_EQ(50, player->currentTime()); - - updateTimeline(200); - EXPECT_EQ(150, player->currentTime()); -} - - -TEST_F(AnimationPlayerTest, SetCurrentTimeBeforeTimelineStarted) -{ - player->setCurrentTime(250); - EXPECT_EQ(250, player->currentTime()); - EXPECT_EQ(-250, player->timeDrift()); - - updateTimeline(0); - EXPECT_EQ(250, player->currentTime()); -} - -TEST_F(AnimationPlayerTest, SetCurrentTimeBeforeStartTimeSet) -{ - updateTimeline(0); - player = Player::create(*timeline, 0); - - player->setCurrentTime(250); - EXPECT_EQ(250, player->currentTime()); - EXPECT_EQ(-250, player->timeDrift()); - - updateTimeline(100); - player->setStartTime(50); - EXPECT_EQ(300, player->currentTime()); -} - -TEST_F(AnimationPlayerTest, SetPlaybackRate) -{ - updateTimeline(0); - player->setPlaybackRate(2); - EXPECT_EQ(2, player->playbackRate()); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); - - updateTimeline(100); - EXPECT_EQ(200, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, SetPlaybackRateBeforeTimelineStarted) -{ - player->setPlaybackRate(2); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); - - updateTimeline(100); - EXPECT_EQ(200, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, SetPlaybackRateWhilePaused) -{ - updateTimeline(100); - player->setPaused(true); - player->setPlaybackRate(2); - EXPECT_EQ(100, player->currentTime()); - EXPECT_EQ(100, player->timeDrift()); - - updateTimeline(200); - player->setPaused(false); - EXPECT_EQ(100, player->currentTime()); - EXPECT_EQ(300, player->timeDrift()); - - updateTimeline(250); - EXPECT_EQ(200, player->currentTime()); - EXPECT_EQ(300, player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, SetPlaybackRateNaN) -{ - updateTimeline(0); - player->setPlaybackRate(nullValue()); - EXPECT_TRUE(isNull(player->playbackRate())); - EXPECT_TRUE(isNull(player->currentTime())); - EXPECT_TRUE(isNull(player->timeDrift())); - - updateTimeline(100); - EXPECT_TRUE(isNull(player->currentTime())); - EXPECT_TRUE(isNull(player->timeDrift())); -} - -TEST_F(AnimationPlayerTest, SetPlaybackRateInfinity) -{ - updateTimeline(0); - player->setPlaybackRate(std::numeric_limits<double>::infinity()); - EXPECT_EQ(std::numeric_limits<double>::infinity(), player->playbackRate()); - EXPECT_TRUE(isNull(player->currentTime())); - EXPECT_TRUE(isNull(player->timeDrift())); - - updateTimeline(100); - EXPECT_TRUE(isNull(player->currentTime())); - EXPECT_TRUE(isNull(player->timeDrift())); -} - -TEST_F(AnimationPlayerTest, SetPlaybackRateMax) -{ - updateTimeline(0); - player->setPlaybackRate(std::numeric_limits<double>::max()); - EXPECT_EQ(std::numeric_limits<double>::max(), player->playbackRate()); - EXPECT_EQ(0, player->currentTime()); - EXPECT_EQ(0, player->timeDrift()); - - updateTimeline(100); - EXPECT_EQ(std::numeric_limits<double>::infinity(), player->currentTime()); -} - -TEST_F(AnimationPlayerTest, SetCurrentTimeNan) -{ - updateTimeline(0); - player->setCurrentTime(nullValue()); - EXPECT_TRUE(isNull(player->currentTime())); - EXPECT_TRUE(isNull(player->timeDrift())); - - updateTimeline(100); - EXPECT_TRUE(isNull(player->currentTime())); - EXPECT_TRUE(isNull(player->timeDrift())); -} - -TEST_F(AnimationPlayerTest, SetCurrentTimeInfinity) -{ - updateTimeline(0); - player->setCurrentTime(std::numeric_limits<double>::infinity()); - EXPECT_EQ(std::numeric_limits<double>::infinity(), player->currentTime()); - EXPECT_EQ(-std::numeric_limits<double>::infinity(), player->timeDrift()); - - updateTimeline(100); - EXPECT_EQ(std::numeric_limits<double>::infinity(), player->currentTime()); - EXPECT_EQ(-std::numeric_limits<double>::infinity(), player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, SetCurrentTimeMax) -{ - updateTimeline(0); - player->setCurrentTime(std::numeric_limits<double>::max()); - EXPECT_EQ(std::numeric_limits<double>::max(), player->currentTime()); - EXPECT_EQ(-std::numeric_limits<double>::max(), player->timeDrift()); - - updateTimeline(100); - EXPECT_EQ(std::numeric_limits<double>::max(), player->currentTime()); - EXPECT_EQ(-std::numeric_limits<double>::max(), player->timeDrift()); -} - -TEST_F(AnimationPlayerTest, EmptyPlayersDontUpdateEffects) -{ - double timeToNextEffect; - updateTimeline(0, &timeToNextEffect); - EXPECT_EQ(std::numeric_limits<double>::infinity(), timeToNextEffect); - - timeToNextEffect = 0; - updateTimeline(1234, &timeToNextEffect); - EXPECT_EQ(std::numeric_limits<double>::infinity(), timeToNextEffect); -} - -TEST_F(AnimationPlayerTest, PlayersReturnTimeToNextEffect) -{ - Timing timing; - timing.startDelay = 1; - timing.iterationDuration = 1; - timing.hasIterationDuration = true; - RefPtr<Animation> animation = Animation::create(0, 0, timing); - player = Player::create(*timeline, animation.get()); - player->setStartTime(0); - - double timeToNextEffect; - updateTimeline(0, &timeToNextEffect); - EXPECT_EQ(1, timeToNextEffect); - - updateTimeline(0.5, &timeToNextEffect); - EXPECT_EQ(0.5, timeToNextEffect); - - updateTimeline(1, &timeToNextEffect); - EXPECT_EQ(0, timeToNextEffect); - - updateTimeline(1.5, &timeToNextEffect); - EXPECT_EQ(0, timeToNextEffect); - - updateTimeline(2, &timeToNextEffect); - EXPECT_EQ(std::numeric_limits<double>::infinity(), timeToNextEffect); - - updateTimeline(3, &timeToNextEffect); - EXPECT_EQ(std::numeric_limits<double>::infinity(), timeToNextEffect); -} - -TEST_F(AnimationPlayerTest, AttachedPlayers) -{ - RefPtr<Element> element = document->createElement("foo", ASSERT_NO_EXCEPTION); - - Timing timing; - RefPtr<Animation> animation = Animation::create(element, 0, timing); - RefPtr<Player> player = Player::create(*timeline, animation.get()); - EXPECT_EQ(1U, element->activeAnimations()->players().find(player.get())->value); - - player.release(); - EXPECT_TRUE(element->activeAnimations()->players().isEmpty()); -} - -} diff --git a/chromium/third_party/WebKit/Source/core/animation/SampledEffect.cpp b/chromium/third_party/WebKit/Source/core/animation/SampledEffect.cpp new file mode 100644 index 00000000000..0c5e1548c71 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/SampledEffect.cpp @@ -0,0 +1,66 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/SampledEffect.h" + +#include "core/animation/interpolation/StyleInterpolation.h" + +namespace WebCore { + +SampledEffect::SampledEffect(Animation* animation, PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations) + : m_animation(animation) +#if !ENABLE(OILPAN) + , m_player(animation->player()) +#endif + , m_interpolations(interpolations) + , m_playerSortInfo(animation->player()->sortInfo()) + , m_priority(animation->priority()) +{ + ASSERT(m_interpolations && !m_interpolations->isEmpty()); +} + +bool SampledEffect::canChange() const +{ +#if ENABLE(OILPAN) + return m_animation; +#else + if (!m_animation) + return false; + // FIXME: This check won't be needed when Animation and AnimationPlayer are moved to Oilpan. + return !m_player->canFree(); +#endif +} + +void SampledEffect::clear() +{ +#if !ENABLE(OILPAN) + m_player = nullptr; +#endif + m_animation = nullptr; + m_interpolations->clear(); +} + +void SampledEffect::removeReplacedInterpolationsIfNeeded(const BitArray<numCSSProperties>& replacedProperties) +{ + if (canChange() && m_animation->isCurrent()) + return; + + size_t dest = 0; + for (size_t i = 0; i < m_interpolations->size(); i++) { + if (!replacedProperties.get(toStyleInterpolation(m_interpolations->at(i).get())->id())) + m_interpolations->at(dest++) = m_interpolations->at(i); + } + m_interpolations->shrink(dest); +} + +void SampledEffect::trace(Visitor* visitor) +{ + visitor->trace(m_animation); +#if ENABLE(OILPAN) + visitor->trace(m_interpolations); +#endif +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/SampledEffect.h b/chromium/third_party/WebKit/Source/core/animation/SampledEffect.h new file mode 100644 index 00000000000..7b4f14617eb --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/SampledEffect.h @@ -0,0 +1,51 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SampledEffect_h +#define SampledEffect_h + +#include "core/animation/Animation.h" +#include "core/animation/AnimationPlayer.h" +#include "core/animation/interpolation/Interpolation.h" +#include "wtf/BitArray.h" +#include "wtf/Vector.h" + +namespace WebCore { + +class SampledEffect : public NoBaseWillBeGarbageCollected<SampledEffect> { +public: + static PassOwnPtrWillBeRawPtr<SampledEffect> create(Animation* animation, PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations) + { + return adoptPtrWillBeNoop(new SampledEffect(animation, interpolations)); + } + + bool canChange() const; + void clear(); + + const WillBeHeapVector<RefPtrWillBeMember<Interpolation> >& interpolations() const { return *m_interpolations; } + void setInterpolations(PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations) { m_interpolations = interpolations; } + + Animation* animation() const { return m_animation; } + const AnimationPlayer::SortInfo& sortInfo() const { return m_playerSortInfo; } + Animation::Priority priority() const { return m_priority; } + + void removeReplacedInterpolationsIfNeeded(const BitArray<numCSSProperties>&); + + void trace(Visitor*); + +private: + SampledEffect(Animation*, PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > >); + + RawPtrWillBeWeakMember<Animation> m_animation; +#if !ENABLE(OILPAN) + RefPtr<AnimationPlayer> m_player; +#endif + OwnPtrWillBeMember<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > m_interpolations; + AnimationPlayer::SortInfo m_playerSortInfo; + Animation::Priority m_priority; +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/StringKeyframe.cpp b/chromium/third_party/WebKit/Source/core/animation/StringKeyframe.cpp new file mode 100644 index 00000000000..fe13572eb56 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/StringKeyframe.cpp @@ -0,0 +1,152 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/StringKeyframe.h" + +#include "core/animation/css/CSSAnimations.h" +#include "core/animation/interpolation/DefaultStyleInterpolation.h" +#include "core/animation/interpolation/DeferredLegacyStyleInterpolation.h" +#include "core/animation/interpolation/LegacyStyleInterpolation.h" +#include "core/animation/interpolation/LengthStyleInterpolation.h" +#include "core/css/resolver/StyleResolver.h" +#include "core/rendering/style/RenderStyle.h" + +namespace WebCore { + +StringKeyframe::StringKeyframe(const StringKeyframe& copyFrom) + : Keyframe(copyFrom.m_offset, copyFrom.m_composite, copyFrom.m_easing) + , m_propertySet(copyFrom.m_propertySet->mutableCopy()) +{ +} + +void StringKeyframe::setPropertyValue(CSSPropertyID property, const String& value, StyleSheetContents* styleSheetContents) +{ + ASSERT(property != CSSPropertyInvalid); + if (CSSAnimations::isAllowedAnimation(property)) + m_propertySet->setProperty(property, value, false, styleSheetContents); +} + +PropertySet StringKeyframe::properties() const +{ + // This is not used in time-critical code, so we probably don't need to + // worry about caching this result. + PropertySet properties; + for (unsigned i = 0; i < m_propertySet->propertyCount(); ++i) + properties.add(m_propertySet->propertyAt(i).id()); + return properties; +} + +PassRefPtrWillBeRawPtr<Keyframe> StringKeyframe::clone() const +{ + return adoptRefWillBeNoop(new StringKeyframe(*this)); +} +PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> StringKeyframe::createPropertySpecificKeyframe(CSSPropertyID property) const +{ + return adoptPtrWillBeNoop(new PropertySpecificKeyframe(offset(), easing(), propertyValue(property), composite())); +} + +void StringKeyframe::trace(Visitor* visitor) +{ + visitor->trace(m_propertySet); + Keyframe::trace(visitor); +} + +StringKeyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, CSSValue* value, AnimationEffect::CompositeOperation op) + : Keyframe::PropertySpecificKeyframe(offset, easing, op) + , m_value(value) +{ } + +StringKeyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, CSSValue* value) + : Keyframe::PropertySpecificKeyframe(offset, easing, AnimationEffect::CompositeReplace) + , m_value(value) +{ + ASSERT(!isNull(m_offset)); +} + +PassRefPtrWillBeRawPtr<Interpolation> StringKeyframe::PropertySpecificKeyframe::createInterpolation(CSSPropertyID property, Keyframe::PropertySpecificKeyframe* end, Element* element) const +{ + CSSValue* fromCSSValue = m_value.get(); + CSSValue* toCSSValue = toStringPropertySpecificKeyframe(end)->value(); + ValueRange range = ValueRangeAll; + + if (!CSSAnimations::isAnimatableProperty(property)) + return DefaultStyleInterpolation::create(fromCSSValue, toCSSValue, property); + + switch (property) { + case CSSPropertyBorderBottomWidth: + case CSSPropertyBorderLeftWidth: + case CSSPropertyBorderRightWidth: + case CSSPropertyBorderTopWidth: + case CSSPropertyFontSize: + case CSSPropertyHeight: + case CSSPropertyLineHeight: + case CSSPropertyMaxHeight: + case CSSPropertyMaxWidth: + case CSSPropertyMinHeight: + case CSSPropertyMinWidth: + case CSSPropertyOutlineWidth: + case CSSPropertyPaddingBottom: + case CSSPropertyPaddingLeft: + case CSSPropertyPaddingRight: + case CSSPropertyPaddingTop: + case CSSPropertyPerspective: + case CSSPropertyShapeMargin: + case CSSPropertyWidth: + range = ValueRangeNonNegative; + // Fall through + case CSSPropertyBottom: + case CSSPropertyLeft: + case CSSPropertyLetterSpacing: + case CSSPropertyMarginBottom: + case CSSPropertyMarginLeft: + case CSSPropertyMarginRight: + case CSSPropertyMarginTop: + case CSSPropertyOutlineOffset: + case CSSPropertyRight: + case CSSPropertyTop: + case CSSPropertyVerticalAlign: + case CSSPropertyWordSpacing: + if (LengthStyleInterpolation::canCreateFrom(*fromCSSValue) && LengthStyleInterpolation::canCreateFrom(*toCSSValue)) + return LengthStyleInterpolation::create(fromCSSValue, toCSSValue, property, range); + break; + default: + break; + } + + if (DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(*fromCSSValue) || DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(*toCSSValue)) + return DeferredLegacyStyleInterpolation::create(fromCSSValue, toCSSValue, property); + + // FIXME: Remove the use of AnimatableValues, RenderStyles and Elements here. + // FIXME: Remove this cache + ASSERT(element); + if (!m_animatableValueCache) + m_animatableValueCache = StyleResolver::createAnimatableValueSnapshot(*element, property, *fromCSSValue); + + RefPtrWillBeRawPtr<AnimatableValue> to = StyleResolver::createAnimatableValueSnapshot(*element, property, *toCSSValue); + toStringPropertySpecificKeyframe(end)->m_animatableValueCache = to; + + return LegacyStyleInterpolation::create(m_animatableValueCache.get(), to.release(), property); +} + +PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> StringKeyframe::PropertySpecificKeyframe::neutralKeyframe(double offset, PassRefPtr<TimingFunction> easing) const +{ + return adoptPtrWillBeNoop(new PropertySpecificKeyframe(offset, easing, 0, AnimationEffect::CompositeAdd)); +} + +PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> StringKeyframe::PropertySpecificKeyframe::cloneWithOffset(double offset) const +{ + Keyframe::PropertySpecificKeyframe* theClone = new PropertySpecificKeyframe(offset, m_easing, m_value.get()); + toStringPropertySpecificKeyframe(theClone)->m_animatableValueCache = m_animatableValueCache; + return adoptPtrWillBeNoop(theClone); +} + +void StringKeyframe::PropertySpecificKeyframe::trace(Visitor* visitor) +{ + visitor->trace(m_value); + visitor->trace(m_animatableValueCache); + Keyframe::PropertySpecificKeyframe::trace(visitor); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/StringKeyframe.h b/chromium/third_party/WebKit/Source/core/animation/StringKeyframe.h new file mode 100644 index 00000000000..c6a753e3a76 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/StringKeyframe.h @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef StringKeyframe_h +#define StringKeyframe_h + +#include "core/animation/Keyframe.h" +#include "core/css/StylePropertySet.h" + +namespace WebCore { + +class StyleSheetContents; + +class StringKeyframe : public Keyframe { +public: + static PassRefPtrWillBeRawPtr<StringKeyframe> create() + { + return adoptRefWillBeNoop(new StringKeyframe); + } + void setPropertyValue(CSSPropertyID, const String& value, StyleSheetContents*); + void clearPropertyValue(CSSPropertyID property) { m_propertySet->removeProperty(property); } + CSSValue* propertyValue(CSSPropertyID property) const + { + int index = m_propertySet->findPropertyIndex(property); + RELEASE_ASSERT(index >= 0); + return m_propertySet->propertyAt(static_cast<unsigned>(index)).value(); + } + virtual PropertySet properties() const OVERRIDE; + + virtual void trace(Visitor*) OVERRIDE; + + class PropertySpecificKeyframe : public Keyframe::PropertySpecificKeyframe { + public: + PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, CSSValue*, AnimationEffect::CompositeOperation); + + CSSValue* value() const { return m_value.get(); } + virtual const PassRefPtrWillBeRawPtr<AnimatableValue> getAnimatableValue() const OVERRIDE FINAL { + return m_animatableValueCache.get(); + } + + virtual PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> neutralKeyframe(double offset, PassRefPtr<TimingFunction> easing) const OVERRIDE FINAL; + virtual PassRefPtrWillBeRawPtr<Interpolation> createInterpolation(CSSPropertyID, WebCore::Keyframe::PropertySpecificKeyframe* end, Element*) const OVERRIDE FINAL; + + virtual void trace(Visitor*) OVERRIDE; + + private: + PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, CSSValue*); + + virtual PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> cloneWithOffset(double offset) const; + virtual bool isStringPropertySpecificKeyframe() const OVERRIDE { return true; } + + RefPtrWillBeMember<CSSValue> m_value; + mutable RefPtrWillBeMember<AnimatableValue> m_animatableValueCache; + }; + +private: + StringKeyframe() + : m_propertySet(MutableStylePropertySet::create()) + { } + + StringKeyframe(const StringKeyframe& copyFrom); + + virtual PassRefPtrWillBeRawPtr<Keyframe> clone() const OVERRIDE; + virtual PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> createPropertySpecificKeyframe(CSSPropertyID) const OVERRIDE; + + virtual bool isStringKeyframe() const OVERRIDE { return true; } + + RefPtrWillBeMember<MutableStylePropertySet> m_propertySet; +}; + +typedef StringKeyframe::PropertySpecificKeyframe StringPropertySpecificKeyframe; + +DEFINE_TYPE_CASTS(StringKeyframe, Keyframe, value, value->isStringKeyframe(), value.isStringKeyframe()); +DEFINE_TYPE_CASTS(StringPropertySpecificKeyframe, Keyframe::PropertySpecificKeyframe, value, value->isStringPropertySpecificKeyframe(), value.isStringPropertySpecificKeyframe()); + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/TimedItemTest.cpp b/chromium/third_party/WebKit/Source/core/animation/TimedItemTest.cpp deleted file mode 100644 index 6bd34ab67cc..00000000000 --- a/chromium/third_party/WebKit/Source/core/animation/TimedItemTest.cpp +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Copyright (c) 2013, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "core/animation/TimedItem.h" - -#include <gtest/gtest.h> - -using namespace WebCore; - -namespace { - -class TestTimedItemEventDelegate : public TimedItem::EventDelegate { -public: - void onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration) OVERRIDE - { - m_eventTriggered = true; - m_phaseChanged = previousPhase != timedItem->phase(); - m_iterationChanged = previousIteration != timedItem->currentIteration(); - - } - void reset() - { - m_eventTriggered = false; - m_phaseChanged = false; - m_iterationChanged = false; - } - bool eventTriggered() { return m_eventTriggered; } - bool phaseChanged() { return m_phaseChanged; } - bool iterationChanged() { return m_iterationChanged; } - -private: - bool m_eventTriggered; - bool m_phaseChanged; - bool m_iterationChanged; -}; - -class TestTimedItem : public TimedItem { -public: - static PassRefPtr<TestTimedItem> create(const Timing& specified) - { - return adoptRef(new TestTimedItem(specified, new TestTimedItemEventDelegate())); - } - - void updateInheritedTime(double time) - { - m_eventDelegate->reset(); - TimedItem::updateInheritedTime(time); - } - - bool updateChildrenAndEffects() const OVERRIDE { return false; } - void willDetach() { } - TestTimedItemEventDelegate* eventDelegate() { return m_eventDelegate; } - double calculateTimeToEffectChange(double localTime, double timeToNextIteration) const OVERRIDE - { - m_localTime = localTime; - m_timeToNextIteration = timeToNextIteration; - return -1; - } - - double takeLocalTime() - { - const double result = m_localTime; - m_localTime = nullValue(); - return result; - } - - double takeTimeToNextIteration() - { - const double result = m_timeToNextIteration; - m_timeToNextIteration = nullValue(); - return result; - } - -private: - TestTimedItem(const Timing& specified, TestTimedItemEventDelegate* eventDelegate) - : TimedItem(specified, adoptPtr(eventDelegate)) - , m_eventDelegate(eventDelegate) - { - } - - TestTimedItemEventDelegate* m_eventDelegate; - mutable double m_localTime; - mutable double m_timeToNextIteration; -}; - -TEST(AnimationTimedItemTest, Sanity) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 2; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - EXPECT_EQ(0, timedItem->startTime()); - - timedItem->updateInheritedTime(0); - - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->startTime()); - EXPECT_EQ(2, timedItem->activeDuration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->startTime()); - EXPECT_EQ(2, timedItem->activeDuration()); - EXPECT_EQ(0.5, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2); - - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->startTime()); - EXPECT_EQ(2, timedItem->activeDuration()); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(3); - - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->startTime()); - EXPECT_EQ(2, timedItem->activeDuration()); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, FillForwards) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(2); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, FillBackwards) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.fillMode = Timing::FillModeBackwards; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2); - EXPECT_TRUE(isNull(timedItem->timeFraction())); -} - -TEST(AnimationTimedItemTest, FillBoth) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.fillMode = Timing::FillModeBoth; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, StartDelay) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.startDelay = 0.5; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0.5); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1.5); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroIteration) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.iterationCount = 0; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_TRUE(isNull(timedItem->currentIteration())); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, InfiniteIteration) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.iterationCount = std::numeric_limits<double>::infinity(); - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_TRUE(isNull(timedItem->currentIteration())); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, Iteration) -{ - Timing timing; - timing.iterationCount = 2; - timing.hasIterationDuration = true; - timing.iterationDuration = 2; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0.5, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(5); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, IterationStart) -{ - Timing timing; - timing.iterationStart = 1.2; - timing.iterationCount = 2.2; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.fillMode = Timing::FillModeBoth; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_NEAR(0.2, timedItem->timeFraction(), 0.000000000000001); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_NEAR(0.2, timedItem->timeFraction(), 0.000000000000001); - - timedItem->updateInheritedTime(10); - EXPECT_EQ(3, timedItem->currentIteration()); - EXPECT_NEAR(0.4, timedItem->timeFraction(), 0.000000000000001); -} - -TEST(AnimationTimedItemTest, IterationAlternate) -{ - Timing timing; - timing.iterationCount = 10; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.direction = Timing::PlaybackDirectionAlternate; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0.75); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0.75, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1.75); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(0.25, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2.75); - EXPECT_EQ(2, timedItem->currentIteration()); - EXPECT_EQ(0.75, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, IterationAlternateReverse) -{ - Timing timing; - timing.iterationCount = 10; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.direction = Timing::PlaybackDirectionAlternateReverse; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0.75); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0.25, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1.75); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(0.75, timedItem->timeFraction()); - - timedItem->updateInheritedTime(2.75); - EXPECT_EQ(2, timedItem->currentIteration()); - EXPECT_EQ(0.25, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationSanity) -{ - Timing timing; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - EXPECT_EQ(0, timedItem->startTime()); - - timedItem->updateInheritedTime(0); - - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->startTime()); - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->startTime()); - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationFillForwards) -{ - Timing timing; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationFillBackwards) -{ - Timing timing; - timing.fillMode = Timing::FillModeBackwards; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(0); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(1); - EXPECT_TRUE(isNull(timedItem->timeFraction())); -} - -TEST(AnimationTimedItemTest, ZeroDurationFillBoth) -{ - Timing timing; - timing.fillMode = Timing::FillModeBoth; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationStartDelay) -{ - Timing timing; - timing.startDelay = 0.5; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0.5); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1.5); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationIterationStartAndCount) -{ - Timing timing; - timing.iterationStart = 0.1; - timing.iterationCount = 0.2; - timing.fillMode = Timing::FillModeBoth; - timing.startDelay = 0.3; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(0.1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(0.3); - EXPECT_DOUBLE_EQ(0.3, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_DOUBLE_EQ(0.3, timedItem->timeFraction()); -} - -// FIXME: Needs specification work. -TEST(AnimationTimedItemTest, ZeroDurationInfiniteIteration) -{ - Timing timing; - timing.iterationCount = std::numeric_limits<double>::infinity(); - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_TRUE(isNull(timedItem->currentIteration())); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->currentIteration()); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationIteration) -{ - Timing timing; - timing.iterationCount = 2; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_TRUE(isNull(timedItem->currentIteration())); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationIterationStart) -{ - Timing timing; - timing.iterationStart = 1.2; - timing.iterationCount = 2.2; - timing.fillMode = Timing::FillModeBoth; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_NEAR(0.2, timedItem->timeFraction(), 0.000000000000001); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(3, timedItem->currentIteration()); - EXPECT_NEAR(0.4, timedItem->timeFraction(), 0.000000000000001); - - timedItem->updateInheritedTime(10); - EXPECT_EQ(3, timedItem->currentIteration()); - EXPECT_NEAR(0.4, timedItem->timeFraction(), 0.000000000000001); -} - -TEST(AnimationTimedItemTest, ZeroDurationIterationAlternate) -{ - Timing timing; - timing.iterationCount = 2; - timing.direction = Timing::PlaybackDirectionAlternate; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_TRUE(isNull(timedItem->currentIteration())); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, ZeroDurationIterationAlternateReverse) -{ - Timing timing; - timing.iterationCount = 2; - timing.direction = Timing::PlaybackDirectionAlternateReverse; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(-1); - EXPECT_TRUE(isNull(timedItem->currentIteration())); - EXPECT_TRUE(isNull(timedItem->timeFraction())); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(1, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - EXPECT_EQ(1, timedItem->currentIteration()); - EXPECT_EQ(1, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, InfiniteDurationSanity) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = std::numeric_limits<double>::infinity(); - timing.iterationCount = 1; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - EXPECT_EQ(0, timedItem->startTime()); - - timedItem->updateInheritedTime(0); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -// FIXME: Needs specification work. -TEST(AnimationTimedItemTest, InfiniteDurationZeroIterations) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = std::numeric_limits<double>::infinity(); - timing.iterationCount = 0; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - EXPECT_EQ(0, timedItem->startTime()); - - timedItem->updateInheritedTime(0); - - EXPECT_EQ(0, timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, InfiniteDurationInfiniteIterations) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = std::numeric_limits<double>::infinity(); - timing.iterationCount = std::numeric_limits<double>::infinity(); - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - EXPECT_EQ(0, timedItem->startTime()); - - timedItem->updateInheritedTime(0); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(1); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, InfiniteDurationZeroPlaybackRate) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = std::numeric_limits<double>::infinity(); - timing.playbackRate = 0; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - EXPECT_EQ(0, timedItem->startTime()); - - timedItem->updateInheritedTime(0); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_TRUE(timedItem->isInPlay()); - EXPECT_TRUE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); - - timedItem->updateInheritedTime(std::numeric_limits<double>::infinity()); - - EXPECT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration()); - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_FALSE(timedItem->isInPlay()); - EXPECT_FALSE(timedItem->isCurrent()); - EXPECT_TRUE(timedItem->isInEffect()); - EXPECT_EQ(0, timedItem->currentIteration()); - EXPECT_EQ(0, timedItem->timeFraction()); -} - -TEST(AnimationTimedItemTest, Events) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.iterationCount = 2; - timing.startDelay = 1; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - // First sample - timedItem->updateInheritedTime(0.0); - EXPECT_TRUE(timedItem->eventDelegate()->eventTriggered()); - - // Before start - timedItem->updateInheritedTime(0.5); - EXPECT_FALSE(timedItem->eventDelegate()->eventTriggered()); - - // First iteration - timedItem->updateInheritedTime(1.5); - EXPECT_TRUE(timedItem->eventDelegate()->eventTriggered()); - EXPECT_TRUE(timedItem->eventDelegate()->phaseChanged()); - EXPECT_TRUE(timedItem->eventDelegate()->iterationChanged()); - - timedItem->updateInheritedTime(1.6); - EXPECT_FALSE(timedItem->eventDelegate()->eventTriggered()); - - // Second iteration - timedItem->updateInheritedTime(2.5); - EXPECT_TRUE(timedItem->eventDelegate()->eventTriggered()); - EXPECT_FALSE(timedItem->eventDelegate()->phaseChanged()); - EXPECT_TRUE(timedItem->eventDelegate()->iterationChanged()); - - timedItem->updateInheritedTime(2.6); - EXPECT_FALSE(timedItem->eventDelegate()->eventTriggered()); - - // After end - timedItem->updateInheritedTime(3.5); - EXPECT_TRUE(timedItem->eventDelegate()->eventTriggered()); - EXPECT_TRUE(timedItem->eventDelegate()->phaseChanged()); - EXPECT_FALSE(timedItem->eventDelegate()->iterationChanged()); - - timedItem->updateInheritedTime(3.6); - EXPECT_FALSE(timedItem->eventDelegate()->eventTriggered()); -} - -TEST(AnimationTimedItemTest, TimeToEffectChange) -{ - Timing timing; - timing.hasIterationDuration = true; - timing.iterationDuration = 1; - timing.iterationStart = 0.2; - timing.iterationCount = 2.5; - timing.startDelay = 1; - timing.direction = Timing::PlaybackDirectionAlternate; - RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing); - - timedItem->updateInheritedTime(0); - EXPECT_EQ(0, timedItem->takeLocalTime()); - EXPECT_TRUE(std::isinf(timedItem->takeTimeToNextIteration())); - - // Normal iteration. - timedItem->updateInheritedTime(1.75); - EXPECT_EQ(1.75, timedItem->takeLocalTime()); - EXPECT_NEAR(0.05, timedItem->takeTimeToNextIteration(), 0.000000000000001); - - // Reverse iteration. - timedItem->updateInheritedTime(2.75); - EXPECT_EQ(2.75, timedItem->takeLocalTime()); - EXPECT_NEAR(0.05, timedItem->takeTimeToNextIteration(), 0.000000000000001); - - // Item ends before iteration finishes. - timedItem->updateInheritedTime(3.4); - EXPECT_EQ(TimedItem::PhaseActive, timedItem->phase()); - EXPECT_EQ(3.4, timedItem->takeLocalTime()); - EXPECT_TRUE(std::isinf(timedItem->takeTimeToNextIteration())); - - // Item has finished. - timedItem->updateInheritedTime(3.5); - EXPECT_EQ(TimedItem::PhaseAfter, timedItem->phase()); - EXPECT_EQ(3.5, timedItem->takeLocalTime()); - EXPECT_TRUE(std::isinf(timedItem->takeTimeToNextIteration())); -} - -} diff --git a/chromium/third_party/WebKit/Source/core/animation/Timing.h b/chromium/third_party/WebKit/Source/core/animation/Timing.h index 9a3bc4e6235..e862f653aec 100644 --- a/chromium/third_party/WebKit/Source/core/animation/Timing.h +++ b/chromium/third_party/WebKit/Source/core/animation/Timing.h @@ -31,7 +31,7 @@ #ifndef Timing_h #define Timing_h -#include "core/platform/animation/TimingFunction.h" +#include "platform/animation/TimingFunction.h" #include "wtf/MathExtras.h" #include "wtf/RefPtr.h" @@ -39,6 +39,7 @@ namespace WebCore { struct Timing { enum FillMode { + FillModeAuto, FillModeNone, FillModeForwards, FillModeBackwards, @@ -52,37 +53,43 @@ struct Timing { PlaybackDirectionAlternateReverse }; + static const Timing& defaults() + { + DEFINE_STATIC_LOCAL(Timing, timing, ()); + return timing; + } + Timing() : startDelay(0) - , fillMode(FillModeForwards) + , endDelay(0) + , fillMode(FillModeAuto) , iterationStart(0) , iterationCount(1) - , hasIterationDuration(false) - , iterationDuration(0) + , iterationDuration(std::numeric_limits<double>::quiet_NaN()) , playbackRate(1) , direction(PlaybackDirectionNormal) - , timingFunction(LinearTimingFunction::create()) + , timingFunction(LinearTimingFunction::shared()) { } void assertValid() const { ASSERT(std::isfinite(startDelay)); + ASSERT(std::isfinite(endDelay)); ASSERT(std::isfinite(iterationStart)); ASSERT(iterationStart >= 0); ASSERT(iterationCount >= 0); - ASSERT(iterationDuration >= 0); + ASSERT(std::isnan(iterationDuration) || iterationDuration >= 0); ASSERT(std::isfinite(playbackRate)); ASSERT(timingFunction); } double startDelay; + double endDelay; FillMode fillMode; double iterationStart; double iterationCount; - bool hasIterationDuration; double iterationDuration; - // FIXME: Add activeDuration. double playbackRate; PlaybackDirection direction; RefPtr<TimingFunction> timingFunction; diff --git a/chromium/third_party/WebKit/Source/core/animation/Timing.idl b/chromium/third_party/WebKit/Source/core/animation/Timing.idl new file mode 100644 index 00000000000..49e93e1d61f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/Timing.idl @@ -0,0 +1,29 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + RuntimeEnabled=WebAnimationsAPI, + ImplementedAs=AnimationNodeTiming, + WillBeGarbageCollected, +] interface Timing { + attribute double delay; + attribute double endDelay; + attribute DOMString fill; + attribute double iterationStart; + attribute double iterations; + + // FIXME: This uses a NamedPropertyGetter to implement the 'duration' attribute + // because duration has a union type (which is tricky to do with an attribute). + // Fix will be in a follow-up patch if there is a better solution. + [NotEnumerable, ImplementedAs=getDuration] getter (double or DOMString) (DOMString name); + + // FIXME: If the user calls animation.specified.duration = "" (empty string) then duration + // gets set to 0 (This is correct behavior for IDL). Correct result is for duration to + // be set to 'auto'. + [TypeChecking=Interface|Nullable, ImplementedAs=setDuration] setter double (DOMString name, double duration); + + attribute double playbackRate; + attribute DOMString direction; + attribute DOMString easing; +}; diff --git a/chromium/third_party/WebKit/Source/core/animation/TimedItemCalculations.h b/chromium/third_party/WebKit/Source/core/animation/TimingCalculations.h index 28a6228554a..1954e42b665 100644 --- a/chromium/third_party/WebKit/Source/core/animation/TimedItemCalculations.h +++ b/chromium/third_party/WebKit/Source/core/animation/TimingCalculations.h @@ -28,10 +28,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TimedItemCalculations_h -#define TimedItemCalculations_h +#ifndef TimingCalculations_h +#define TimingCalculations_h -#include "core/animation/TimedItem.h" +#include "core/animation/AnimationNode.h" #include "core/animation/Timing.h" #include "platform/animation/AnimationUtilities.h" #include "wtf/MathExtras.h" @@ -45,26 +45,26 @@ static inline double multiplyZeroAlwaysGivesZero(double x, double y) return x && y ? x * y : 0; } -static inline TimedItem::Phase calculatePhase(double activeDuration, double localTime, const Timing& specified) +static inline AnimationNode::Phase calculatePhase(double activeDuration, double localTime, const Timing& specified) { ASSERT(activeDuration >= 0); if (isNull(localTime)) - return TimedItem::PhaseNone; + return AnimationNode::PhaseNone; if (localTime < specified.startDelay) - return TimedItem::PhaseBefore; + return AnimationNode::PhaseBefore; if (localTime >= specified.startDelay + activeDuration) - return TimedItem::PhaseAfter; - return TimedItem::PhaseActive; + return AnimationNode::PhaseAfter; + return AnimationNode::PhaseActive; } -static inline bool isActiveInParentPhase(TimedItem::Phase parentPhase, Timing::FillMode fillMode) +static inline bool isActiveInParentPhase(AnimationNode::Phase parentPhase, Timing::FillMode fillMode) { switch (parentPhase) { - case TimedItem::PhaseBefore: + case AnimationNode::PhaseBefore: return fillMode == Timing::FillModeBackwards || fillMode == Timing::FillModeBoth; - case TimedItem::PhaseActive: + case AnimationNode::PhaseActive: return true; - case TimedItem::PhaseAfter: + case AnimationNode::PhaseAfter: return fillMode == Timing::FillModeForwards || fillMode == Timing::FillModeBoth; default: ASSERT_NOT_REACHED(); @@ -72,25 +72,25 @@ static inline bool isActiveInParentPhase(TimedItem::Phase parentPhase, Timing::F } } -static inline double calculateActiveTime(double activeDuration, double localTime, TimedItem::Phase parentPhase, TimedItem::Phase phase, const Timing& specified) +static inline double calculateActiveTime(double activeDuration, Timing::FillMode fillMode, double localTime, AnimationNode::Phase parentPhase, AnimationNode::Phase phase, const Timing& specified) { ASSERT(activeDuration >= 0); ASSERT(phase == calculatePhase(activeDuration, localTime, specified)); switch (phase) { - case TimedItem::PhaseBefore: - if (specified.fillMode == Timing::FillModeBackwards || specified.fillMode == Timing::FillModeBoth) + case AnimationNode::PhaseBefore: + if (fillMode == Timing::FillModeBackwards || fillMode == Timing::FillModeBoth) return 0; return nullValue(); - case TimedItem::PhaseActive: - if (isActiveInParentPhase(parentPhase, specified.fillMode)) + case AnimationNode::PhaseActive: + if (isActiveInParentPhase(parentPhase, fillMode)) return localTime - specified.startDelay; return nullValue(); - case TimedItem::PhaseAfter: - if (specified.fillMode == Timing::FillModeForwards || specified.fillMode == Timing::FillModeBoth) + case AnimationNode::PhaseAfter: + if (fillMode == Timing::FillModeForwards || fillMode == Timing::FillModeBoth) return activeDuration; return nullValue(); - case TimedItem::PhaseNone: + case AnimationNode::PhaseNone: ASSERT(isNull(localTime)); return nullValue(); default: @@ -190,9 +190,7 @@ static inline double calculateTransformedTime(double currentIteration, double it return directedTime; double timeFraction = directedTime / iterationDuration; ASSERT(timeFraction >= 0 && timeFraction <= 1); - return specified.timingFunction - ? multiplyZeroAlwaysGivesZero(iterationDuration, specified.timingFunction->evaluate(timeFraction, accuracyForDuration(iterationDuration))) - : directedTime; + return multiplyZeroAlwaysGivesZero(iterationDuration, specified.timingFunction->evaluate(timeFraction, accuracyForDuration(iterationDuration))); } } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/TimedItemCalculationsTest.cpp b/chromium/third_party/WebKit/Source/core/animation/TimingCalculationsTest.cpp index 6e053cdde8f..ffd22058166 100644 --- a/chromium/third_party/WebKit/Source/core/animation/TimedItemCalculationsTest.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/TimingCalculationsTest.cpp @@ -29,7 +29,7 @@ */ #include "config.h" -#include "core/animation/TimedItemCalculations.h" +#include "core/animation/TimingCalculations.h" #include <gtest/gtest.h> @@ -37,55 +37,42 @@ using namespace WebCore; namespace { -TEST(AnimationTimedItemCalculationsTest, ActiveTime) +TEST(AnimationTimingCalculationsTest, ActiveTime) { Timing timing; - // calculateActiveTime(activeDuration, localTime, parentPhase, phase, timing) + // calculateActiveTime(activeDuration, fillMode, localTime, parentPhase, phase, timing) // Before Phase timing.startDelay = 10; - timing.fillMode = Timing::FillModeForwards; - EXPECT_TRUE(isNull(calculateActiveTime(20, 0, TimedItem::PhaseActive, TimedItem::PhaseBefore, timing))); - timing.fillMode = Timing::FillModeNone; - EXPECT_TRUE(isNull(calculateActiveTime(20, 0, TimedItem::PhaseActive, TimedItem::PhaseBefore, timing))); - timing.fillMode = Timing::FillModeBackwards; - EXPECT_EQ(0, calculateActiveTime(20, 0, TimedItem::PhaseActive, TimedItem::PhaseBefore, timing)); - timing.fillMode = Timing::FillModeBoth; - EXPECT_EQ(0, calculateActiveTime(20, 0, TimedItem::PhaseActive, TimedItem::PhaseBefore, timing)); + EXPECT_TRUE(isNull(calculateActiveTime(20, Timing::FillModeForwards, 0, AnimationNode::PhaseActive, AnimationNode::PhaseBefore, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(20, Timing::FillModeNone, 0, AnimationNode::PhaseActive, AnimationNode::PhaseBefore, timing))); + EXPECT_EQ(0, calculateActiveTime(20, Timing::FillModeBackwards, 0, AnimationNode::PhaseActive, AnimationNode::PhaseBefore, timing)); + EXPECT_EQ(0, calculateActiveTime(20, Timing::FillModeBoth, 0, AnimationNode::PhaseActive, AnimationNode::PhaseBefore, timing)); // Active Phase timing.startDelay = 10; // Active, and parent Before - timing.fillMode = Timing::FillModeNone; - EXPECT_TRUE(isNull(calculateActiveTime(20, 15, TimedItem::PhaseBefore, TimedItem::PhaseActive, timing))); - timing.fillMode = Timing::FillModeForwards; - EXPECT_TRUE(isNull(calculateActiveTime(20, 15, TimedItem::PhaseBefore, TimedItem::PhaseActive, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(20, Timing::FillModeNone, 15, AnimationNode::PhaseBefore, AnimationNode::PhaseActive, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(20, Timing::FillModeForwards, 15, AnimationNode::PhaseBefore, AnimationNode::PhaseActive, timing))); // Active, and parent After - timing.fillMode = Timing::FillModeNone; - EXPECT_TRUE(isNull(calculateActiveTime(20, 15, TimedItem::PhaseAfter, TimedItem::PhaseActive, timing))); - timing.fillMode = Timing::FillModeBackwards; - EXPECT_TRUE(isNull(calculateActiveTime(20, 15, TimedItem::PhaseAfter, TimedItem::PhaseActive, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(20, Timing::FillModeNone, 15, AnimationNode::PhaseAfter, AnimationNode::PhaseActive, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(20, Timing::FillModeBackwards, 15, AnimationNode::PhaseAfter, AnimationNode::PhaseActive, timing))); // Active, and parent Active - timing.fillMode = Timing::FillModeForwards; - EXPECT_EQ(5, calculateActiveTime(20, 15, TimedItem::PhaseActive, TimedItem::PhaseActive, timing)); + EXPECT_EQ(5, calculateActiveTime(20, Timing::FillModeForwards, 15, AnimationNode::PhaseActive, AnimationNode::PhaseActive, timing)); // After Phase timing.startDelay = 10; - timing.fillMode = Timing::FillModeForwards; - EXPECT_EQ(21, calculateActiveTime(21, 45, TimedItem::PhaseActive, TimedItem::PhaseAfter, timing)); - timing.fillMode = Timing::FillModeBoth; - EXPECT_EQ(21, calculateActiveTime(21, 45, TimedItem::PhaseActive, TimedItem::PhaseAfter, timing)); - timing.fillMode = Timing::FillModeBackwards; - EXPECT_TRUE(isNull(calculateActiveTime(21, 45, TimedItem::PhaseActive, TimedItem::PhaseAfter, timing))); - timing.fillMode = Timing::FillModeNone; - EXPECT_TRUE(isNull(calculateActiveTime(21, 45, TimedItem::PhaseActive, TimedItem::PhaseAfter, timing))); + EXPECT_EQ(21, calculateActiveTime(21, Timing::FillModeForwards, 45, AnimationNode::PhaseActive, AnimationNode::PhaseAfter, timing)); + EXPECT_EQ(21, calculateActiveTime(21, Timing::FillModeBoth, 45, AnimationNode::PhaseActive, AnimationNode::PhaseAfter, timing)); + EXPECT_TRUE(isNull(calculateActiveTime(21, Timing::FillModeBackwards, 45, AnimationNode::PhaseActive, AnimationNode::PhaseAfter, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(21, Timing::FillModeNone, 45, AnimationNode::PhaseActive, AnimationNode::PhaseAfter, timing))); // None - EXPECT_TRUE(isNull(calculateActiveTime(32, nullValue(), TimedItem::PhaseNone, TimedItem::PhaseNone, timing))); + EXPECT_TRUE(isNull(calculateActiveTime(32, Timing::FillModeNone, nullValue(), AnimationNode::PhaseNone, AnimationNode::PhaseNone, timing))); } -TEST(AnimationTimedItemCalculationsTest, ScaledActiveTime) +TEST(AnimationTimingCalculationsTest, ScaledActiveTime) { Timing timing; @@ -111,7 +98,7 @@ TEST(AnimationTimedItemCalculationsTest, ScaledActiveTime) EXPECT_EQ(std::numeric_limits<double>::infinity(), calculateScaledActiveTime(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), 0, timing)); } -TEST(AnimationTimedItemCalculationsTest, IterationTime) +TEST(AnimationTimingCalculationsTest, IterationTime) { Timing timing; @@ -131,7 +118,7 @@ TEST(AnimationTimedItemCalculationsTest, IterationTime) EXPECT_EQ(8, calculateIterationTime(12, 120, 20, 7, timing)); } -TEST(AnimationTimedItemCalculationsTest, CurrentIteration) +TEST(AnimationTimingCalculationsTest, CurrentIteration) { Timing timing; @@ -152,7 +139,7 @@ TEST(AnimationTimedItemCalculationsTest, CurrentIteration) EXPECT_EQ(3, calculateCurrentIteration(3.2, 3.1, 10, timing)); } -TEST(AnimationTimedItemCalculationsTest, DirectedTime) +TEST(AnimationTimingCalculationsTest, DirectedTime) { Timing timing; @@ -183,7 +170,7 @@ TEST(AnimationTimedItemCalculationsTest, DirectedTime) EXPECT_EQ(3, calculateDirectedTime(2, 20, 17, timing)); } -TEST(AnimationTimedItemCalculationsTest, TransformedTime) +TEST(AnimationTimingCalculationsTest, TransformedTime) { Timing timing; @@ -197,18 +184,18 @@ TEST(AnimationTimedItemCalculationsTest, TransformedTime) EXPECT_EQ(12, calculateTransformedTime(1, 20, 12, timing)); // PlaybackDirectionForwards with timing function - timing.timingFunction = StepsTimingFunction::create(4, false /* stepAtStart */); + timing.timingFunction = StepsTimingFunction::create(4, StepsTimingFunction::StepAtEnd); EXPECT_EQ(10, calculateTransformedTime(0, 20, 12, timing)); EXPECT_EQ(10, calculateTransformedTime(1, 20, 12, timing)); // PlaybackDirectionReverse - timing.timingFunction = 0; + timing.timingFunction = Timing::defaults().timingFunction; timing.direction = Timing::PlaybackDirectionReverse; EXPECT_EQ(8, calculateTransformedTime(0, 20, 12, timing)); EXPECT_EQ(8, calculateTransformedTime(1, 20, 12, timing)); // PlaybackDirectionReverse with timing function - timing.timingFunction = StepsTimingFunction::create(4, false /* stepAtStart */); + timing.timingFunction = StepsTimingFunction::create(4, StepsTimingFunction::StepAtEnd); EXPECT_EQ(5, calculateTransformedTime(0, 20, 12, timing)); EXPECT_EQ(5, calculateTransformedTime(1, 20, 12, timing)); diff --git a/chromium/third_party/WebKit/Source/core/animation/TimingInput.cpp b/chromium/third_party/WebKit/Source/core/animation/TimingInput.cpp new file mode 100644 index 00000000000..f9eeedaa668 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/TimingInput.cpp @@ -0,0 +1,154 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/TimingInput.h" + +#include "bindings/v8/Dictionary.h" +#include "core/css/parser/BisonCSSParser.h" +#include "core/css/resolver/CSSToStyleMap.h" + +namespace WebCore { + +void TimingInput::setStartDelay(Timing& timing, double startDelay) +{ + if (std::isfinite(startDelay)) + timing.startDelay = startDelay / 1000; + else + timing.startDelay = Timing::defaults().startDelay; +} + +void TimingInput::setEndDelay(Timing& timing, double endDelay) +{ + if (std::isfinite(endDelay)) + timing.endDelay = endDelay / 1000; + else + timing.endDelay = Timing::defaults().endDelay; +} + +void TimingInput::setFillMode(Timing& timing, const String& fillMode) +{ + if (fillMode == "none") { + timing.fillMode = Timing::FillModeNone; + } else if (fillMode == "backwards") { + timing.fillMode = Timing::FillModeBackwards; + } else if (fillMode == "both") { + timing.fillMode = Timing::FillModeBoth; + } else if (fillMode == "forwards") { + timing.fillMode = Timing::FillModeForwards; + } else { + timing.fillMode = Timing::defaults().fillMode; + } +} + +void TimingInput::setIterationStart(Timing& timing, double iterationStart) +{ + if (std::isfinite(iterationStart)) + timing.iterationStart = std::max<double>(iterationStart, 0); + else + timing.iterationStart = Timing::defaults().iterationStart; +} + +void TimingInput::setIterationCount(Timing& timing, double iterationCount) +{ + if (!std::isnan(iterationCount)) + timing.iterationCount = std::max<double>(iterationCount, 0); + else + timing.iterationCount = Timing::defaults().iterationCount; +} + +void TimingInput::setIterationDuration(Timing& timing, double iterationDuration) +{ + if (!std::isnan(iterationDuration) && iterationDuration >= 0) + timing.iterationDuration = iterationDuration / 1000; + else + timing.iterationDuration = Timing::defaults().iterationDuration; +} + +void TimingInput::setPlaybackRate(Timing& timing, double playbackRate) +{ + if (std::isfinite(playbackRate)) + timing.playbackRate = playbackRate; + else + timing.playbackRate = Timing::defaults().playbackRate; +} + +void TimingInput::setPlaybackDirection(Timing& timing, const String& direction) +{ + if (direction == "reverse") { + timing.direction = Timing::PlaybackDirectionReverse; + } else if (direction == "alternate") { + timing.direction = Timing::PlaybackDirectionAlternate; + } else if (direction == "alternate-reverse") { + timing.direction = Timing::PlaybackDirectionAlternateReverse; + } else { + timing.direction = Timing::defaults().direction; + } +} + +void TimingInput::setTimingFunction(Timing& timing, const String& timingFunctionString) +{ + if (RefPtrWillBeRawPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString)) + timing.timingFunction = CSSToStyleMap::mapAnimationTimingFunction(timingFunctionValue.get(), true); + else + timing.timingFunction = Timing::defaults().timingFunction; +} + +Timing TimingInput::convert(const Dictionary& timingInputDictionary) +{ + Timing result; + + // FIXME: This method needs to be refactored to handle invalid + // null, NaN, Infinity values better. + // See: http://www.w3.org/TR/WebIDL/#es-double + double startDelay = Timing::defaults().startDelay; + timingInputDictionary.get("delay", startDelay); + setStartDelay(result, startDelay); + + double endDelay = Timing::defaults().endDelay; + timingInputDictionary.get("endDelay", endDelay); + setEndDelay(result, endDelay); + + String fillMode; + timingInputDictionary.get("fill", fillMode); + setFillMode(result, fillMode); + + double iterationStart = Timing::defaults().iterationStart; + timingInputDictionary.get("iterationStart", iterationStart); + setIterationStart(result, iterationStart); + + double iterationCount = Timing::defaults().iterationCount; + timingInputDictionary.get("iterations", iterationCount); + setIterationCount(result, iterationCount); + + double iterationDuration = 0; + if (timingInputDictionary.get("duration", iterationDuration)) { + setIterationDuration(result, iterationDuration); + } + + double playbackRate = Timing::defaults().playbackRate; + timingInputDictionary.get("playbackRate", playbackRate); + setPlaybackRate(result, playbackRate); + + String direction; + timingInputDictionary.get("direction", direction); + setPlaybackDirection(result, direction); + + String timingFunctionString; + timingInputDictionary.get("easing", timingFunctionString); + setTimingFunction(result, timingFunctionString); + + result.assertValid(); + + return result; +} + +Timing TimingInput::convert(double duration) +{ + Timing result; + setIterationDuration(result, duration); + return result; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/TimingInput.h b/chromium/third_party/WebKit/Source/core/animation/TimingInput.h new file mode 100644 index 00000000000..c338201e092 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/TimingInput.h @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TimingInput_h +#define TimingInput_h + +#include "core/animation/Timing.h" + +namespace WebCore { + +class Dictionary; + +class TimingInput { +public: + static Timing convert(const Dictionary& timingInputDictionary); + static Timing convert(double duration); + + static void setStartDelay(Timing&, double startDelay); + static void setEndDelay(Timing&, double endDelay); + static void setFillMode(Timing&, const String& fillMode); + static void setIterationStart(Timing&, double iterationStart); + static void setIterationCount(Timing&, double iterationCount); + static void setIterationDuration(Timing&, double iterationDuration); + static void setPlaybackRate(Timing&, double playbackRate); + static void setPlaybackDirection(Timing&, const String& direction); + static void setTimingFunction(Timing&, const String& timingFunctionString); +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/TimingInputTest.cpp b/chromium/third_party/WebKit/Source/core/animation/TimingInputTest.cpp new file mode 100644 index 00000000000..57b6a0893bb --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/TimingInputTest.cpp @@ -0,0 +1,181 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/TimingInput.h" + +#include "bindings/v8/Dictionary.h" +#include "core/animation/AnimationNodeTiming.h" +#include "core/animation/AnimationTestHelper.h" +#include <gtest/gtest.h> +#include <v8.h> + +namespace WebCore { + +class AnimationTimingInputTest : public ::testing::Test { +protected: + AnimationTimingInputTest() + : m_isolate(v8::Isolate::GetCurrent()) + , m_scope(m_isolate) + { + } + + Timing applyTimingInputNumber(String timingProperty, double timingPropertyValue) + { + v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); + setV8ObjectPropertyAsNumber(timingInput, timingProperty, timingPropertyValue); + Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); + return TimingInput::convert(timingInputDictionary); + } + + Timing applyTimingInputString(String timingProperty, String timingPropertyValue) + { + v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); + setV8ObjectPropertyAsString(timingInput, timingProperty, timingPropertyValue); + Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); + return TimingInput::convert(timingInputDictionary); + } + + v8::Isolate* m_isolate; + +private: + V8TestingScope m_scope; +}; + +TEST_F(AnimationTimingInputTest, TimingInputStartDelay) +{ + EXPECT_EQ(1.1, applyTimingInputNumber("delay", 1100).startDelay); + EXPECT_EQ(-1, applyTimingInputNumber("delay", -1000).startDelay); + EXPECT_EQ(1, applyTimingInputString("delay", "1000").startDelay); + EXPECT_EQ(0, applyTimingInputString("delay", "1s").startDelay); + EXPECT_EQ(0, applyTimingInputString("delay", "Infinity").startDelay); + EXPECT_EQ(0, applyTimingInputString("delay", "-Infinity").startDelay); + EXPECT_EQ(0, applyTimingInputString("delay", "NaN").startDelay); + EXPECT_EQ(0, applyTimingInputString("delay", "rubbish").startDelay); +} + +TEST_F(AnimationTimingInputTest, TimingInputEndDelay) +{ + EXPECT_EQ(10, applyTimingInputNumber("endDelay", 10000).endDelay); + EXPECT_EQ(-2.5, applyTimingInputNumber("endDelay", -2500).endDelay); +} + +TEST_F(AnimationTimingInputTest, TimingInputFillMode) +{ + Timing::FillMode defaultFillMode = Timing::FillModeAuto; + + EXPECT_EQ(Timing::FillModeAuto, applyTimingInputString("fill", "auto").fillMode); + EXPECT_EQ(Timing::FillModeForwards, applyTimingInputString("fill", "forwards").fillMode); + EXPECT_EQ(Timing::FillModeNone, applyTimingInputString("fill", "none").fillMode); + EXPECT_EQ(Timing::FillModeBackwards, applyTimingInputString("fill", "backwards").fillMode); + EXPECT_EQ(Timing::FillModeBoth, applyTimingInputString("fill", "both").fillMode); + EXPECT_EQ(defaultFillMode, applyTimingInputString("fill", "everything!").fillMode); + EXPECT_EQ(defaultFillMode, applyTimingInputString("fill", "backwardsandforwards").fillMode); + EXPECT_EQ(defaultFillMode, applyTimingInputNumber("fill", 2).fillMode); +} + +TEST_F(AnimationTimingInputTest, TimingInputIterationStart) +{ + EXPECT_EQ(1.1, applyTimingInputNumber("iterationStart", 1.1).iterationStart); + EXPECT_EQ(0, applyTimingInputNumber("iterationStart", -1).iterationStart); + EXPECT_EQ(0, applyTimingInputString("iterationStart", "Infinity").iterationStart); + EXPECT_EQ(0, applyTimingInputString("iterationStart", "-Infinity").iterationStart); + EXPECT_EQ(0, applyTimingInputString("iterationStart", "NaN").iterationStart); + EXPECT_EQ(0, applyTimingInputString("iterationStart", "rubbish").iterationStart); +} + +TEST_F(AnimationTimingInputTest, TimingInputIterationCount) +{ + EXPECT_EQ(2.1, applyTimingInputNumber("iterations", 2.1).iterationCount); + EXPECT_EQ(0, applyTimingInputNumber("iterations", -1).iterationCount); + + Timing timing = applyTimingInputString("iterations", "Infinity"); + EXPECT_TRUE(std::isinf(timing.iterationCount)); + EXPECT_GT(timing.iterationCount, 0); + + EXPECT_EQ(0, applyTimingInputString("iterations", "-Infinity").iterationCount); + EXPECT_EQ(1, applyTimingInputString("iterations", "NaN").iterationCount); + EXPECT_EQ(1, applyTimingInputString("iterations", "rubbish").iterationCount); +} + +TEST_F(AnimationTimingInputTest, TimingInputIterationDuration) +{ + EXPECT_EQ(1.1, applyTimingInputNumber("duration", 1100).iterationDuration); + EXPECT_TRUE(std::isnan(applyTimingInputNumber("duration", -1000).iterationDuration)); + EXPECT_EQ(1, applyTimingInputString("duration", "1000").iterationDuration); + + Timing timing = applyTimingInputString("duration", "Infinity"); + EXPECT_TRUE(std::isinf(timing.iterationDuration)); + EXPECT_GT(timing.iterationDuration, 0); + + EXPECT_TRUE(std::isnan(applyTimingInputString("duration", "-Infinity").iterationDuration)); + EXPECT_TRUE(std::isnan(applyTimingInputString("duration", "NaN").iterationDuration)); + EXPECT_TRUE(std::isnan(applyTimingInputString("duration", "auto").iterationDuration)); + EXPECT_TRUE(std::isnan(applyTimingInputString("duration", "rubbish").iterationDuration)); +} + +TEST_F(AnimationTimingInputTest, TimingInputPlaybackRate) +{ + EXPECT_EQ(2.1, applyTimingInputNumber("playbackRate", 2.1).playbackRate); + EXPECT_EQ(-1, applyTimingInputNumber("playbackRate", -1).playbackRate); + EXPECT_EQ(1, applyTimingInputString("playbackRate", "Infinity").playbackRate); + EXPECT_EQ(1, applyTimingInputString("playbackRate", "-Infinity").playbackRate); + EXPECT_EQ(1, applyTimingInputString("playbackRate", "NaN").playbackRate); + EXPECT_EQ(1, applyTimingInputString("playbackRate", "rubbish").playbackRate); +} + +TEST_F(AnimationTimingInputTest, TimingInputDirection) +{ + Timing::PlaybackDirection defaultPlaybackDirection = Timing::PlaybackDirectionNormal; + + EXPECT_EQ(Timing::PlaybackDirectionNormal, applyTimingInputString("direction", "normal").direction); + EXPECT_EQ(Timing::PlaybackDirectionReverse, applyTimingInputString("direction", "reverse").direction); + EXPECT_EQ(Timing::PlaybackDirectionAlternate, applyTimingInputString("direction", "alternate").direction); + EXPECT_EQ(Timing::PlaybackDirectionAlternateReverse, applyTimingInputString("direction", "alternate-reverse").direction); + EXPECT_EQ(defaultPlaybackDirection, applyTimingInputString("direction", "rubbish").direction); + EXPECT_EQ(defaultPlaybackDirection, applyTimingInputNumber("direction", 2).direction); +} + +TEST_F(AnimationTimingInputTest, TimingInputTimingFunction) +{ + const RefPtr<TimingFunction> defaultTimingFunction = LinearTimingFunction::shared(); + + EXPECT_EQ(*CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease), *applyTimingInputString("easing", "ease").timingFunction); + EXPECT_EQ(*CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn), *applyTimingInputString("easing", "ease-in").timingFunction); + EXPECT_EQ(*CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut), *applyTimingInputString("easing", "ease-out").timingFunction); + EXPECT_EQ(*CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut), *applyTimingInputString("easing", "ease-in-out").timingFunction); + EXPECT_EQ(*LinearTimingFunction::shared(), *applyTimingInputString("easing", "linear").timingFunction); + EXPECT_EQ(*StepsTimingFunction::preset(StepsTimingFunction::Start), *applyTimingInputString("easing", "step-start").timingFunction); + EXPECT_EQ(*StepsTimingFunction::preset(StepsTimingFunction::Middle), *applyTimingInputString("easing", "step-middle").timingFunction); + EXPECT_EQ(*StepsTimingFunction::preset(StepsTimingFunction::End), *applyTimingInputString("easing", "step-end").timingFunction); + EXPECT_EQ(*CubicBezierTimingFunction::create(1, 1, 0.3, 0.3), *applyTimingInputString("easing", "cubic-bezier(1, 1, 0.3, 0.3)").timingFunction); + EXPECT_EQ(*StepsTimingFunction::create(3, StepsTimingFunction::StepAtStart), *applyTimingInputString("easing", "steps(3, start)").timingFunction); + EXPECT_EQ(*StepsTimingFunction::create(5, StepsTimingFunction::StepAtMiddle), *applyTimingInputString("easing", "steps(5, middle)").timingFunction); + EXPECT_EQ(*StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd), *applyTimingInputString("easing", "steps(5, end)").timingFunction); + EXPECT_EQ(*defaultTimingFunction, *applyTimingInputString("easing", "steps(5.6, end)").timingFunction); + EXPECT_EQ(*defaultTimingFunction, *applyTimingInputString("easing", "cubic-bezier(2, 2, 0.3, 0.3)").timingFunction); + EXPECT_EQ(*defaultTimingFunction, *applyTimingInputString("easing", "rubbish").timingFunction); + EXPECT_EQ(*defaultTimingFunction, *applyTimingInputNumber("easing", 2).timingFunction); + EXPECT_EQ(*defaultTimingFunction, *applyTimingInputString("easing", "initial").timingFunction); +} + +TEST_F(AnimationTimingInputTest, TimingInputEmpty) +{ + Timing controlTiming; + + v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); + Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); + Timing updatedTiming = TimingInput::convert(timingInputDictionary); + + EXPECT_EQ(controlTiming.startDelay, updatedTiming.startDelay); + EXPECT_EQ(controlTiming.fillMode, updatedTiming.fillMode); + EXPECT_EQ(controlTiming.iterationStart, updatedTiming.iterationStart); + EXPECT_EQ(controlTiming.iterationCount, updatedTiming.iterationCount); + EXPECT_TRUE(std::isnan(updatedTiming.iterationDuration)); + EXPECT_EQ(controlTiming.playbackRate, updatedTiming.playbackRate); + EXPECT_EQ(controlTiming.direction, updatedTiming.direction); + EXPECT_EQ(*controlTiming.timingFunction, *updatedTiming.timingFunction); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp index 41138cdd177..b401069725e 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp @@ -31,7 +31,7 @@ #include "config.h" #include "core/animation/css/CSSAnimatableValueFactory.h" -#include "CSSValueKeywords.h" +#include "core/CSSValueKeywords.h" #include "core/animation/AnimatableClipPathOperation.h" #include "core/animation/AnimatableColor.h" #include "core/animation/AnimatableDouble.h" @@ -41,6 +41,7 @@ #include "core/animation/AnimatableLengthBox.h" #include "core/animation/AnimatableLengthBoxAndBool.h" #include "core/animation/AnimatableLengthPoint.h" +#include "core/animation/AnimatableLengthPoint3D.h" #include "core/animation/AnimatableLengthSize.h" #include "core/animation/AnimatableRepeatable.h" #include "core/animation/AnimatableSVGLength.h" @@ -61,23 +62,13 @@ namespace WebCore { -static PassRefPtr<AnimatableValue> createFromLength(const Length& length, const RenderStyle& style) +static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLength(const Length& length, const RenderStyle& style) { switch (length.type()) { case Fixed: - return AnimatableLength::create(adjustFloatForAbsoluteZoom(length.value(), style), AnimatableLength::UnitTypePixels); case Percent: - return AnimatableLength::create(length.value(), AnimatableLength::UnitTypePercentage); - case ViewportPercentageWidth: - return AnimatableLength::create(length.value(), AnimatableLength::UnitTypeViewportWidth); - case ViewportPercentageHeight: - return AnimatableLength::create(length.value(), AnimatableLength::UnitTypeViewportHeight); - case ViewportPercentageMin: - return AnimatableLength::create(length.value(), AnimatableLength::UnitTypeViewportMin); - case ViewportPercentageMax: - return AnimatableLength::create(length.value(), AnimatableLength::UnitTypeViewportMax); case Calculated: - return AnimatableLength::create(CSSCalcValue::createExpressionNode(length.calculationValue()->expression(), style.effectiveZoom())); + return AnimatableLength::create(length, style.effectiveZoom()); case Auto: case Intrinsic: case MinIntrinsic: @@ -85,18 +76,20 @@ static PassRefPtr<AnimatableValue> createFromLength(const Length& length, const case MaxContent: case FillAvailable: case FitContent: - return AnimatableUnknown::create(CSSPrimitiveValue::create(length)); + return AnimatableUnknown::create(CSSPrimitiveValue::create(length, 1)); case Undefined: return AnimatableUnknown::create(CSSValueNone); case ExtendToZoom: // Does not apply to elements. + case DeviceWidth: + case DeviceHeight: ASSERT_NOT_REACHED(); - return 0; + return nullptr; } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } -static PassRefPtr<AnimatableValue> createFromLineHeight(const Length& length, const RenderStyle& style) +static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLineHeight(const Length& length, const RenderStyle& style) { if (length.type() == Percent) { double value = length.value(); @@ -108,12 +101,12 @@ static PassRefPtr<AnimatableValue> createFromLineHeight(const Length& length, co return createFromLength(length, style); } -inline static PassRefPtr<AnimatableValue> createFromDouble(double value, AnimatableDouble::Constraint constraint = AnimatableDouble::Unconstrained) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromDouble(double value, AnimatableDouble::Constraint constraint = AnimatableDouble::Unconstrained) { return AnimatableDouble::create(value, constraint); } -inline static PassRefPtr<AnimatableValue> createFromLengthBox(const LengthBox& lengthBox, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthBox(const LengthBox& lengthBox, const RenderStyle& style) { return AnimatableLengthBox::create( createFromLength(lengthBox.left(), style), @@ -122,14 +115,14 @@ inline static PassRefPtr<AnimatableValue> createFromLengthBox(const LengthBox& l createFromLength(lengthBox.bottom(), style)); } -static PassRefPtr<AnimatableValue> createFromBorderImageLength(const BorderImageLength& borderImageLength, const RenderStyle& style) +static PassRefPtrWillBeRawPtr<AnimatableValue> createFromBorderImageLength(const BorderImageLength& borderImageLength, const RenderStyle& style) { if (borderImageLength.isNumber()) return createFromDouble(borderImageLength.number()); return createFromLength(borderImageLength.length(), style); } -inline static PassRefPtr<AnimatableValue> createFromBorderImageLengthBox(const BorderImageLengthBox& borderImageLengthBox, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromBorderImageLengthBox(const BorderImageLengthBox& borderImageLengthBox, const RenderStyle& style) { return AnimatableLengthBox::create( createFromBorderImageLength(borderImageLengthBox.left(), style), @@ -138,35 +131,37 @@ inline static PassRefPtr<AnimatableValue> createFromBorderImageLengthBox(const B createFromBorderImageLength(borderImageLengthBox.bottom(), style)); } -inline static PassRefPtr<AnimatableValue> createFromLengthBoxAndBool(const LengthBox lengthBox, const bool flag, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthBoxAndBool(const LengthBox lengthBox, const bool flag, const RenderStyle& style) { return AnimatableLengthBoxAndBool::create( createFromLengthBox(lengthBox, style), flag); } -inline static PassRefPtr<AnimatableValue> createFromLengthPoint(const LengthPoint& lengthPoint, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthPoint(const LengthPoint& lengthPoint, const RenderStyle& style) { return AnimatableLengthPoint::create( createFromLength(lengthPoint.x(), style), createFromLength(lengthPoint.y(), style)); } -inline static PassRefPtr<AnimatableValue> createFromLengthSize(const LengthSize& lengthSize, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromLengthSize(const LengthSize& lengthSize, const RenderStyle& style) { return AnimatableLengthSize::create( createFromLength(lengthSize.width(), style), createFromLength(lengthSize.height(), style)); } -inline static PassRefPtr<AnimatableValue> createFromStyleImage(StyleImage* image) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromStyleImage(StyleImage* image) { - if (image) - return AnimatableImage::create(image); + if (image) { + if (RefPtrWillBeRawPtr<CSSValue> cssValue = image->cssValue()) + return AnimatableImage::create(cssValue.release()); + } return AnimatableUnknown::create(CSSValueNone); } -inline static PassRefPtr<AnimatableValue> createFromFillSize(const FillSize& fillSize, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFillSize(const FillSize& fillSize, const RenderStyle& style) { switch (fillSize.type) { case SizeLength: @@ -177,15 +172,22 @@ inline static PassRefPtr<AnimatableValue> createFromFillSize(const FillSize& fil return AnimatableUnknown::create(CSSPrimitiveValue::create(fillSize.type)); default: ASSERT_NOT_REACHED(); - return 0; + return nullptr; } } +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromBackgroundPosition(const Length& length, bool originIsSet, BackgroundEdgeOrigin origin, const RenderStyle& style) +{ + if (!originIsSet || origin == LeftEdge || origin == TopEdge) + return createFromLength(length, style); + return createFromLength(length.subtractFromOneHundredPercent(), style); +} + template<CSSPropertyID property> -inline static PassRefPtr<AnimatableValue> createFromFillLayers(const FillLayer* fillLayer, const RenderStyle& style) +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFillLayers(const FillLayer* fillLayer, const RenderStyle& style) { ASSERT(fillLayer); - Vector<RefPtr<AnimatableValue> > values; + WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > values; while (fillLayer) { if (property == CSSPropertyBackgroundImage || property == CSSPropertyWebkitMaskImage) { if (!fillLayer->isImageSet()) @@ -194,11 +196,11 @@ inline static PassRefPtr<AnimatableValue> createFromFillLayers(const FillLayer* } else if (property == CSSPropertyBackgroundPositionX || property == CSSPropertyWebkitMaskPositionX) { if (!fillLayer->isXPositionSet()) break; - values.append(createFromLength(fillLayer->xPosition(), style)); + values.append(createFromBackgroundPosition(fillLayer->xPosition(), fillLayer->isBackgroundXOriginSet(), fillLayer->backgroundXOrigin(), style)); } else if (property == CSSPropertyBackgroundPositionY || property == CSSPropertyWebkitMaskPositionY) { if (!fillLayer->isYPositionSet()) break; - values.append(createFromLength(fillLayer->yPosition(), style)); + values.append(createFromBackgroundPosition(fillLayer->yPosition(), fillLayer->isBackgroundYOriginSet(), fillLayer->backgroundYOrigin(), style)); } else if (property == CSSPropertyBackgroundSize || property == CSSPropertyWebkitMaskSize) { if (!fillLayer->isSizeSet()) break; @@ -211,42 +213,56 @@ inline static PassRefPtr<AnimatableValue> createFromFillLayers(const FillLayer* return AnimatableRepeatable::create(values); } -PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::createFromColor(CSSPropertyID property, const RenderStyle& style) +PassRefPtrWillBeRawPtr<AnimatableValue> CSSAnimatableValueFactory::createFromColor(CSSPropertyID property, const RenderStyle& style) { Color color = style.colorIncludingFallback(property, false); Color visitedLinkColor = style.colorIncludingFallback(property, true); - Color fallbackColor = style.color(); - Color fallbackVisitedLinkColor = style.visitedLinkColor(); - Color resolvedColor; + return AnimatableColor::create(color, visitedLinkColor); +} + +inline static PassRefPtrWillBeRawPtr<AnimatableValue> createFromShapeValue(ShapeValue* value) +{ + if (value) + return AnimatableShapeValue::create(value); + return AnimatableUnknown::create(CSSValueNone); +} - if (property == CSSPropertyBackgroundColor) { - // For background-color, invalid color means transparent and not currentColor. - fallbackColor = Color::transparent; - fallbackVisitedLinkColor = Color::transparent; +static double fontWeightToDouble(FontWeight fontWeight) +{ + switch (fontWeight) { + case FontWeight100: + return 100; + case FontWeight200: + return 200; + case FontWeight300: + return 300; + case FontWeight400: + return 400; + case FontWeight500: + return 500; + case FontWeight600: + return 600; + case FontWeight700: + return 700; + case FontWeight800: + return 800; + case FontWeight900: + return 900; } - if (color.isValid()) - resolvedColor = color; - else - resolvedColor = fallbackColor; - Color resolvedVisitedLinkColor; - if (visitedLinkColor.isValid()) - resolvedVisitedLinkColor = visitedLinkColor; - else - resolvedVisitedLinkColor = fallbackVisitedLinkColor; - return AnimatableColor::create(resolvedColor, resolvedVisitedLinkColor); + ASSERT_NOT_REACHED(); + return 400; } -inline static PassRefPtr<AnimatableValue> createFromShapeValue(ShapeValue* value) +static PassRefPtrWillBeRawPtr<AnimatableValue> createFromFontWeight(FontWeight fontWeight) { - if (value) - return AnimatableShapeValue::create(value); - return AnimatableUnknown::create(CSSValueAuto); + return createFromDouble(fontWeightToDouble(fontWeight)); } // FIXME: Generate this function. -PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID property, const RenderStyle& style) +PassRefPtrWillBeRawPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID property, const RenderStyle& style) { + ASSERT(CSSAnimations::isAnimatableProperty(property)); switch (property) { case CSSPropertyBackgroundColor: return createFromColor(property, style); @@ -307,7 +323,10 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop case CSSPropertyFillOpacity: return createFromDouble(style.fillOpacity()); case CSSPropertyFill: - return AnimatableSVGPaint::create(style.svgStyle()->fillPaintType(), style.svgStyle()->fillPaintColor(), style.svgStyle()->fillPaintUri()); + return AnimatableSVGPaint::create( + style.svgStyle()->fillPaintType(), style.svgStyle()->visitedLinkFillPaintType(), + style.svgStyle()->fillPaintColor(), style.svgStyle()->visitedLinkFillPaintColor(), + style.svgStyle()->fillPaintUri(), style.svgStyle()->visitedLinkFillPaintUri()); case CSSPropertyFlexGrow: return createFromDouble(style.flexGrow(), AnimatableDouble::InterpolationIsNonContinuousWithZero); case CSSPropertyFlexShrink: @@ -324,10 +343,10 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop // FIXME: Should we introduce an option to pass the computed font size here, allowing consumers to // enable text zoom rather than Text Autosizing? See http://crbug.com/227545. return createFromDouble(style.specifiedFontSize()); + case CSSPropertyFontWeight: + return createFromFontWeight(style.fontWeight()); case CSSPropertyHeight: return createFromLength(style.height(), style); - case CSSPropertyKerning: - return AnimatableSVGLength::create(style.kerning()); case CSSPropertyLightingColor: return createFromColor(property, style); case CSSPropertyListStyleImage: @@ -391,9 +410,12 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop case CSSPropertyStrokeOpacity: return createFromDouble(style.strokeOpacity()); case CSSPropertyStroke: - return AnimatableSVGPaint::create(style.svgStyle()->strokePaintType(), style.svgStyle()->strokePaintColor(), style.svgStyle()->strokePaintUri()); + return AnimatableSVGPaint::create( + style.svgStyle()->strokePaintType(), style.svgStyle()->visitedLinkStrokePaintType(), + style.svgStyle()->strokePaintColor(), style.svgStyle()->visitedLinkStrokePaintColor(), + style.svgStyle()->strokePaintUri(), style.svgStyle()->visitedLinkStrokePaintUri()); case CSSPropertyTextDecorationColor: - return createFromColor(property, style); + return AnimatableColor::create(style.textDecorationColor().resolve(style.color()), style.visitedLinkTextDecorationColor().resolve(style.visitedLinkColor())); case CSSPropertyTextIndent: return createFromLength(style.textIndent(), style); case CSSPropertyTextShadow: @@ -436,14 +458,19 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop return createFromFillLayers<CSSPropertyWebkitMaskPositionY>(style.maskLayers(), style); case CSSPropertyWebkitMaskSize: return createFromFillLayers<CSSPropertyWebkitMaskSize>(style.maskLayers(), style); - case CSSPropertyWebkitPerspective: + case CSSPropertyPerspective: return createFromDouble(style.perspective()); + case CSSPropertyPerspectiveOrigin: + ASSERT(RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); + return AnimatableLengthPoint::create( + createFromLength(style.perspectiveOriginX(), style), + createFromLength(style.perspectiveOriginY(), style)); case CSSPropertyWebkitPerspectiveOriginX: + ASSERT(!RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); return createFromLength(style.perspectiveOriginX(), style); case CSSPropertyWebkitPerspectiveOriginY: + ASSERT(!RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); return createFromLength(style.perspectiveOriginY(), style); - case CSSPropertyShapeInside: - return createFromShapeValue(style.shapeInside()); case CSSPropertyShapeOutside: return createFromShapeValue(style.shapeOutside()); case CSSPropertyShapeMargin: @@ -452,13 +479,22 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop return createFromDouble(style.shapeImageThreshold()); case CSSPropertyWebkitTextStrokeColor: return createFromColor(property, style); - case CSSPropertyWebkitTransform: + case CSSPropertyTransform: return AnimatableTransform::create(style.transform()); + case CSSPropertyTransformOrigin: + ASSERT(RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); + return AnimatableLengthPoint3D::create( + createFromLength(style.transformOriginX(), style), + createFromLength(style.transformOriginY(), style), + createFromDouble(style.transformOriginZ())); case CSSPropertyWebkitTransformOriginX: + ASSERT(!RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); return createFromLength(style.transformOriginX(), style); case CSSPropertyWebkitTransformOriginY: + ASSERT(!RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); return createFromLength(style.transformOriginY(), style); case CSSPropertyWebkitTransformOriginZ: + ASSERT(!RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()); return createFromDouble(style.transformOriginZ()); case CSSPropertyWidows: return createFromDouble(style.widows()); @@ -466,6 +502,10 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop return createFromLength(style.width(), style); case CSSPropertyWordSpacing: return createFromDouble(style.wordSpacing()); + case CSSPropertyVerticalAlign: + if (style.verticalAlign() == LENGTH) + return createFromLength(style.verticalAlignLength(), style); + return AnimatableUnknown::create(CSSPrimitiveValue::create(style.verticalAlign())); case CSSPropertyVisibility: return AnimatableVisibility::create(style.visibility()); case CSSPropertyZIndex: @@ -473,10 +513,9 @@ PassRefPtr<AnimatableValue> CSSAnimatableValueFactory::create(CSSPropertyID prop case CSSPropertyZoom: return createFromDouble(style.zoom()); default: - ASSERT_WITH_MESSAGE(!CSSAnimations::isAnimatableProperty(property), "Web Animations not yet implemented: Create AnimatableValue from render style: %s", getPropertyNameString(property).utf8().data()); ASSERT_NOT_REACHED(); // This return value is to avoid a release crash if possible. - return AnimatableUnknown::create(0); + return AnimatableUnknown::create(nullptr); } } diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.h b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.h index bac18c19241..7e6ea3dc03d 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.h +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.h @@ -31,7 +31,7 @@ #ifndef CSSAnimatableValueFactory_h #define CSSAnimatableValueFactory_h -#include "CSSPropertyNames.h" +#include "core/CSSPropertyNames.h" #include "core/animation/AnimatableValue.h" #include "wtf/PassRefPtr.h" @@ -41,9 +41,9 @@ class RenderStyle; class CSSAnimatableValueFactory { public: - static PassRefPtr<AnimatableValue> create(CSSPropertyID, const RenderStyle&); + static PassRefPtrWillBeRawPtr<AnimatableValue> create(CSSPropertyID, const RenderStyle&); private: - static PassRefPtr<AnimatableValue> createFromColor(CSSPropertyID, const RenderStyle&); + static PassRefPtrWillBeRawPtr<AnimatableValue> createFromColor(CSSPropertyID, const RenderStyle&); }; } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimationData.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimationData.cpp new file mode 100644 index 00000000000..bda53a4f535 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimationData.cpp @@ -0,0 +1,54 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/css/CSSAnimationData.h" + +#include "core/animation/Timing.h" + +namespace WebCore { + +CSSAnimationData::CSSAnimationData() +{ + m_nameList.append(initialName()); + m_iterationCountList.append(initialIterationCount()); + m_directionList.append(initialDirection()); + m_fillModeList.append(initialFillMode()); + m_playStateList.append(initialPlayState()); +} + +CSSAnimationData::CSSAnimationData(const CSSAnimationData& other) + : CSSTimingData(other) + , m_nameList(other.m_nameList) + , m_iterationCountList(other.m_iterationCountList) + , m_directionList(other.m_directionList) + , m_fillModeList(other.m_fillModeList) + , m_playStateList(other.m_playStateList) +{ +} + +const AtomicString& CSSAnimationData::initialName() +{ + DEFINE_STATIC_LOCAL(const AtomicString, name, ("none", AtomicString::ConstructFromLiteral)); + return name; +} + +bool CSSAnimationData::animationsMatchForStyleRecalc(const CSSAnimationData& other) const +{ + return m_nameList == other.m_nameList && m_playStateList == other.m_playStateList; +} + +Timing CSSAnimationData::convertToTiming(size_t index) const +{ + ASSERT(index < m_nameList.size()); + Timing timing = CSSTimingData::convertToTiming(index); + + timing.iterationCount = getRepeated(m_iterationCountList, index); + timing.direction = getRepeated(m_directionList, index); + timing.fillMode = getRepeated(m_fillModeList, index); + timing.assertValid(); + return timing; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimationData.h b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimationData.h new file mode 100644 index 00000000000..5a23feedcc4 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimationData.h @@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CSSAnimationData_h +#define CSSAnimationData_h + +#include "core/animation/Timing.h" +#include "core/animation/css/CSSTimingData.h" +#include "core/rendering/style/RenderStyleConstants.h" + +namespace WebCore { + +class CSSAnimationData FINAL : public CSSTimingData { +public: + static PassOwnPtrWillBeRawPtr<CSSAnimationData> create() + { + return adoptPtrWillBeNoop(new CSSAnimationData); + } + + static PassOwnPtrWillBeRawPtr<CSSAnimationData> create(const CSSAnimationData& animationData) + { + return adoptPtrWillBeNoop(new CSSAnimationData(animationData)); + } + + bool animationsMatchForStyleRecalc(const CSSAnimationData& other) const; + + Timing convertToTiming(size_t index) const; + + const Vector<AtomicString>& nameList() const { return m_nameList; } + const Vector<double>& iterationCountList() const { return m_iterationCountList; } + const Vector<Timing::PlaybackDirection>& directionList() const { return m_directionList; } + const Vector<Timing::FillMode>& fillModeList() const { return m_fillModeList; } + const Vector<EAnimPlayState>& playStateList() const { return m_playStateList; } + + Vector<AtomicString>& nameList() { return m_nameList; } + Vector<double>& iterationCountList() { return m_iterationCountList; } + Vector<Timing::PlaybackDirection>& directionList() { return m_directionList; } + Vector<Timing::FillMode>& fillModeList() { return m_fillModeList; } + Vector<EAnimPlayState>& playStateList() { return m_playStateList; } + + static const AtomicString& initialName(); + static Timing::PlaybackDirection initialDirection() { return Timing::PlaybackDirectionNormal; } + static Timing::FillMode initialFillMode() { return Timing::FillModeNone; } + static double initialIterationCount() { return 1.0; } + static EAnimPlayState initialPlayState() { return AnimPlayStatePlaying; } + +private: + CSSAnimationData(); + explicit CSSAnimationData(const CSSAnimationData&); + + Vector<AtomicString> m_nameList; + Vector<double> m_iterationCountList; + Vector<Timing::PlaybackDirection> m_directionList; + Vector<Timing::FillMode> m_fillModeList; + Vector<EAnimPlayState> m_playStateList; +}; + +} // namespace WebCore + +#endif // CSSAnimationData_h diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp index 807c59096b5..05e358ec619 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp @@ -31,25 +31,26 @@ #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/KeyframeAnimationEffect.h" +#include "core/animation/KeyframeEffectModel.h" #include "core/animation/css/CSSAnimatableValueFactory.h" +#include "core/animation/css/CSSPropertyEquality.h" +#include "core/animation/interpolation/LegacyStyleInterpolation.h" #include "core/css/CSSKeyframeRule.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/events/TransitionEvent.h" #include "core/events/WebKitAnimationEvent.h" #include "core/frame/UseCounter.h" -#include "core/frame/animation/CSSPropertyAnimation.h" -#include "core/platform/animation/CSSAnimationDataList.h" -#include "core/platform/animation/TimingFunction.h" +#include "core/rendering/RenderLayer.h" #include "core/rendering/RenderObject.h" #include "core/rendering/style/KeyframeList.h" +#include "platform/animation/TimingFunction.h" #include "public/platform/Platform.h" #include "wtf/BitArray.h" #include "wtf/HashSet.h" @@ -58,90 +59,81 @@ namespace WebCore { 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 KeyframeAnimationEffect::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: + if (RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()) + return CSSPropertyPerspectiveOrigin; + break; + case CSSPropertyWebkitTransformOriginX: + case CSSPropertyWebkitTransformOriginY: + case CSSPropertyWebkitTransformOriginZ: + if (RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled()) + return CSSPropertyTransformOrigin; + break; + 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<KeyframeAnimationEffect::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions) + AnimatableValueKeyframeVector& keyframes) { - ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); // When the element is null, use its parent for scoping purposes. const Element* elementForScoping = element ? element : &parentElement; const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl()); 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; - KeyframeAnimationEffect::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 (CSSAnimations::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)); @@ -159,16 +151,18 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El 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); @@ -194,7 +188,7 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El 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) @@ -203,129 +197,6 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El } 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; - KeyframeAnimationEffect::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(); - timing.hasIterationDuration = true; - } - 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(timing.iterationDuration >= 0 && std::isfinite(timing.iterationDuration)); - - isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused; - return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction(); } } // namespace @@ -347,21 +218,32 @@ const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* return 0; } -PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver) +CSSAnimations::CSSAnimations() { - ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); - 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(); } void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver) { const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0; - const CSSAnimationDataList* animationDataList = style.animations(); + +#if !ASSERT_ENABLED + // 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 CSSAnimationData* animationData = style.animations(); const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0; HashSet<AtomicString> inactive; @@ -370,12 +252,12 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element 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, @@ -384,85 +266,76 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element 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); + } continue; } } - Timing timing; - bool isPaused; - RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationData(animationData, timing, isPaused); - Vector<std::pair<KeyframeAnimationEffect::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 KeyframeAnimationEffect to skip normalization. - animations.add(InertAnimation::create(KeyframeAnimationEffect::create(keyframesAndTimingFunctions[j].first), timing, isPaused)); - } - update->startAnimation(animationName, animations); + 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, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused)); } } } ASSERT(inactive.isEmpty() || cssAnimations); - for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) - update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter)); + for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) { + ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); + 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_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations()); - m_previousCompositableValuesForAnimations.swap(update->compositableValuesForAnimations()); + // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here + // since we call this from recalc style. + // https://code.google.com/p/chromium/issues/detail?id=339847 + 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); - player->setPaused(!isFirstPlayerPaused); - } + 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) { + for (WillBeHeapVector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) { + const InertAnimation* inertAnimation = iter->animation.get(); 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->specified(), Animation::DefaultPriority, eventDelegate.release()); - Player* player = element->document().timeline()->createPlayer(animation.get()); - player->setPaused(inertAnimation->paused()); - element->document().cssPendingAnimations().add(player); - player->update(); - players.add(player); - } - m_animations.set(iter->name, players); + 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 @@ -470,15 +343,17 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element) // 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) { @@ -490,42 +365,46 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element) CSSPropertyID id = newTransition.id; InertAnimation* inertAnimation = newTransition.animation.get(); - OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id)); + OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(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); - KeyframeAnimationEffect* oldEffect = toKeyframeAnimationEffect(inertAnimation->effect()); - const KeyframeAnimationEffect::KeyframeVector& frames = oldEffect->getFrames(); - KeyframeAnimationEffect::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 = KeyframeAnimationEffect::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->specified(), 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()) { @@ -538,44 +417,37 @@ void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const } } - if (anim->duration() + anim->delay() <= 0) + if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style)) return; - - if (CSSPropertyAnimation::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 (!from->usesNonDefaultInterpolationWith(to.get())) + if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get())) + return; + + Timing timing = transitionData.convertToTiming(transitionIndex); + if (timing.startDelay + timing.iterationDuration <= 0) return; - KeyframeAnimationEffect::KeyframeVector keyframes; + 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<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::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()); } @@ -586,37 +458,41 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const 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 ASSERT_ENABLED // 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) { + id = propertyForAnimation(id); if (CSSAnimations::isAnimatableProperty(id)) listedProperties.set(id); else @@ -625,9 +501,9 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const // 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); } } } @@ -635,10 +511,11 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const 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); } } @@ -648,130 +525,129 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const 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(timedItem->specified().hasIterationDuration); - const double elapsedTime = timedItem->specified().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->specified().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::TransitionEventDelegate::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(); - if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase != currentPhase) && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) { + 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->specified(); + 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) { @@ -810,8 +686,8 @@ bool CSSAnimations::isAnimatableProperty(CSSPropertyID property) case CSSPropertyFloodColor: case CSSPropertyFloodOpacity: case CSSPropertyFontSize: + case CSSPropertyFontWeight: case CSSPropertyHeight: - case CSSPropertyKerning: case CSSPropertyLeft: case CSSPropertyLetterSpacing: case CSSPropertyLightingColor: @@ -848,6 +724,7 @@ bool CSSAnimations::isAnimatableProperty(CSSPropertyID property) case CSSPropertyTextIndent: case CSSPropertyTextShadow: case CSSPropertyTop: + case CSSPropertyVerticalAlign: case CSSPropertyVisibility: case CSSPropertyWebkitBackgroundSize: case CSSPropertyWebkitBorderHorizontalSpacing: @@ -868,29 +745,27 @@ bool CSSAnimations::isAnimatableProperty(CSSPropertyID property) case CSSPropertyWebkitMaskPositionX: case CSSPropertyWebkitMaskPositionY: case CSSPropertyWebkitMaskSize: - case CSSPropertyWebkitPerspective: - case CSSPropertyWebkitPerspectiveOriginX: - case CSSPropertyWebkitPerspectiveOriginY: - case CSSPropertyShapeInside: + case CSSPropertyPerspective: case CSSPropertyShapeOutside: case CSSPropertyShapeMargin: case CSSPropertyShapeImageThreshold: case CSSPropertyWebkitTextStrokeColor: - case CSSPropertyWebkitTransform: - case CSSPropertyWebkitTransformOriginX: - case CSSPropertyWebkitTransformOriginY: - case CSSPropertyWebkitTransformOriginZ: + case CSSPropertyTransform: case CSSPropertyWidows: case CSSPropertyWidth: case CSSPropertyWordSpacing: case CSSPropertyZIndex: case CSSPropertyZoom: return true; - // FIXME: Shorthands should not be present in this list, but - // CSSPropertyAnimation implements animation of these shorthands - // directly and makes use of this method. - case CSSPropertyFlex: - return !RuntimeEnabledFeatures::webAnimationsCSSEnabled(); + case CSSPropertyPerspectiveOrigin: + case CSSPropertyTransformOrigin: + return RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled(); + case CSSPropertyWebkitPerspectiveOriginX: + case CSSPropertyWebkitPerspectiveOriginY: + case CSSPropertyWebkitTransformOriginX: + case CSSPropertyWebkitTransformOriginY: + case CSSPropertyWebkitTransformOriginZ: + return !RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled(); default: return false; } @@ -911,4 +786,61 @@ const StylePropertyShorthand& CSSAnimations::animatableProperties() return propertyShorthand; } +// 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) +{ + visitor->trace(m_transitions); + visitor->trace(m_pendingUpdate); + visitor->trace(m_animations); + visitor->trace(m_previousActiveInterpolationsForAnimations); +} + +void CSSAnimationUpdate::trace(Visitor* visitor) +{ + visitor->trace(m_newTransitions); + visitor->trace(m_activeInterpolationsForAnimations); + visitor->trace(m_activeInterpolationsForTransitions); + visitor->trace(m_newAnimations); + visitor->trace(m_cancelledAnimationPlayers); +} + } // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.h b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.h index fb553d96232..31159f908ec 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.h +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.h @@ -32,11 +32,11 @@ #define CSSAnimations_h #include "core/animation/Animation.h" +#include "core/animation/AnimationPlayer.h" #include "core/animation/InertAnimation.h" -#include "core/animation/Player.h" +#include "core/animation/interpolation/Interpolation.h" #include "core/css/StylePropertySet.h" #include "core/dom/Document.h" -#include "core/platform/animation/CSSAnimationData.h" #include "core/rendering/style/RenderStyleConstants.h" #include "wtf/HashMap.h" #include "wtf/Vector.h" @@ -44,39 +44,40 @@ namespace WebCore { +class CSSTransitionData; class Element; class StylePropertyShorthand; class StyleResolver; class StyleRuleKeyframes; // This class stores the CSS Animations/Transitions information we use during a style recalc. -// This includes updates to animations/transitions as well as the CompositableValueMaps to be applied. -class CSSAnimationUpdate FINAL { +// This includes updates to animations/transitions as well as the Interpolations to be applied. +class CSSAnimationUpdate FINAL : public NoBaseWillBeGarbageCollectedFinalized<CSSAnimationUpdate> { public: - void startAnimation(AtomicString& animationName, const HashSet<RefPtr<InertAnimation> >& animations) + void startAnimation(AtomicString& animationName, PassRefPtrWillBeRawPtr<InertAnimation> animation) { NewAnimation newAnimation; newAnimation.name = animationName; - newAnimation.animations = animations; + newAnimation.animation = animation; m_newAnimations.append(newAnimation); } // Returns whether player has been cancelled and should be filtered during style application. - bool isCancelledAnimation(const Player* player) const { return m_cancelledAnimationPlayers.contains(player); } - void cancelAnimation(const AtomicString& name, const HashSet<RefPtr<Player> >& players) + bool isCancelledAnimation(const AnimationPlayer* player) const { return m_cancelledAnimationPlayers.contains(player); } + void cancelAnimation(const AtomicString& name, AnimationPlayer& player) { m_cancelledAnimationNames.append(name); - for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter) - m_cancelledAnimationPlayers.add(iter->get()); + m_cancelledAnimationPlayers.add(&player); } void toggleAnimationPaused(const AtomicString& name) { m_animationsWithPauseToggled.append(name); } - void startTransition(CSSPropertyID id, const AnimatableValue* from, const AnimatableValue* to, PassRefPtr<InertAnimation> animation) + void startTransition(CSSPropertyID id, CSSPropertyID eventId, const AnimatableValue* from, const AnimatableValue* to, PassRefPtrWillBeRawPtr<InertAnimation> animation) { NewTransition newTransition; newTransition.id = id; + newTransition.eventId = eventId; newTransition.from = from; newTransition.to = to; newTransition.animation = animation; @@ -86,29 +87,46 @@ public: void cancelTransition(CSSPropertyID id) { m_cancelledTransitions.add(id); } struct NewAnimation { + ALLOW_ONLY_INLINE_ALLOCATION(); + public: + void trace(Visitor* visitor) + { + visitor->trace(animation); + } + AtomicString name; - HashSet<RefPtr<InertAnimation> > animations; + RefPtrWillBeMember<InertAnimation> animation; }; - const Vector<NewAnimation>& newAnimations() const { return m_newAnimations; } + const WillBeHeapVector<NewAnimation>& newAnimations() const { return m_newAnimations; } const Vector<AtomicString>& cancelledAnimationNames() const { return m_cancelledAnimationNames; } - const HashSet<const Player*>& cancelledAnimationPlayers() const { return m_cancelledAnimationPlayers; } + const WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> >& cancelledAnimationAnimationPlayers() const { return m_cancelledAnimationPlayers; } const Vector<AtomicString>& animationsWithPauseToggled() const { return m_animationsWithPauseToggled; } struct NewTransition { + ALLOW_ONLY_INLINE_ALLOCATION(); + public: + void trace(Visitor* visitor) + { + visitor->trace(from); + visitor->trace(to); + visitor->trace(animation); + } + CSSPropertyID id; - const AnimatableValue* from; - const AnimatableValue* to; - RefPtr<InertAnimation> animation; + CSSPropertyID eventId; + RawPtrWillBeMember<const AnimatableValue> from; + RawPtrWillBeMember<const AnimatableValue> to; + RefPtrWillBeMember<InertAnimation> animation; }; - typedef HashMap<CSSPropertyID, NewTransition> NewTransitionMap; + typedef WillBeHeapHashMap<CSSPropertyID, NewTransition> NewTransitionMap; const NewTransitionMap& newTransitions() const { return m_newTransitions; } const HashSet<CSSPropertyID>& cancelledTransitions() const { return m_cancelledTransitions; } - void adoptCompositableValuesForAnimations(AnimationEffect::CompositableValueMap& newMap) { newMap.swap(m_compositableValuesForAnimations); } - void adoptCompositableValuesForTransitions(AnimationEffect::CompositableValueMap& newMap) { newMap.swap(m_compositableValuesForTransitions); } - const AnimationEffect::CompositableValueMap& compositableValuesForAnimations() const { return m_compositableValuesForAnimations; } - const AnimationEffect::CompositableValueMap& compositableValuesForTransitions() const { return m_compositableValuesForTransitions; } - AnimationEffect::CompositableValueMap& compositableValuesForAnimations() { return m_compositableValuesForAnimations; } + void adoptActiveInterpolationsForAnimations(WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >& newMap) { newMap.swap(m_activeInterpolationsForAnimations); } + void adoptActiveInterpolationsForTransitions(WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >& newMap) { newMap.swap(m_activeInterpolationsForTransitions); } + const WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >& activeInterpolationsForAnimations() const { return m_activeInterpolationsForAnimations; } + const WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >& activeInterpolationsForTransitions() const { return m_activeInterpolationsForTransitions; } + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >& activeInterpolationsForAnimations() { return m_activeInterpolationsForAnimations; } bool isEmpty() const { @@ -118,28 +136,35 @@ public: && m_animationsWithPauseToggled.isEmpty() && m_newTransitions.isEmpty() && m_cancelledTransitions.isEmpty() - && m_compositableValuesForAnimations.isEmpty() - && m_compositableValuesForTransitions.isEmpty(); + && m_activeInterpolationsForAnimations.isEmpty() + && m_activeInterpolationsForTransitions.isEmpty(); } + + void trace(Visitor*); + private: // Order is significant since it defines the order in which new animations // will be started. Note that there may be multiple animations present // with the same name, due to the way in which we split up animations with // incomplete keyframes. - Vector<NewAnimation> m_newAnimations; + WillBeHeapVector<NewAnimation> m_newAnimations; Vector<AtomicString> m_cancelledAnimationNames; - HashSet<const Player*> m_cancelledAnimationPlayers; + WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> > m_cancelledAnimationPlayers; Vector<AtomicString> m_animationsWithPauseToggled; NewTransitionMap m_newTransitions; HashSet<CSSPropertyID> m_cancelledTransitions; - AnimationEffect::CompositableValueMap m_compositableValuesForAnimations; - AnimationEffect::CompositableValueMap m_compositableValuesForTransitions; + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > m_activeInterpolationsForAnimations; + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > m_activeInterpolationsForTransitions; }; class CSSAnimations FINAL { + WTF_MAKE_NONCOPYABLE(CSSAnimations); + DISALLOW_ALLOCATION(); public: + CSSAnimations(); + // FIXME: This method is only used here and in the legacy animations // implementation. It should be made private or file-scope when the legacy // engine is removed. @@ -147,68 +172,91 @@ public: static bool isAnimatableProperty(CSSPropertyID); static const StylePropertyShorthand& animatableProperties(); + static bool isAllowedAnimation(CSSPropertyID); // FIXME: This should take a const ScopedStyleTree instead of a StyleResolver. // We should also change the Element* to a const Element* - static PassOwnPtr<CSSAnimationUpdate> calculateUpdate(Element*, const Element& parentElement, const RenderStyle&, RenderStyle* parentStyle, StyleResolver*); + static PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> calculateUpdate(Element*, const Element& parentElement, const RenderStyle&, RenderStyle* parentStyle, StyleResolver*); - void setPendingUpdate(PassOwnPtr<CSSAnimationUpdate> update) { m_pendingUpdate = update; } + void setPendingUpdate(PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> update) { m_pendingUpdate = update; } void maybeApplyPendingUpdate(Element*); bool isEmpty() const { return m_animations.isEmpty() && m_transitions.isEmpty() && !m_pendingUpdate; } void cancel(); + void trace(Visitor*); + private: - // Note that a single animation name may map to multiple players due to - // the way in which we split up animations with incomplete keyframes. - // FIXME: Once the Web Animations model supports groups, we could use a - // ParGroup to drive multiple animations from a single Player. - typedef HashMap<AtomicString, HashSet<RefPtr<Player> > > AnimationMap; struct RunningTransition { - Animation* transition; // The TransitionTimeline keeps the Players alive - const AnimatableValue* from; - const AnimatableValue* to; + ALLOW_ONLY_INLINE_ALLOCATION(); + public: + void trace(Visitor* visitor) + { + visitor->trace(from); + visitor->trace(to); + visitor->trace(player); + } + + RefPtrWillBeMember<AnimationPlayer> player; + RawPtrWillBeMember<const AnimatableValue> from; + RawPtrWillBeMember<const AnimatableValue> to; }; - typedef HashMap<CSSPropertyID, RunningTransition > TransitionMap; + + typedef WillBeHeapHashMap<AtomicString, RefPtrWillBeMember<AnimationPlayer> > AnimationMap; AnimationMap m_animations; + + typedef WillBeHeapHashMap<CSSPropertyID, RunningTransition> TransitionMap; TransitionMap m_transitions; - OwnPtr<CSSAnimationUpdate> m_pendingUpdate; - AnimationEffect::CompositableValueMap m_previousCompositableValuesForAnimations; + OwnPtrWillBeMember<CSSAnimationUpdate> m_pendingUpdate; + + WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > m_previousActiveInterpolationsForAnimations; static void calculateAnimationUpdate(CSSAnimationUpdate*, Element*, const Element& parentElement, const RenderStyle&, RenderStyle* parentStyle, StyleResolver*); static void calculateTransitionUpdate(CSSAnimationUpdate*, const Element*, const RenderStyle&); - static void calculateTransitionUpdateForProperty(CSSPropertyID, const CSSAnimationData*, const RenderStyle& oldStyle, const RenderStyle&, const TransitionMap* activeTransitions, CSSAnimationUpdate*, const Element*); + static void calculateTransitionUpdateForProperty(CSSPropertyID, CSSPropertyID eventId, const CSSTransitionData&, size_t transitionIndex, const RenderStyle& oldStyle, const RenderStyle&, const TransitionMap* activeTransitions, CSSAnimationUpdate*, const Element*); - static void calculateAnimationCompositableValues(CSSAnimationUpdate*, const Element*); - static void calculateTransitionCompositableValues(CSSAnimationUpdate*, const Element*); + static void calculateAnimationActiveInterpolations(CSSAnimationUpdate*, const Element*, double timelineCurrentTime); + static void calculateTransitionActiveInterpolations(CSSAnimationUpdate*, const Element*, double timelineCurrentTime); - class AnimationEventDelegate FINAL : public TimedItem::EventDelegate { + class AnimationEventDelegate FINAL : public AnimationNode::EventDelegate { public: AnimationEventDelegate(Element* target, const AtomicString& name) : m_target(target) , m_name(name) + , m_previousPhase(AnimationNode::PhaseNone) + , m_previousIteration(nullValue()) { } - virtual void onEventCondition(const TimedItem*, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration) OVERRIDE; + virtual void onEventCondition(const AnimationNode*) OVERRIDE; private: void maybeDispatch(Document::ListenerType, const AtomicString& eventName, double elapsedTime); Element* m_target; const AtomicString m_name; + AnimationNode::Phase m_previousPhase; + double m_previousIteration; }; - class TransitionEventDelegate FINAL : public TimedItem::EventDelegate { + class TransitionEventDelegate FINAL : public AnimationNode::EventDelegate { public: TransitionEventDelegate(Element* target, CSSPropertyID property) : m_target(target) , m_property(property) + , m_previousPhase(AnimationNode::PhaseNone) { } - virtual void onEventCondition(const TimedItem*, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration) OVERRIDE; + virtual void onEventCondition(const AnimationNode*) OVERRIDE; private: Element* m_target; const CSSPropertyID m_property; + AnimationNode::Phase m_previousPhase; }; }; } // namespace WebCore +namespace WTF { +template<> struct VectorTraits<WebCore::CSSAnimationUpdate::NewAnimation> : VectorTraitsBase<WebCore::CSSAnimationUpdate::NewAnimation> { + static const bool canInitializeWithMemset = true; +}; +} + #endif diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp new file mode 100644 index 00000000000..70ad01dbcb4 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp @@ -0,0 +1,318 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/css/CSSPropertyEquality.h" + +#include "core/animation/css/CSSAnimations.h" +#include "core/rendering/style/DataEquivalency.h" +#include "core/rendering/style/RenderStyle.h" +#include "core/rendering/style/ShadowList.h" + +namespace WebCore { + +namespace { + +template <CSSPropertyID property> +bool fillLayersEqual(const FillLayer* aLayer, const FillLayer* bLayer) +{ + if (aLayer == bLayer) + return true; + if (!aLayer || !bLayer) + return false; + while (aLayer && bLayer) { + switch (property) { + case CSSPropertyBackgroundPositionX: + case CSSPropertyWebkitMaskPositionX: + if (aLayer->xPosition() != bLayer->xPosition()) + return false; + break; + case CSSPropertyBackgroundPositionY: + case CSSPropertyWebkitMaskPositionY: + if (aLayer->yPosition() != bLayer->yPosition()) + return false; + break; + case CSSPropertyBackgroundSize: + case CSSPropertyWebkitBackgroundSize: + case CSSPropertyWebkitMaskSize: + if (!(aLayer->sizeLength() == bLayer->sizeLength())) + return false; + break; + case CSSPropertyBackgroundImage: + if (!dataEquivalent(aLayer->image(), bLayer->image())) + return false; + break; + default: + ASSERT_NOT_REACHED(); + return true; + } + + aLayer = aLayer->next(); + bLayer = bLayer->next(); + } + + // FIXME: Shouldn't this be return !aLayer && !bLayer; ? + return true; +} + +} + +bool CSSPropertyEquality::propertiesEqual(CSSPropertyID prop, const RenderStyle& a, const RenderStyle& b) +{ + switch (prop) { + case CSSPropertyBackgroundColor: + return a.backgroundColor().resolve(a.color()) == b.backgroundColor().resolve(b.color()) + && a.visitedLinkBackgroundColor().resolve(a.color()) == b.visitedLinkBackgroundColor().resolve(b.color()); + case CSSPropertyBackgroundImage: + return fillLayersEqual<CSSPropertyBackgroundImage>(a.backgroundLayers(), b.backgroundLayers()); + case CSSPropertyBackgroundPositionX: + return fillLayersEqual<CSSPropertyBackgroundPositionX>(a.backgroundLayers(), b.backgroundLayers()); + case CSSPropertyBackgroundPositionY: + return fillLayersEqual<CSSPropertyBackgroundPositionY>(a.backgroundLayers(), b.backgroundLayers()); + case CSSPropertyBackgroundSize: + return fillLayersEqual<CSSPropertyBackgroundSize>(a.backgroundLayers(), b.backgroundLayers()); + case CSSPropertyBaselineShift: + return dataEquivalent(a.baselineShiftValue(), b.baselineShiftValue()); + case CSSPropertyBorderBottomColor: + return a.borderBottomColor().resolve(a.color()) == b.borderBottomColor().resolve(b.color()) + && a.visitedLinkBorderBottomColor().resolve(a.color()) == b.visitedLinkBorderBottomColor().resolve(b.color()); + case CSSPropertyBorderBottomLeftRadius: + return a.borderBottomLeftRadius() == b.borderBottomLeftRadius(); + case CSSPropertyBorderBottomRightRadius: + return a.borderBottomRightRadius() == b.borderBottomRightRadius(); + case CSSPropertyBorderBottomWidth: + return a.borderBottomWidth() == b.borderBottomWidth(); + case CSSPropertyBorderImageOutset: + return a.borderImageOutset() == b.borderImageOutset(); + case CSSPropertyBorderImageSlice: + return a.borderImageSlices() == b.borderImageSlices(); + case CSSPropertyBorderImageSource: + return dataEquivalent(a.borderImageSource(), b.borderImageSource()); + case CSSPropertyBorderImageWidth: + return a.borderImageWidth() == b.borderImageWidth(); + case CSSPropertyBorderLeftColor: + return a.borderLeftColor().resolve(a.color()) == b.borderLeftColor().resolve(b.color()) + && a.visitedLinkBorderLeftColor().resolve(a.color()) == b.visitedLinkBorderLeftColor().resolve(b.color()); + case CSSPropertyBorderLeftWidth: + return a.borderLeftWidth() == b.borderLeftWidth(); + case CSSPropertyBorderRightColor: + return a.borderRightColor().resolve(a.color()) == b.borderRightColor().resolve(b.color()) + && a.visitedLinkBorderRightColor().resolve(a.color()) == b.visitedLinkBorderRightColor().resolve(b.color()); + case CSSPropertyBorderRightWidth: + return a.borderRightWidth() == b.borderRightWidth(); + case CSSPropertyBorderTopColor: + return a.borderTopColor().resolve(a.color()) == b.borderTopColor().resolve(b.color()) + && a.visitedLinkBorderTopColor().resolve(a.color()) == b.visitedLinkBorderTopColor().resolve(b.color()); + case CSSPropertyBorderTopLeftRadius: + return a.borderTopLeftRadius() == b.borderTopLeftRadius(); + case CSSPropertyBorderTopRightRadius: + return a.borderTopRightRadius() == b.borderTopRightRadius(); + case CSSPropertyBorderTopWidth: + return a.borderTopWidth() == b.borderTopWidth(); + case CSSPropertyBottom: + return a.bottom() == b.bottom(); + case CSSPropertyBoxShadow: + return dataEquivalent(a.boxShadow(), b.boxShadow()); + case CSSPropertyClip: + return a.clip() == b.clip(); + case CSSPropertyColor: + return a.color() == b.color() && a.visitedLinkColor() == b.visitedLinkColor(); + case CSSPropertyFill: { + const SVGRenderStyle& aSVG = *a.svgStyle(); + const SVGRenderStyle& bSVG = *b.svgStyle(); + return aSVG.fillPaintType() == bSVG.fillPaintType() + && (aSVG.fillPaintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || aSVG.fillPaintColor() == bSVG.fillPaintColor()) + && aSVG.visitedLinkFillPaintType() == bSVG.visitedLinkFillPaintType() + && (aSVG.visitedLinkFillPaintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || aSVG.visitedLinkFillPaintColor() == bSVG.visitedLinkFillPaintColor()); + } + case CSSPropertyFillOpacity: + return a.fillOpacity() == b.fillOpacity(); + case CSSPropertyFlexBasis: + return a.flexBasis() == b.flexBasis(); + case CSSPropertyFlexGrow: + return a.flexGrow() == b.flexGrow(); + case CSSPropertyFlexShrink: + return a.flexShrink() == b.flexShrink(); + case CSSPropertyFloodColor: + return a.floodColor() == b.floodColor(); + case CSSPropertyFloodOpacity: + return a.floodOpacity() == b.floodOpacity(); + case CSSPropertyFontSize: + // CSSPropertyFontSize: Must pass a specified size to setFontSize if Text Autosizing is enabled, but a computed size + // if text zoom is enabled (if neither is enabled it's irrelevant as they're probably the same). + // FIXME: Should we introduce an option to pass the computed font size here, allowing consumers to + // enable text zoom rather than Text Autosizing? See http://crbug.com/227545. + return a.specifiedFontSize() == b.specifiedFontSize(); + case CSSPropertyFontWeight: + return a.fontWeight() == b.fontWeight(); + case CSSPropertyHeight: + return a.height() == b.height(); + case CSSPropertyLeft: + return a.left() == b.left(); + case CSSPropertyLetterSpacing: + return a.letterSpacing() == b.letterSpacing(); + case CSSPropertyLightingColor: + return a.lightingColor() == b.lightingColor(); + case CSSPropertyLineHeight: + return a.specifiedLineHeight() == b.specifiedLineHeight(); + case CSSPropertyListStyleImage: + return dataEquivalent(a.listStyleImage(), b.listStyleImage()); + case CSSPropertyMarginBottom: + return a.marginBottom() == b.marginBottom(); + case CSSPropertyMarginLeft: + return a.marginLeft() == b.marginLeft(); + case CSSPropertyMarginRight: + return a.marginRight() == b.marginRight(); + case CSSPropertyMarginTop: + return a.marginTop() == b.marginTop(); + case CSSPropertyMaxHeight: + return a.maxHeight() == b.maxHeight(); + case CSSPropertyMaxWidth: + return a.maxWidth() == b.maxWidth(); + case CSSPropertyMinHeight: + return a.minHeight() == b.minHeight(); + case CSSPropertyMinWidth: + return a.minWidth() == b.minWidth(); + case CSSPropertyObjectPosition: + return a.objectPosition() == b.objectPosition(); + case CSSPropertyOpacity: + return a.opacity() == b.opacity(); + case CSSPropertyOrphans: + return a.orphans() == b.orphans(); + case CSSPropertyOutlineColor: + return a.outlineColor().resolve(a.color()) == b.outlineColor().resolve(b.color()) + && a.visitedLinkOutlineColor().resolve(a.color()) == b.visitedLinkOutlineColor().resolve(b.color()); + case CSSPropertyOutlineOffset: + return a.outlineOffset() == b.outlineOffset(); + case CSSPropertyOutlineWidth: + return a.outlineWidth() == b.outlineWidth(); + case CSSPropertyPaddingBottom: + return a.paddingBottom() == b.paddingBottom(); + case CSSPropertyPaddingLeft: + return a.paddingLeft() == b.paddingLeft(); + case CSSPropertyPaddingRight: + return a.paddingRight() == b.paddingRight(); + case CSSPropertyPaddingTop: + return a.paddingTop() == b.paddingTop(); + case CSSPropertyRight: + return a.right() == b.right(); + case CSSPropertyShapeImageThreshold: + return a.shapeImageThreshold() == b.shapeImageThreshold(); + case CSSPropertyShapeMargin: + return a.shapeMargin() == b.shapeMargin(); + case CSSPropertyShapeOutside: + return dataEquivalent(a.shapeOutside(), b.shapeOutside()); + case CSSPropertyStopColor: + return a.stopColor() == b.stopColor(); + case CSSPropertyStopOpacity: + return a.stopOpacity() == b.stopOpacity(); + case CSSPropertyStroke: { + const SVGRenderStyle& aSVG = *a.svgStyle(); + const SVGRenderStyle& bSVG = *b.svgStyle(); + return aSVG.strokePaintType() == bSVG.strokePaintType() + && (aSVG.strokePaintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || aSVG.strokePaintColor() == bSVG.strokePaintColor()) + && aSVG.visitedLinkStrokePaintType() == bSVG.visitedLinkStrokePaintType() + && (aSVG.visitedLinkStrokePaintType() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR || aSVG.visitedLinkStrokePaintColor() == bSVG.visitedLinkStrokePaintColor()); + } + case CSSPropertyStrokeDasharray: + return dataEquivalent(a.strokeDashArray(), b.strokeDashArray()); + case CSSPropertyStrokeDashoffset: + return dataEquivalent(a.strokeDashOffset(), b.strokeDashOffset()); + case CSSPropertyStrokeMiterlimit: + return a.strokeMiterLimit() == b.strokeMiterLimit(); + case CSSPropertyStrokeOpacity: + return a.strokeOpacity() == b.strokeOpacity(); + case CSSPropertyStrokeWidth: + return dataEquivalent(a.strokeWidth(), b.strokeWidth()); + case CSSPropertyTextDecorationColor: + return a.textDecorationColor().resolve(a.color()) == b.textDecorationColor().resolve(b.color()) + && a.visitedLinkTextDecorationColor().resolve(a.color()) == b.visitedLinkTextDecorationColor().resolve(b.color()); + case CSSPropertyTextIndent: + return a.textIndent() == b.textIndent(); + case CSSPropertyTextShadow: + return dataEquivalent(a.textShadow(), b.textShadow()); + case CSSPropertyTop: + return a.top() == b.top(); + case CSSPropertyVerticalAlign: + return a.verticalAlign() == b.verticalAlign() + && (a.verticalAlign() != LENGTH || a.verticalAlignLength() == b.verticalAlignLength()); + case CSSPropertyVisibility: + return a.visibility() == b.visibility(); + case CSSPropertyWebkitBackgroundSize: + return fillLayersEqual<CSSPropertyWebkitBackgroundSize>(a.backgroundLayers(), b.backgroundLayers()); + case CSSPropertyWebkitBorderHorizontalSpacing: + return a.horizontalBorderSpacing() == b.horizontalBorderSpacing(); + case CSSPropertyWebkitBorderVerticalSpacing: + return a.verticalBorderSpacing() == b.verticalBorderSpacing(); + case CSSPropertyWebkitBoxShadow: + return dataEquivalent(a.boxShadow(), b.boxShadow()); + case CSSPropertyWebkitClipPath: + return dataEquivalent(a.clipPath(), b.clipPath()); + case CSSPropertyWebkitColumnCount: + return a.columnCount() == b.columnCount(); + case CSSPropertyWebkitColumnGap: + return a.columnGap() == b.columnGap(); + case CSSPropertyWebkitColumnRuleColor: + return a.columnRuleColor().resolve(a.color()) == b.columnRuleColor().resolve(b.color()) + && a.visitedLinkColumnRuleColor().resolve(a.color()) == b.visitedLinkColumnRuleColor().resolve(b.color()); + case CSSPropertyWebkitColumnRuleWidth: + return a.columnRuleWidth() == b.columnRuleWidth(); + case CSSPropertyWebkitColumnWidth: + return a.columnWidth() == b.columnWidth(); + case CSSPropertyWebkitFilter: + return a.filter() == b.filter(); + case CSSPropertyWebkitMaskBoxImageOutset: + return a.maskBoxImageOutset() == b.maskBoxImageOutset(); + case CSSPropertyWebkitMaskBoxImageSlice: + return a.maskBoxImageSlices() == b.maskBoxImageSlices(); + case CSSPropertyWebkitMaskBoxImageSource: + return dataEquivalent(a.maskBoxImageSource(), b.maskBoxImageSource()); + case CSSPropertyWebkitMaskBoxImageWidth: + return a.maskBoxImageWidth() == b.maskBoxImageWidth(); + case CSSPropertyWebkitMaskImage: + return dataEquivalent(a.maskImage(), b.maskImage()); + case CSSPropertyWebkitMaskPositionX: + return fillLayersEqual<CSSPropertyWebkitMaskPositionX>(a.maskLayers(), b.maskLayers()); + case CSSPropertyWebkitMaskPositionY: + return fillLayersEqual<CSSPropertyWebkitMaskPositionY>(a.maskLayers(), b.maskLayers()); + case CSSPropertyWebkitMaskSize: + return fillLayersEqual<CSSPropertyWebkitMaskSize>(a.maskLayers(), b.maskLayers()); + case CSSPropertyPerspective: + return a.perspective() == b.perspective(); + case CSSPropertyPerspectiveOrigin: + return a.perspectiveOriginX() == b.perspectiveOriginX() && a.perspectiveOriginY() == b.perspectiveOriginY(); + case CSSPropertyWebkitPerspectiveOriginX: + return a.perspectiveOriginX() == b.perspectiveOriginX(); + case CSSPropertyWebkitPerspectiveOriginY: + return a.perspectiveOriginY() == b.perspectiveOriginY(); + case CSSPropertyWebkitTextStrokeColor: + return a.textStrokeColor().resolve(a.color()) == b.textStrokeColor().resolve(b.color()) + && a.visitedLinkTextStrokeColor().resolve(a.color()) == b.visitedLinkTextStrokeColor().resolve(b.color()); + case CSSPropertyTransform: + return a.transform() == b.transform(); + case CSSPropertyTransformOrigin: + return a.transformOriginX() == b.transformOriginX() && a.transformOriginY() == b.transformOriginY() && a.transformOriginZ() == b.transformOriginZ(); + case CSSPropertyWebkitTransformOriginX: + return a.transformOriginX() == b.transformOriginX(); + case CSSPropertyWebkitTransformOriginY: + return a.transformOriginY() == b.transformOriginY(); + case CSSPropertyWebkitTransformOriginZ: + return a.transformOriginZ() == b.transformOriginZ(); + case CSSPropertyWidows: + return a.widows() == b.widows(); + case CSSPropertyWidth: + return a.width() == b.width(); + case CSSPropertyWordSpacing: + return a.wordSpacing() == b.wordSpacing(); + case CSSPropertyZIndex: + return a.zIndex() == b.zIndex(); + case CSSPropertyZoom: + return a.zoom() == b.zoom(); + default: + ASSERT_NOT_REACHED(); + return true; + } +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.h b/chromium/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.h new file mode 100644 index 00000000000..0a436f0046c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.h @@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CSSPropertyEquality_h +#define CSSPropertyEquality_h + +#include "core/CSSPropertyNames.h" + +namespace WebCore { + +class RenderStyle; + +class CSSPropertyEquality { +public: + static bool propertiesEqual(CSSPropertyID, const RenderStyle&, const RenderStyle&); +}; + +} // namespace WebCore + +#endif // CSSPropertyEquality_h diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSTimingData.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSTimingData.cpp new file mode 100644 index 00000000000..9d628bd32c3 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSTimingData.cpp @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/css/CSSTimingData.h" + +#include "core/animation/Timing.h" + +namespace WebCore { + +CSSTimingData::CSSTimingData() +{ + m_delayList.append(initialDelay()); + m_durationList.append(initialDuration()); + m_timingFunctionList.append(initialTimingFunction()); +} + +CSSTimingData::CSSTimingData(const CSSTimingData& other) + : m_delayList(other.m_delayList) + , m_durationList(other.m_durationList) + , m_timingFunctionList(other.m_timingFunctionList) +{ +} + +Timing CSSTimingData::convertToTiming(size_t index) const +{ + Timing timing; + timing.startDelay = getRepeated(m_delayList, index); + timing.iterationDuration = getRepeated(m_durationList, index); + timing.timingFunction = getRepeated(m_timingFunctionList, index); + timing.assertValid(); + return timing; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSTimingData.h b/chromium/third_party/WebKit/Source/core/animation/css/CSSTimingData.h new file mode 100644 index 00000000000..33eab3b4b27 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSTimingData.h @@ -0,0 +1,50 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CSSTimingData_h +#define CSSTimingData_h + +#include "platform/animation/TimingFunction.h" +#include "platform/heap/Handle.h" +#include "wtf/Vector.h" + +namespace WebCore { + +struct Timing; + +class CSSTimingData : public NoBaseWillBeGarbageCollectedFinalized<CSSTimingData> { +public: + ~CSSTimingData() { } + + void trace(Visitor*) { } + + const Vector<double>& delayList() const { return m_delayList; } + const Vector<double>& durationList() const { return m_durationList; } + const Vector<RefPtr<TimingFunction> >& timingFunctionList() const { return m_timingFunctionList; } + + Vector<double>& delayList() { return m_delayList; } + Vector<double>& durationList() { return m_durationList; } + Vector<RefPtr<TimingFunction> >& timingFunctionList() { return m_timingFunctionList; } + + static double initialDelay() { return 0; } + static double initialDuration() { return 0; } + static PassRefPtr<TimingFunction> initialTimingFunction() { return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); } + + template <class T> static const T& getRepeated(const Vector<T>& v, size_t index) { return v[index % v.size()]; } + +protected: + CSSTimingData(); + explicit CSSTimingData(const CSSTimingData&); + + Timing convertToTiming(size_t index) const; + +private: + Vector<double> m_delayList; + Vector<double> m_durationList; + Vector<RefPtr<TimingFunction> > m_timingFunctionList; +}; + +} // namespace WebCore + +#endif // CSSTimingData_h diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSTransitionData.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSTransitionData.cpp new file mode 100644 index 00000000000..ad20621f30f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSTransitionData.cpp @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/css/CSSTransitionData.h" + +#include "core/animation/Timing.h" + +namespace WebCore { + +CSSTransitionData::CSSTransitionData() +{ + m_propertyList.append(initialProperty()); +} + +CSSTransitionData::CSSTransitionData(const CSSTransitionData& other) + : CSSTimingData(other) + , m_propertyList(other.m_propertyList) +{ +} + +bool CSSTransitionData::transitionsMatchForStyleRecalc(const CSSTransitionData& other) const +{ + return m_propertyList == other.m_propertyList; +} + +Timing CSSTransitionData::convertToTiming(size_t index) const +{ + ASSERT(index < m_propertyList.size()); + // Note that the backwards fill part is required for delay to work. + Timing timing = CSSTimingData::convertToTiming(index); + timing.fillMode = Timing::FillModeBoth; + return timing; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSTransitionData.h b/chromium/third_party/WebKit/Source/core/animation/css/CSSTransitionData.h new file mode 100644 index 00000000000..792afc77afa --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSTransitionData.h @@ -0,0 +1,81 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CSSTransitionData_h +#define CSSTransitionData_h + +#include "core/CSSPropertyNames.h" +#include "core/animation/css/CSSTimingData.h" +#include "wtf/Vector.h" + +namespace WebCore { + +class CSSTransitionData FINAL : public CSSTimingData { +public: + enum TransitionPropertyType { + TransitionNone, + TransitionSingleProperty, + TransitionUnknown, + TransitionAll + }; + + // FIXME: We shouldn't allow 'none' to be used alongside other properties. + struct TransitionProperty { + TransitionProperty(CSSPropertyID id) + : propertyType(TransitionSingleProperty) + , propertyId(id) + { + ASSERT(id != CSSPropertyInvalid); + } + + TransitionProperty(const String& string) + : propertyType(TransitionUnknown) + , propertyId(CSSPropertyInvalid) + , propertyString(string) + { + } + + TransitionProperty(TransitionPropertyType type) + : propertyType(type) + , propertyId(CSSPropertyInvalid) + { + ASSERT(type == TransitionNone || type == TransitionAll); + } + + bool operator==(const TransitionProperty& other) const { return propertyType == other.propertyType && propertyId == other.propertyId && propertyString == other.propertyString; } + + TransitionPropertyType propertyType; + CSSPropertyID propertyId; + String propertyString; + }; + + static PassOwnPtrWillBeRawPtr<CSSTransitionData> create() + { + return adoptPtrWillBeNoop(new CSSTransitionData); + } + + static PassOwnPtrWillBeRawPtr<CSSTransitionData> create(const CSSTransitionData& transitionData) + { + return adoptPtrWillBeNoop(new CSSTransitionData(transitionData)); + } + + bool transitionsMatchForStyleRecalc(const CSSTransitionData& other) const; + + Timing convertToTiming(size_t index) const; + + const Vector<TransitionProperty>& propertyList() const { return m_propertyList; } + Vector<TransitionProperty>& propertyList() { return m_propertyList; } + + static TransitionProperty initialProperty() { return TransitionProperty(TransitionAll); } + +private: + CSSTransitionData(); + explicit CSSTransitionData(const CSSTransitionData&); + + Vector<TransitionProperty> m_propertyList; +}; + +} // namespace WebCore + +#endif // CSSTransitionData_h diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/DefaultStyleInterpolation.h b/chromium/third_party/WebKit/Source/core/animation/interpolation/DefaultStyleInterpolation.h new file mode 100644 index 00000000000..174c12b9fdd --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/DefaultStyleInterpolation.h @@ -0,0 +1,46 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DefaultStyleInterpolation_h +#define DefaultStyleInterpolation_h + +#include "core/animation/interpolation/StyleInterpolation.h" +#include "core/css/resolver/StyleBuilder.h" + +namespace WebCore { + +class DefaultStyleInterpolation : public StyleInterpolation { +public: + static PassRefPtrWillBeRawPtr<DefaultStyleInterpolation> create(CSSValue* start, CSSValue* end, CSSPropertyID id) + { + return adoptRefWillBeNoop(new DefaultStyleInterpolation(start, end, id)); + } + + virtual void apply(StyleResolverState& state) const + { + StyleBuilder::applyProperty(m_id, state, toInterpolableBool(m_cachedValue.get())->value() ? m_endCSSValue.get() : m_startCSSValue.get()); + } + + virtual void trace(Visitor* visitor) OVERRIDE + { + StyleInterpolation::trace(visitor); + visitor->trace(m_startCSSValue); + visitor->trace(m_endCSSValue); + } + +private: + DefaultStyleInterpolation(CSSValue* start, CSSValue* end, CSSPropertyID id) + : StyleInterpolation(InterpolableBool::create(false), InterpolableBool::create(true), id) + , m_startCSSValue(start) + , m_endCSSValue(end) + { + } + + RefPtrWillBeMember<CSSValue> m_startCSSValue; + RefPtrWillBeMember<CSSValue> m_endCSSValue; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolation.cpp b/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolation.cpp new file mode 100644 index 00000000000..6a0be63049c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolation.cpp @@ -0,0 +1,152 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/interpolation/DeferredLegacyStyleInterpolation.h" + +#include "core/animation/interpolation/LegacyStyleInterpolation.h" +#include "core/css/CSSImageValue.h" +#include "core/css/CSSPrimitiveValue.h" +#include "core/css/CSSSVGDocumentValue.h" +#include "core/css/CSSShadowValue.h" +#include "core/css/CSSValueList.h" +#include "core/css/Pair.h" +#include "core/css/Rect.h" +#include "core/css/resolver/StyleResolver.h" +#include "core/css/resolver/StyleResolverState.h" + +namespace WebCore { + +void DeferredLegacyStyleInterpolation::apply(StyleResolverState& state) const +{ + RefPtrWillBeRawPtr<LegacyStyleInterpolation> innerInterpolation = LegacyStyleInterpolation::create( + StyleResolver::createAnimatableValueSnapshot(state, m_id, *m_startCSSValue), + StyleResolver::createAnimatableValueSnapshot(state, m_id, *m_endCSSValue), + m_id); + innerInterpolation->interpolate(m_cachedIteration, m_cachedFraction); + innerInterpolation->apply(state); +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSValue& value) +{ + switch (value.cssValueType()) { + case CSSValue::CSS_INHERIT: + return true; + case CSSValue::CSS_PRIMITIVE_VALUE: + return interpolationRequiresStyleResolve(toCSSPrimitiveValue(value)); + case CSSValue::CSS_VALUE_LIST: + return interpolationRequiresStyleResolve(toCSSValueList(value)); + case CSSValue::CSS_CUSTOM: + if (value.isImageValue()) + return interpolationRequiresStyleResolve(toCSSImageValue(value)); + if (value.isShadowValue()) + return interpolationRequiresStyleResolve(toCSSShadowValue(value)); + if (value.isSVGDocumentValue()) + return interpolationRequiresStyleResolve(toCSSSVGDocumentValue(value)); + // FIXME: consider other custom types. + return true; + case CSSValue::CSS_INITIAL: + // FIXME: should not require resolving styles for initial. + return true; + default: + ASSERT_NOT_REACHED(); + return true; + } +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSPrimitiveValue& primitiveValue) +{ + // FIXME: consider other types. + if (primitiveValue.isNumber() || primitiveValue.isPercentage() || primitiveValue.isAngle() || primitiveValue.isRGBColor() || primitiveValue.isURI()) + return false; + + if (primitiveValue.isLength()) + return primitiveValue.isFontRelativeLength() || primitiveValue.isViewportPercentageLength(); + + if (primitiveValue.isCalculated()) { + CSSLengthArray lengthArray(CSSPrimitiveValue::LengthUnitTypeCount); + primitiveValue.accumulateLengthArray(lengthArray); + return lengthArray[CSSPrimitiveValue::UnitTypeFontSize] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeFontXSize] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeRootFontSize] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeZeroCharacterWidth] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeViewportWidth] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeViewportHeight] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeViewportMin] != 0 + || lengthArray[CSSPrimitiveValue::UnitTypeViewportMax] != 0; + } + + if (Pair* pair = primitiveValue.getPairValue()) { + return interpolationRequiresStyleResolve(*pair->first()) + || interpolationRequiresStyleResolve(*pair->second()); + } + + if (Rect* rect = primitiveValue.getRectValue()) { + return interpolationRequiresStyleResolve(*rect->top()) + || interpolationRequiresStyleResolve(*rect->right()) + || interpolationRequiresStyleResolve(*rect->bottom()) + || interpolationRequiresStyleResolve(*rect->left()); + } + + if (Quad* quad = primitiveValue.getQuadValue()) { + return interpolationRequiresStyleResolve(*quad->top()) + || interpolationRequiresStyleResolve(*quad->right()) + || interpolationRequiresStyleResolve(*quad->bottom()) + || interpolationRequiresStyleResolve(*quad->left()); + } + + if (primitiveValue.isShape()) + return interpolationRequiresStyleResolve(*primitiveValue.getShapeValue()); + + CSSValueID id = primitiveValue.getValueID(); + bool isColor = ((id >= CSSValueAqua && id <= CSSValueTransparent) + || (id >= CSSValueAliceblue && id <= CSSValueYellowgreen) + || id == CSSValueGrey); + return (id != CSSValueNone) && !isColor; +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSImageValue& imageValue) +{ + return false; +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSShadowValue& shadowValue) +{ + return (shadowValue.x && interpolationRequiresStyleResolve(*shadowValue.x)) + || (shadowValue.y && interpolationRequiresStyleResolve(*shadowValue.y)) + || (shadowValue.blur && interpolationRequiresStyleResolve(*shadowValue.blur)) + || (shadowValue.spread && interpolationRequiresStyleResolve(*shadowValue.spread)) + || (shadowValue.style && interpolationRequiresStyleResolve(*shadowValue.style)) + || (shadowValue.color && interpolationRequiresStyleResolve(*shadowValue.color)); +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSSVGDocumentValue& documentValue) +{ + return true; +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSValueList& valueList) +{ + size_t length = valueList.length(); + for (size_t index = 0; index < length; ++index) { + if (interpolationRequiresStyleResolve(*valueList.item(index))) + return true; + } + return false; +} + +bool DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(const CSSBasicShape& shape) +{ + // FIXME: Should determine the specific shape, and inspect the members. + return false; +} + +void DeferredLegacyStyleInterpolation::trace(Visitor* visitor) +{ + visitor->trace(m_startCSSValue); + visitor->trace(m_endCSSValue); + StyleInterpolation::trace(visitor); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolation.h b/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolation.h new file mode 100644 index 00000000000..f1737bae269 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolation.h @@ -0,0 +1,53 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DeferredLegacyStyleInterpolation_h +#define DeferredLegacyStyleInterpolation_h + +#include "core/animation/interpolation/StyleInterpolation.h" +#include "core/css/CSSValue.h" + +namespace WebCore { + +class CSSBasicShape; +class CSSImageValue; +class CSSPrimitiveValue; +class CSSShadowValue; +class CSSSVGDocumentValue; +class CSSValueList; + +class DeferredLegacyStyleInterpolation : public StyleInterpolation { +public: + static PassRefPtrWillBeRawPtr<DeferredLegacyStyleInterpolation> create(PassRefPtrWillBeRawPtr<CSSValue> start, PassRefPtrWillBeRawPtr<CSSValue> end, CSSPropertyID id) + { + return adoptRefWillBeNoop(new DeferredLegacyStyleInterpolation(start, end, id)); + } + + virtual void apply(StyleResolverState&) const OVERRIDE; + + virtual void trace(Visitor*) OVERRIDE; + + static bool interpolationRequiresStyleResolve(const CSSValue&); + static bool interpolationRequiresStyleResolve(const CSSPrimitiveValue&); + static bool interpolationRequiresStyleResolve(const CSSImageValue&); + static bool interpolationRequiresStyleResolve(const CSSShadowValue&); + static bool interpolationRequiresStyleResolve(const CSSSVGDocumentValue&); + static bool interpolationRequiresStyleResolve(const CSSValueList&); + static bool interpolationRequiresStyleResolve(const CSSBasicShape&); + +private: + DeferredLegacyStyleInterpolation(PassRefPtrWillBeRawPtr<CSSValue> start, PassRefPtrWillBeRawPtr<CSSValue> end, CSSPropertyID id) + : StyleInterpolation(InterpolableNumber::create(0), InterpolableNumber::create(1), id) + , m_startCSSValue(start) + , m_endCSSValue(end) + { + } + + RefPtrWillBeMember<CSSValue> m_startCSSValue; + RefPtrWillBeMember<CSSValue> m_endCSSValue; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolationTest.cpp b/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolationTest.cpp new file mode 100644 index 00000000000..4c851f9648f --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/DeferredLegacyStyleInterpolationTest.cpp @@ -0,0 +1,94 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/interpolation/DeferredLegacyStyleInterpolation.h" + +#include "core/css/CSSInheritedValue.h" +#include "core/css/CSSPrimitiveValue.h" +#include "core/css/CSSValueList.h" +#include "core/css/StylePropertySet.h" +#include "core/css/parser/BisonCSSParser.h" + +#include <gtest/gtest.h> + +namespace WebCore { + +class AnimationDeferredLegacyStyleInterpolationTest : public ::testing::Test { +protected: + static bool test(CSSPropertyID propertyID, const String& string) + { + CSSParserMode parserMode = HTMLStandardMode; + if (propertyID == CSSPropertyFloodColor) + parserMode = SVGAttributeMode; + RefPtrWillBeRawPtr<MutableStylePropertySet> dummyStyle = MutableStylePropertySet::create(); + bool parseSuccess = BisonCSSParser::parseValue(dummyStyle.get(), propertyID, string, false, parserMode, 0); + ASSERT_UNUSED(parseSuccess, parseSuccess); + return DeferredLegacyStyleInterpolation::interpolationRequiresStyleResolve(*dummyStyle->getPropertyCSSValue(propertyID)); + } +}; + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Inherit) +{ + EXPECT_TRUE(test(CSSPropertyCaptionSide, "inherit")); +} + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Color) +{ + EXPECT_FALSE(test(CSSPropertyColor, "rgb(10, 20, 30)")); + EXPECT_FALSE(test(CSSPropertyColor, "aqua")); + EXPECT_FALSE(test(CSSPropertyColor, "yellow")); + EXPECT_FALSE(test(CSSPropertyColor, "transparent")); + EXPECT_FALSE(test(CSSPropertyFloodColor, "aliceblue")); + EXPECT_FALSE(test(CSSPropertyFloodColor, "yellowgreen")); + EXPECT_FALSE(test(CSSPropertyFloodColor, "grey")); + EXPECT_TRUE(test(CSSPropertyColor, "currentcolor")); +} + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Relative) +{ + EXPECT_TRUE(test(CSSPropertyFontWeight, "bolder")); + EXPECT_TRUE(test(CSSPropertyFontWeight, "lighter")); + EXPECT_TRUE(test(CSSPropertyFontSize, "smaller")); + EXPECT_TRUE(test(CSSPropertyFontSize, "larger")); +} + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Length) +{ + EXPECT_FALSE(test(CSSPropertyWidth, "10px")); + EXPECT_TRUE(test(CSSPropertyWidth, "10em")); + EXPECT_TRUE(test(CSSPropertyWidth, "10vh")); +} + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Number) +{ + EXPECT_FALSE(test(CSSPropertyOpacity, "0.5")); +} + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Transform) +{ + EXPECT_TRUE(test(CSSPropertyTransform, "translateX(1em)")); + EXPECT_FALSE(test(CSSPropertyTransform, "translateY(20px)")); + EXPECT_FALSE(test(CSSPropertyTransform, "skewX(10rad) perspective(400px)")); + EXPECT_TRUE(test(CSSPropertyTransform, "skewX(20rad) perspective(50em)")); +} + +TEST_F(AnimationDeferredLegacyStyleInterpolationTest, Filter) +{ + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "hue-rotate(180deg) blur(6px)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "grayscale(0) blur(0px)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "none")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "brightness(0) contrast(0)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "drop-shadow(20px 10px green)")); + EXPECT_TRUE(test(CSSPropertyWebkitFilter, "drop-shadow(20px 10vw green)")); + EXPECT_TRUE(test(CSSPropertyWebkitFilter, "drop-shadow(0px 0px 0px currentcolor)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "opacity(1)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "saturate(0)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "grayscale(1)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "invert(1)")); + EXPECT_FALSE(test(CSSPropertyWebkitFilter, "sepia(1)")); + EXPECT_TRUE(test(CSSPropertyWebkitFilter, "url(#svgfilter)")); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/Interpolation.cpp b/chromium/third_party/WebKit/Source/core/animation/interpolation/Interpolation.cpp new file mode 100644 index 00000000000..1ba5c6d74a8 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/Interpolation.cpp @@ -0,0 +1,63 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/interpolation/Interpolation.h" + +namespace WebCore { + +DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(Interpolation); + +namespace { + +bool typesMatch(const InterpolableValue* start, const InterpolableValue* end) +{ + if (start->isNumber()) + return end->isNumber(); + if (start->isBool()) + return end->isBool(); + if (start->isAnimatableValue()) + return end->isAnimatableValue(); + if (!(start->isList() && end->isList())) + return false; + const InterpolableList* startList = toInterpolableList(start); + const InterpolableList* endList = toInterpolableList(end); + if (startList->length() != endList->length()) + return false; + for (size_t i = 0; i < startList->length(); ++i) { + if (!typesMatch(startList->get(i), endList->get(i))) + return false; + } + return true; +} + +} + +Interpolation::Interpolation(PassOwnPtrWillBeRawPtr<InterpolableValue> start, PassOwnPtrWillBeRawPtr<InterpolableValue> end) + : m_start(start) + , m_end(end) + , m_cachedFraction(0) + , m_cachedIteration(0) + , m_cachedValue(m_start->clone()) +{ + RELEASE_ASSERT(typesMatch(m_start.get(), m_end.get())); +} + +void Interpolation::interpolate(int iteration, double fraction) const +{ + if (m_cachedFraction != fraction || m_cachedIteration != iteration) { + m_cachedValue = m_start->interpolate(*m_end, fraction); + m_cachedIteration = iteration; + m_cachedFraction = fraction; + } +} + +void Interpolation::trace(Visitor* visitor) +{ + visitor->trace(m_start); + visitor->trace(m_end); + visitor->trace(m_cachedValue); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/Interpolation.h b/chromium/third_party/WebKit/Source/core/animation/interpolation/Interpolation.h new file mode 100644 index 00000000000..ffa62b0cfca --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/Interpolation.h @@ -0,0 +1,47 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Interpolation_h +#define Interpolation_h + +#include "core/animation/InterpolableValue.h" +#include "platform/heap/Handle.h" + +namespace WebCore { + +class Interpolation : public RefCountedWillBeGarbageCollected<Interpolation> { + DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(Interpolation); +public: + static PassRefPtrWillBeRawPtr<Interpolation> create(PassOwnPtrWillBeRawPtr<InterpolableValue> start, PassOwnPtrWillBeRawPtr<InterpolableValue> end) + { + return adoptRefWillBeNoop(new Interpolation(start, end)); + } + + void interpolate(int iteration, double fraction) const; + + virtual bool isStyleInterpolation() const { return false; } + virtual bool isLegacyStyleInterpolation() const { return false; } + + virtual void trace(Visitor*); + +protected: + const OwnPtrWillBeMember<InterpolableValue> m_start; + const OwnPtrWillBeMember<InterpolableValue> m_end; + + mutable double m_cachedFraction; + mutable int m_cachedIteration; + mutable OwnPtrWillBeMember<InterpolableValue> m_cachedValue; + + Interpolation(PassOwnPtrWillBeRawPtr<InterpolableValue> start, PassOwnPtrWillBeRawPtr<InterpolableValue> end); + +private: + InterpolableValue* getCachedValueForTesting() const { return m_cachedValue.get(); } + + friend class AnimationInterpolableValueTest; + friend class AnimationInterpolationEffectTest; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/LegacyStyleInterpolation.h b/chromium/third_party/WebKit/Source/core/animation/interpolation/LegacyStyleInterpolation.h new file mode 100644 index 00000000000..d16bd994f4a --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/LegacyStyleInterpolation.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LegacyStyleInterpolation_h +#define LegacyStyleInterpolation_h + +#include "core/animation/interpolation/StyleInterpolation.h" +#include "core/css/resolver/AnimatedStyleBuilder.h" + +namespace WebCore { + +class LegacyStyleInterpolation : public StyleInterpolation { +public: + static PassRefPtrWillBeRawPtr<LegacyStyleInterpolation> create(PassRefPtrWillBeRawPtr<AnimatableValue> start, PassRefPtrWillBeRawPtr<AnimatableValue> end, CSSPropertyID id) + { + return adoptRefWillBeNoop(new LegacyStyleInterpolation(InterpolableAnimatableValue::create(start), InterpolableAnimatableValue::create(end), id)); + } + + virtual void apply(StyleResolverState& state) const OVERRIDE + { + AnimatedStyleBuilder::applyProperty(m_id, state, currentValue().get()); + } + + virtual bool isLegacyStyleInterpolation() const OVERRIDE FINAL { return true; } + PassRefPtrWillBeRawPtr<AnimatableValue> currentValue() const + { + InterpolableAnimatableValue* value = static_cast<InterpolableAnimatableValue*>(m_cachedValue.get()); + return value->value(); + } + + virtual void trace(Visitor* visitor) OVERRIDE + { + StyleInterpolation::trace(visitor); + } + +private: + LegacyStyleInterpolation(PassOwnPtrWillBeRawPtr<InterpolableValue> start, PassOwnPtrWillBeRawPtr<InterpolableValue> end, CSSPropertyID id) + : StyleInterpolation(start, end, id) + { + } +}; + +DEFINE_TYPE_CASTS(LegacyStyleInterpolation, Interpolation, value, value->isLegacyStyleInterpolation(), value.isLegacyStyleInterpolation()); + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolation.cpp b/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolation.cpp new file mode 100644 index 00000000000..013d3067be6 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolation.cpp @@ -0,0 +1,109 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/interpolation/LengthStyleInterpolation.h" + +#include "core/css/CSSCalculationValue.h" +#include "core/css/resolver/StyleBuilder.h" + +namespace WebCore { + +bool LengthStyleInterpolation::canCreateFrom(const CSSValue& value) +{ + if (value.isPrimitiveValue()) { + const CSSPrimitiveValue& primitiveValue = WebCore::toCSSPrimitiveValue(value); + if (primitiveValue.cssCalcValue()) + return true; + + CSSPrimitiveValue::LengthUnitType type; + // Only returns true if the type is a primitive length unit. + return CSSPrimitiveValue::unitTypeToLengthUnitType(primitiveValue.primitiveType(), type); + } + return value.isCalcValue(); +} + +PassOwnPtrWillBeRawPtr<InterpolableValue> LengthStyleInterpolation::lengthToInterpolableValue(CSSValue* value) +{ + OwnPtrWillBeRawPtr<InterpolableList> result = InterpolableList::create(CSSPrimitiveValue::LengthUnitTypeCount); + CSSPrimitiveValue* primitive = toCSSPrimitiveValue(value); + + CSSLengthArray array; + for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) + array.append(0); + primitive->accumulateLengthArray(array); + + for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) + result->set(i, InterpolableNumber::create(array.at(i))); + + return result.release(); +} + +namespace { + +static CSSPrimitiveValue::UnitType toUnitType(int lengthUnitType) +{ + return static_cast<CSSPrimitiveValue::UnitType>(CSSPrimitiveValue::lengthUnitTypeToUnitType(static_cast<CSSPrimitiveValue::LengthUnitType>(lengthUnitType))); +} + +static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> constructCalcExpression(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> previous, InterpolableList* list, size_t position) +{ + while (position != CSSPrimitiveValue::LengthUnitTypeCount) { + const InterpolableNumber *subValue = toInterpolableNumber(list->get(position)); + if (subValue->value()) { + RefPtrWillBeRawPtr<CSSCalcExpressionNode> next; + if (previous) + next = CSSCalcValue::createExpressionNode(previous, CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(subValue->value(), toUnitType(position))), CalcAdd); + else + next = CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(subValue->value(), toUnitType(position))); + return constructCalcExpression(next, list, position + 1); + } + position++; + } + return previous; +} + +} + +PassRefPtrWillBeRawPtr<CSSValue> LengthStyleInterpolation::interpolableValueToLength(InterpolableValue* value, ValueRange range) +{ + InterpolableList* listValue = toInterpolableList(value); + unsigned unitCount = 0; + for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { + const InterpolableNumber* subValue = toInterpolableNumber(listValue->get(i)); + if (subValue->value()) { + unitCount++; + } + } + + switch (unitCount) { + case 0: + return CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_PX); + case 1: + for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { + const InterpolableNumber* subValue = toInterpolableNumber(listValue->get(i)); + double value = subValue->value(); + if (value) { + if (range == ValueRangeNonNegative && value < 0) + value = 0; + return CSSPrimitiveValue::create(value, toUnitType(i)); + } + } + ASSERT_NOT_REACHED(); + default: + return CSSPrimitiveValue::create(CSSCalcValue::create(constructCalcExpression(nullptr, listValue, 0), range)); + } +} + +void LengthStyleInterpolation::apply(StyleResolverState& state) const +{ + StyleBuilder::applyProperty(m_id, state, interpolableValueToLength(m_cachedValue.get(), m_range).get()); +} + +void LengthStyleInterpolation::trace(Visitor* visitor) +{ + StyleInterpolation::trace(visitor); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolation.h b/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolation.h new file mode 100644 index 00000000000..d344957da8c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolation.h @@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LengthStyleInterpolation_h +#define LengthStyleInterpolation_h + +#include "core/animation/interpolation/StyleInterpolation.h" +#include "platform/Length.h" + +namespace WebCore { + +class LengthStyleInterpolation : public StyleInterpolation { +public: + static PassRefPtrWillBeRawPtr<LengthStyleInterpolation> create(CSSValue* start, CSSValue* end, CSSPropertyID id, ValueRange range) + { + return adoptRefWillBeNoop(new LengthStyleInterpolation(lengthToInterpolableValue(start), lengthToInterpolableValue(end), id, range)); + } + + static bool canCreateFrom(const CSSValue&); + + virtual void apply(StyleResolverState&) const OVERRIDE; + + virtual void trace(Visitor*) OVERRIDE; + +private: + LengthStyleInterpolation(PassOwnPtrWillBeRawPtr<InterpolableValue> start, PassOwnPtrWillBeRawPtr<InterpolableValue> end, CSSPropertyID id, ValueRange range) + : StyleInterpolation(start, end, id) + , m_range(range) + { } + + static PassOwnPtrWillBeRawPtr<InterpolableValue> lengthToInterpolableValue(CSSValue*); + static PassRefPtrWillBeRawPtr<CSSValue> interpolableValueToLength(InterpolableValue*, ValueRange); + + ValueRange m_range; + + friend class AnimationLengthStyleInterpolationTest; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolationTest.cpp b/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolationTest.cpp new file mode 100644 index 00000000000..8120e846fdf --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/LengthStyleInterpolationTest.cpp @@ -0,0 +1,119 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "core/animation/interpolation/LengthStyleInterpolation.h" + +#include "core/css/CSSPrimitiveValue.h" +#include "core/css/StylePropertySet.h" + +#include <gtest/gtest.h> + +namespace WebCore { + +class AnimationLengthStyleInterpolationTest : public ::testing::Test { +protected: + static PassOwnPtrWillBeRawPtr<InterpolableValue> lengthToInterpolableValue(CSSValue* value) + { + return LengthStyleInterpolation::lengthToInterpolableValue(value); + } + + static PassRefPtrWillBeRawPtr<CSSValue> interpolableValueToLength(InterpolableValue* value, ValueRange range) + { + return LengthStyleInterpolation::interpolableValueToLength(value, range); + } + + static PassRefPtrWillBeRawPtr<CSSValue> roundTrip(PassRefPtrWillBeRawPtr<CSSValue> value) + { + return interpolableValueToLength(lengthToInterpolableValue(value.get()).get(), ValueRangeAll); + } + + static void testPrimitiveValue(RefPtrWillBeRawPtr<CSSValue> value, double doubleValue, CSSPrimitiveValue::UnitType unitType) + { + EXPECT_TRUE(value->isPrimitiveValue()); + EXPECT_EQ(doubleValue, toCSSPrimitiveValue(value.get())->getDoubleValue()); + EXPECT_EQ(unitType, toCSSPrimitiveValue(value.get())->primitiveType()); + } + + static PassOwnPtrWillBeRawPtr<InterpolableList> createInterpolableLength(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j) + { + OwnPtrWillBeRawPtr<InterpolableList> list = InterpolableList::create(10); + list->set(0, InterpolableNumber::create(a)); + list->set(1, InterpolableNumber::create(b)); + list->set(2, InterpolableNumber::create(c)); + list->set(3, InterpolableNumber::create(d)); + list->set(4, InterpolableNumber::create(e)); + list->set(5, InterpolableNumber::create(f)); + list->set(6, InterpolableNumber::create(g)); + list->set(7, InterpolableNumber::create(h)); + list->set(8, InterpolableNumber::create(i)); + list->set(9, InterpolableNumber::create(j)); + + return list.release(); + } + + void initLengthArray(CSSLengthArray& lengthArray) + { + lengthArray.resize(CSSPrimitiveValue::LengthUnitTypeCount); + for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; ++i) + lengthArray.at(i) = 0; + } + + CSSLengthArray& setLengthArray(CSSLengthArray& lengthArray, String text) + { + initLengthArray(lengthArray); + RefPtr<MutableStylePropertySet> propertySet = MutableStylePropertySet::create(); + propertySet->setProperty(CSSPropertyLeft, text); + toCSSPrimitiveValue(propertySet->getPropertyCSSValue(CSSPropertyLeft).get())->accumulateLengthArray(lengthArray); + return lengthArray; + } + + bool lengthArraysEqual(CSSLengthArray& a, CSSLengthArray& b) + { + for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; ++i) { + if (a.at(i) != b.at(i)) + return false; + } + return true; + } +}; + +TEST_F(AnimationLengthStyleInterpolationTest, ZeroLength) +{ + RefPtrWillBeRawPtr<CSSValue> value = roundTrip(CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_PX)); + testPrimitiveValue(value, 0, CSSPrimitiveValue::CSS_PX); + + value = roundTrip(CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_EMS)); + testPrimitiveValue(value, 0, CSSPrimitiveValue::CSS_PX); +} + +TEST_F(AnimationLengthStyleInterpolationTest, SingleUnit) +{ + RefPtrWillBeRawPtr<CSSValue> value = roundTrip(CSSPrimitiveValue::create(10, CSSPrimitiveValue::CSS_PX)); + testPrimitiveValue(value, 10, CSSPrimitiveValue::CSS_PX); + + value = roundTrip(CSSPrimitiveValue::create(30, CSSPrimitiveValue::CSS_PERCENTAGE)); + testPrimitiveValue(value, 30, CSSPrimitiveValue::CSS_PERCENTAGE); + + value = roundTrip(CSSPrimitiveValue::create(-10, CSSPrimitiveValue::CSS_EMS)); + testPrimitiveValue(value, -10, CSSPrimitiveValue::CSS_EMS); +} + +TEST_F(AnimationLengthStyleInterpolationTest, SingleClampedUnit) +{ + RefPtrWillBeRawPtr<CSSValue> value = CSSPrimitiveValue::create(-10, CSSPrimitiveValue::CSS_EMS); + value = interpolableValueToLength(lengthToInterpolableValue(value.get()).get(), ValueRangeNonNegative); + testPrimitiveValue(value, 0, CSSPrimitiveValue::CSS_EMS); +} + +TEST_F(AnimationLengthStyleInterpolationTest, MultipleUnits) +{ + CSSLengthArray actual, expectation; + initLengthArray(expectation); + OwnPtrWillBeRawPtr<InterpolableList> list = createInterpolableLength(0, 10, 0, 10, 0, 10, 0, 10, 0, 10); + toCSSPrimitiveValue(interpolableValueToLength(list.get(), ValueRangeAll).get())->accumulateLengthArray(expectation); + EXPECT_TRUE(lengthArraysEqual(expectation, setLengthArray(actual, "calc(10%% + 10ex + 10ch + 10vh + 10vmax)"))); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/animation/interpolation/StyleInterpolation.h b/chromium/third_party/WebKit/Source/core/animation/interpolation/StyleInterpolation.h new file mode 100644 index 00000000000..5c277439ffd --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/animation/interpolation/StyleInterpolation.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef StyleInterpolation_h +#define StyleInterpolation_h + +#include "core/CSSPropertyNames.h" +#include "core/animation/interpolation/Interpolation.h" + +namespace WebCore { + +class StyleResolverState; + +class StyleInterpolation : public Interpolation { +public: + // 1) convert m_cachedValue into an X + // 2) shove X into StyleResolverState + // X can be: + // (1) a CSSValue (and applied via StyleBuilder::applyProperty) + // (2) an AnimatableValue (and applied via // AnimatedStyleBuilder::applyProperty) + // (3) a custom value that is inserted directly into the StyleResolverState. + virtual void apply(StyleResolverState&) const = 0; + + virtual bool isStyleInterpolation() const OVERRIDE FINAL { return true; } + + CSSPropertyID id() const { return m_id; } + + virtual void trace(Visitor* visitor) OVERRIDE + { + Interpolation::trace(visitor); + } + +protected: + CSSPropertyID m_id; + + StyleInterpolation(PassOwnPtrWillBeRawPtr<InterpolableValue> start, PassOwnPtrWillBeRawPtr<InterpolableValue> end, CSSPropertyID id) + : Interpolation(start, end) + , m_id(id) + { + } +}; + +DEFINE_TYPE_CASTS(StyleInterpolation, Interpolation, value, value->isStyleInterpolation(), value.isStyleInterpolation()); + +} + +#endif |