[Web Animations] Crash when setting an animation style on an existing animation that...
[WebKit-https.git] / Source / WebCore / animation / CSSAnimation.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CSSAnimation.h"
28
29 #include "Animation.h"
30 #include "Element.h"
31
32 namespace WebCore {
33
34 Ref<CSSAnimation> CSSAnimation::create(Element& target, const Animation& backingAnimation, const RenderStyle* oldStyle, const RenderStyle& newStyle)
35 {
36     auto result = adoptRef(*new CSSAnimation(target, backingAnimation, newStyle));
37     result->initialize(target, oldStyle, newStyle);
38     return result;
39 }
40
41 CSSAnimation::CSSAnimation(Element& element, const Animation& backingAnimation, const RenderStyle& unanimatedStyle)
42     : DeclarativeAnimation(element, backingAnimation)
43     , m_animationName(backingAnimation.name())
44     , m_unanimatedStyle(RenderStyle::clonePtr(unanimatedStyle))
45 {
46 }
47
48 void CSSAnimation::syncPropertiesWithBackingAnimation()
49 {
50     DeclarativeAnimation::syncPropertiesWithBackingAnimation();
51
52     if (!effect())
53         return;
54
55     suspendEffectInvalidation();
56
57     auto& animation = backingAnimation();
58     auto* timing = effect()->timing();
59
60     switch (animation.fillMode()) {
61     case AnimationFillMode::None:
62         timing->setFill(FillMode::None);
63         break;
64     case AnimationFillMode::Backwards:
65         timing->setFill(FillMode::Backwards);
66         break;
67     case AnimationFillMode::Forwards:
68         timing->setFill(FillMode::Forwards);
69         break;
70     case AnimationFillMode::Both:
71         timing->setFill(FillMode::Both);
72         break;
73     }
74
75     switch (animation.direction()) {
76     case Animation::AnimationDirectionNormal:
77         timing->setDirection(PlaybackDirection::Normal);
78         break;
79     case Animation::AnimationDirectionAlternate:
80         timing->setDirection(PlaybackDirection::Alternate);
81         break;
82     case Animation::AnimationDirectionReverse:
83         timing->setDirection(PlaybackDirection::Reverse);
84         break;
85     case Animation::AnimationDirectionAlternateReverse:
86         timing->setDirection(PlaybackDirection::AlternateReverse);
87         break;
88     }
89
90     auto iterationCount = animation.iterationCount();
91     timing->setIterations(iterationCount == Animation::IterationCountInfinite ? std::numeric_limits<double>::infinity() : iterationCount);
92
93     timing->setDelay(Seconds(animation.delay()));
94     timing->setIterationDuration(Seconds(animation.duration()));
95
96     // Synchronize the play state
97     if (animation.playState() == AnimationPlayState::Playing && playState() == WebAnimation::PlayState::Paused) {
98         if (!m_stickyPaused)
99             play();
100     } else if (animation.playState() == AnimationPlayState::Paused && playState() == WebAnimation::PlayState::Running)
101         pause();
102
103     unsuspendEffectInvalidation();
104 }
105
106 std::optional<double> CSSAnimation::bindingsStartTime() const
107 {
108     flushPendingStyleChanges();
109     return DeclarativeAnimation::bindingsStartTime();
110 }
111
112 void CSSAnimation::setBindingsStartTime(std::optional<double> startTime)
113 {
114     flushPendingStyleChanges();
115     return DeclarativeAnimation::setBindingsStartTime(startTime);
116 }
117
118 std::optional<double> CSSAnimation::bindingsCurrentTime() const
119 {
120     flushPendingStyleChanges();
121     auto currentTime = DeclarativeAnimation::bindingsCurrentTime();
122     if (currentTime) {
123         if (auto* animationEffect = effect())
124             return std::max(0.0, std::min(currentTime.value(), animationEffect->timing()->activeDuration().milliseconds()));
125     }
126     return currentTime;
127 }
128
129 ExceptionOr<void> CSSAnimation::setBindingsCurrentTime(std::optional<double> currentTime)
130 {
131     flushPendingStyleChanges();
132     return DeclarativeAnimation::setBindingsCurrentTime(currentTime);
133 }
134
135 WebAnimation::PlayState CSSAnimation::bindingsPlayState() const
136 {
137     flushPendingStyleChanges();
138     return DeclarativeAnimation::bindingsPlayState();
139 }
140
141 bool CSSAnimation::bindingsPending() const
142 {
143     flushPendingStyleChanges();
144     return DeclarativeAnimation::bindingsPending();
145 }
146
147 WebAnimation::ReadyPromise& CSSAnimation::bindingsReady()
148 {
149     flushPendingStyleChanges();
150     return DeclarativeAnimation::bindingsReady();
151 }
152
153 WebAnimation::FinishedPromise& CSSAnimation::bindingsFinished()
154 {
155     flushPendingStyleChanges();
156     return DeclarativeAnimation::bindingsFinished();
157 }
158
159 ExceptionOr<void> CSSAnimation::bindingsPlay()
160 {
161     flushPendingStyleChanges();
162     m_stickyPaused = false;
163     return DeclarativeAnimation::bindingsPlay();
164 }
165
166 ExceptionOr<void> CSSAnimation::bindingsPause()
167 {
168     flushPendingStyleChanges();
169     m_stickyPaused = true;
170     return DeclarativeAnimation::bindingsPause();
171 }
172
173 void CSSAnimation::flushPendingStyleChanges() const
174 {
175     if (auto* animationEffect = effect()) {
176         if (is<KeyframeEffectReadOnly>(animationEffect)) {
177             if (auto* target = downcast<KeyframeEffectReadOnly>(animationEffect)->target())
178                 target->document().updateStyleIfNeeded();
179         }
180     }
181 }
182
183 } // namespace WebCore