[Web Animations] Implement replaced animations
[WebKit-https.git] / Source / WebCore / animation / AnimationTimeline.cpp
1 /*
2  * Copyright (C) Canon Inc. 2016
3  * Copyright (C) 2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "AnimationTimeline.h"
29
30 #include "Animation.h"
31 #include "AnimationEffect.h"
32 #include "AnimationList.h"
33 #include "CSSAnimation.h"
34 #include "CSSPropertyAnimation.h"
35 #include "CSSTransition.h"
36 #include "DocumentTimeline.h"
37 #include "Element.h"
38 #include "KeyframeEffect.h"
39 #include "RenderStyle.h"
40 #include "RenderView.h"
41 #include "StylePropertyShorthand.h"
42 #include "StyleResolver.h"
43 #include "WebAnimationUtilities.h"
44 #include <wtf/text/TextStream.h>
45 #include <wtf/text/WTFString.h>
46
47 namespace WebCore {
48
49 AnimationTimeline::AnimationTimeline()
50 {
51 }
52
53 AnimationTimeline::~AnimationTimeline()
54 {
55 }
56
57 void AnimationTimeline::forgetAnimation(WebAnimation* animation)
58 {
59     m_allAnimations.removeFirst(animation);
60 }
61
62 void AnimationTimeline::animationTimingDidChange(WebAnimation& animation)
63 {
64     if (m_animations.add(&animation)) {
65         animation.setGlobalPosition(m_allAnimations.size());
66         m_allAnimations.append(makeWeakPtr(&animation));
67         auto* timeline = animation.timeline();
68         if (timeline && timeline != this)
69             timeline->removeAnimation(animation);
70     }
71 }
72
73 void AnimationTimeline::removeAnimation(WebAnimation& animation)
74 {
75     ASSERT(!animation.timeline() || animation.timeline() == this);
76     m_animations.remove(&animation);
77     if (is<KeyframeEffect>(animation.effect())) {
78         if (auto* target = downcast<KeyframeEffect>(animation.effect())->target())
79             animationWasRemovedFromElement(animation, *target);
80     }
81 }
82
83 Optional<double> AnimationTimeline::bindingsCurrentTime()
84 {
85     auto time = currentTime();
86     if (!time)
87         return WTF::nullopt;
88     return secondsToWebAnimationsAPITime(*time);
89 }
90
91 void AnimationTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element)
92 {
93     [&] () -> ElementToAnimationsMap& {
94         if (is<CSSTransition>(animation) && downcast<CSSTransition>(animation).owningElement())
95             return m_elementToCSSTransitionsMap;
96         if (is<CSSAnimation>(animation) && downcast<CSSAnimation>(animation).owningElement())
97             return m_elementToCSSAnimationsMap;
98         return m_elementToAnimationsMap;
99     }().ensure(&element, [] {
100         return ListHashSet<RefPtr<WebAnimation>> { };
101     }).iterator->value.add(&animation);
102 }
103
104 static inline bool removeCSSTransitionFromMap(CSSTransition& transition, Element& element, HashMap<Element*, AnimationTimeline::PropertyToTransitionMap>& map)
105 {
106     auto iterator = map.find(&element);
107     if (iterator == map.end())
108         return false;
109
110     auto& cssTransitionsByProperty = iterator->value;
111
112     auto transitionIterator = cssTransitionsByProperty.find(transition.property());
113     if (transitionIterator == cssTransitionsByProperty.end() || transitionIterator->value != &transition)
114         return false;
115
116     cssTransitionsByProperty.remove(transitionIterator);
117
118     if (cssTransitionsByProperty.isEmpty())
119         map.remove(&element);
120     return true;
121 }
122
123 static inline void removeAnimationFromMapForElement(WebAnimation& animation, AnimationTimeline::ElementToAnimationsMap& map, Element& element)
124 {
125     auto iterator = map.find(&element);
126     if (iterator == map.end())
127         return;
128
129     auto& animations = iterator->value;
130     animations.remove(&animation);
131     if (!animations.size())
132         map.remove(iterator);
133 }
134
135 void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
136 {
137     removeAnimationFromMapForElement(animation, m_elementToCSSTransitionsMap, element);
138     removeAnimationFromMapForElement(animation, m_elementToCSSAnimationsMap, element);
139     removeAnimationFromMapForElement(animation, m_elementToAnimationsMap, element);
140
141     // Now, if we're dealing with a declarative animation, we remove it from either the m_elementToCSSAnimationByName
142     // or the m_elementToRunningCSSTransitionByCSSPropertyID map, whichever is relevant to this type of animation.
143     if (is<DeclarativeAnimation>(animation))
144         removeDeclarativeAnimationFromListsForOwningElement(animation, element);
145 }
146
147 void AnimationTimeline::removeDeclarativeAnimationFromListsForOwningElement(WebAnimation& animation, Element& element)
148 {
149     ASSERT(is<DeclarativeAnimation>(animation));
150
151     if (is<CSSAnimation>(animation)) {
152         auto iterator = m_elementToCSSAnimationByName.find(&element);
153         if (iterator != m_elementToCSSAnimationByName.end()) {
154             auto& cssAnimationsByName = iterator->value;
155             auto& name = downcast<CSSAnimation>(animation).animationName();
156             cssAnimationsByName.remove(name);
157             if (cssAnimationsByName.isEmpty())
158                 m_elementToCSSAnimationByName.remove(&element);
159         }
160     } else if (is<CSSTransition>(animation)) {
161         auto& transition = downcast<CSSTransition>(animation);
162         if (!removeCSSTransitionFromMap(transition, element, m_elementToRunningCSSTransitionByCSSPropertyID))
163             removeCSSTransitionFromMap(transition, element, m_elementToCompletedCSSTransitionByCSSPropertyID);
164     }
165 }
166
167 Vector<RefPtr<WebAnimation>> AnimationTimeline::animationsForElement(Element& element, Ordering ordering) const
168 {
169     Vector<RefPtr<WebAnimation>> animations;
170     if (m_elementToCSSTransitionsMap.contains(&element)) {
171         const auto& cssTransitions = m_elementToCSSTransitionsMap.get(&element);
172         if (ordering == Ordering::Sorted) {
173             Vector<RefPtr<WebAnimation>> sortedCSSTransitions(cssTransitions.size());
174             sortedCSSTransitions.appendRange(cssTransitions.begin(), cssTransitions.end());
175             std::sort(sortedCSSTransitions.begin(), sortedCSSTransitions.end(), [](auto& lhs, auto& rhs) {
176                 // Sort transitions first by their generation time, and then by transition-property.
177                 // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
178                 auto* lhsTransition = downcast<CSSTransition>(lhs.get());
179                 auto* rhsTransition = downcast<CSSTransition>(rhs.get());
180                 if (lhsTransition->generationTime() != rhsTransition->generationTime())
181                     return lhsTransition->generationTime() < rhsTransition->generationTime();
182                 return lhsTransition->transitionProperty().utf8() < rhsTransition->transitionProperty().utf8();
183             });
184             animations.appendVector(sortedCSSTransitions);
185         } else
186             animations.appendRange(cssTransitions.begin(), cssTransitions.end());
187     }
188     if (m_elementToCSSAnimationsMap.contains(&element)) {
189         const auto& cssAnimations = m_elementToCSSAnimationsMap.get(&element);
190         animations.appendRange(cssAnimations.begin(), cssAnimations.end());
191     }
192     if (m_elementToAnimationsMap.contains(&element)) {
193         const auto& webAnimations = m_elementToAnimationsMap.get(&element);
194         if (ordering == Ordering::Sorted) {
195             Vector<RefPtr<WebAnimation>> sortedWebAnimations(webAnimations.size());
196             sortedWebAnimations.appendRange(webAnimations.begin(), webAnimations.end());
197             std::sort(sortedWebAnimations.begin(), sortedWebAnimations.end(), [](auto& lha, auto& rha) {
198                 return lha->globalPosition() < rha->globalPosition();
199             });
200             animations.appendVector(sortedWebAnimations);
201         } else
202             animations.appendRange(webAnimations.begin(), webAnimations.end());
203     }
204     return animations;
205 }
206
207 void AnimationTimeline::elementWasRemoved(Element& element)
208 {
209     for (auto& cssTransition : m_elementToCSSTransitionsMap.get(&element))
210         cssTransition->cancel(WebAnimation::Silently::Yes);
211     for (auto& cssAnimation : m_elementToCSSAnimationsMap.get(&element))
212         cssAnimation->cancel(WebAnimation::Silently::Yes);
213 }
214
215 void AnimationTimeline::removeAnimationsForElement(Element& element)
216 {
217     for (auto& animation : animationsForElement(element))
218         animation->remove();
219 }
220
221 void AnimationTimeline::cancelDeclarativeAnimationsForElement(Element& element)
222 {
223     for (auto& cssTransition : m_elementToCSSTransitionsMap.get(&element))
224         cssTransition->cancel();
225     for (auto& cssAnimation : m_elementToCSSAnimationsMap.get(&element))
226         cssAnimation->cancel();
227 }
228
229 static bool shouldConsiderAnimation(Element& element, const Animation& animation)
230 {
231     if (!animation.isValidAnimation())
232         return false;
233
234     static NeverDestroyed<const String> animationNameNone(MAKE_STATIC_STRING_IMPL("none"));
235
236     auto& name = animation.name();
237     if (name == animationNameNone || name.isEmpty())
238         return false;
239
240     if (auto* styleScope = Style::Scope::forOrdinal(element, animation.nameStyleScopeOrdinal()))
241         return styleScope->resolver().isAnimationNameValid(name);
242
243     return false;
244 }
245
246 void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const RenderStyle* currentStyle, const RenderStyle& afterChangeStyle)
247 {
248     // In case this element is newly getting a "display: none" we need to cancel all of its animations and disregard new ones.
249     if (currentStyle && currentStyle->hasAnimations() && currentStyle->display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
250         if (m_elementToCSSAnimationByName.contains(&element)) {
251             for (const auto& cssAnimationsByNameMapItem : m_elementToCSSAnimationByName.take(&element))
252                 cancelDeclarativeAnimation(*cssAnimationsByNameMapItem.value);
253         }
254         return;
255     }
256
257     if (currentStyle && currentStyle->hasAnimations() && afterChangeStyle.hasAnimations() && *(currentStyle->animations()) == *(afterChangeStyle.animations()))
258         return;
259
260     // First, compile the list of animation names that were applied to this element up to this point.
261     HashSet<String> namesOfPreviousAnimations;
262     if (currentStyle && currentStyle->hasAnimations()) {
263         auto* previousAnimations = currentStyle->animations();
264         for (size_t i = 0; i < previousAnimations->size(); ++i) {
265             auto& previousAnimation = previousAnimations->animation(i);
266             if (shouldConsiderAnimation(element, previousAnimation))
267                 namesOfPreviousAnimations.add(previousAnimation.name());
268         }
269     }
270
271     // Create or get the CSSAnimations by animation name map for this element.
272     auto& cssAnimationsByName = m_elementToCSSAnimationByName.ensure(&element, [] {
273         return HashMap<String, RefPtr<CSSAnimation>> { };
274     }).iterator->value;
275
276     if (auto* currentAnimations = afterChangeStyle.animations()) {
277         for (size_t i = 0; i < currentAnimations->size(); ++i) {
278             auto& currentAnimation = currentAnimations->animation(i);
279             auto& name = currentAnimation.name();
280             if (namesOfPreviousAnimations.contains(name)) {
281                 // We've found the name of this animation in our list of previous animations, this means we've already
282                 // created a CSSAnimation object for it and need to ensure that this CSSAnimation is backed by the current
283                 // animation object for this animation name.
284                 if (auto cssAnimation = cssAnimationsByName.get(name))
285                     cssAnimation->setBackingAnimation(currentAnimation);
286             } else if (shouldConsiderAnimation(element, currentAnimation)) {
287                 // Otherwise we are dealing with a new animation name and must create a CSSAnimation for it.
288                 cssAnimationsByName.set(name, CSSAnimation::create(element, currentAnimation, currentStyle, afterChangeStyle));
289             }
290             // Remove the name of this animation from our list since it's now known to be current.
291             namesOfPreviousAnimations.remove(name);
292         }
293     }
294
295     // The animations names left in namesOfPreviousAnimations are now known to no longer apply so we need to
296     // remove the CSSAnimation object created for them.
297     for (const auto& nameOfAnimationToRemove : namesOfPreviousAnimations) {
298         if (auto animation = cssAnimationsByName.take(nameOfAnimationToRemove))
299             cancelDeclarativeAnimation(*animation);
300     }
301 }
302
303 RefPtr<WebAnimation> AnimationTimeline::cssAnimationForElementAndProperty(Element& element, CSSPropertyID property)
304 {
305     RefPtr<WebAnimation> matchingAnimation;
306     for (const auto& animation : m_elementToCSSAnimationsMap.get(&element)) {
307         auto* effect = animation->effect();
308         if (is<KeyframeEffect>(effect) && downcast<KeyframeEffect>(effect)->animatedProperties().contains(property))
309             matchingAnimation = animation;
310     }
311     return matchingAnimation;
312 }
313
314 static bool propertyInStyleMatchesValueForTransitionInMap(CSSPropertyID property, const RenderStyle& style, AnimationTimeline::PropertyToTransitionMap& transitions)
315 {
316     if (auto* transition = transitions.get(property)) {
317         if (CSSPropertyAnimation::propertiesEqual(property, &style, &transition->targetStyle()))
318             return true;
319     }
320     return false;
321 }
322
323 static double transitionCombinedDuration(const Animation* transition)
324 {
325     return std::max(0.0, transition->duration()) + transition->delay();
326 }
327
328 static bool transitionMatchesProperty(const Animation& transition, CSSPropertyID property)
329 {
330     auto mode = transition.animationMode();
331     if (mode == Animation::AnimateNone || mode == Animation::AnimateUnknownProperty)
332         return false;
333     if (mode == Animation::AnimateSingleProperty) {
334         auto transitionProperty = transition.property();
335         if (transitionProperty != property) {
336             auto shorthand = shorthandForProperty(transitionProperty);
337             for (size_t i = 0; i < shorthand.length(); ++i) {
338                 if (shorthand.properties()[i] == property)
339                     return true;
340             }
341             return false;
342         }
343     }
344     return true;
345 }
346
347 AnimationTimeline::PropertyToTransitionMap& AnimationTimeline::ensureRunningTransitionsByProperty(Element& element)
348 {
349     return m_elementToRunningCSSTransitionByCSSPropertyID.ensure(&element, [] {
350         return PropertyToTransitionMap { };
351     }).iterator->value;
352 }
353
354 void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle)
355 {
356     // In case this element is newly getting a "display: none" we need to cancel all of its transitions and disregard new ones.
357     if (currentStyle.hasTransitions() && currentStyle.display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
358         if (m_elementToRunningCSSTransitionByCSSPropertyID.contains(&element)) {
359             for (const auto& cssTransitionsByCSSPropertyIDMapItem : m_elementToRunningCSSTransitionByCSSPropertyID.take(&element))
360                 cancelDeclarativeAnimation(*cssTransitionsByCSSPropertyIDMapItem.value);
361         }
362         return;
363     }
364
365     // Section 3 "Starting of transitions" from the CSS Transitions Level 1 specification.
366     // https://drafts.csswg.org/css-transitions-1/#starting
367
368     auto& runningTransitionsByProperty = ensureRunningTransitionsByProperty(element);
369
370     auto& completedTransitionsByProperty = m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(&element, [] {
371         return PropertyToTransitionMap { };
372     }).iterator->value;
373
374     auto generationTime = MonotonicTime::now();
375
376     auto numberOfProperties = CSSPropertyAnimation::getNumProperties();
377     for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) {
378         Optional<bool> isShorthand;
379         auto property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
380         if (isShorthand && *isShorthand)
381             continue;
382
383         const Animation* matchingBackingAnimation = nullptr;
384         if (auto* transitions = afterChangeStyle.transitions()) {
385             for (size_t i = 0; i < transitions->size(); ++i) {
386                 auto& backingAnimation = transitions->animation(i);
387                 if (transitionMatchesProperty(backingAnimation, property))
388                     matchingBackingAnimation = &backingAnimation;
389             }
390         }
391
392         // https://drafts.csswg.org/css-transitions-1/#before-change-style
393         // Define the before-change style as the computed values of all properties on the element as of the previous style change event, except with
394         // any styles derived from declarative animations such as CSS Transitions, CSS Animations, and SMIL Animations updated to the current time.
395         auto existingAnimation = cssAnimationForElementAndProperty(element, property);
396         const auto& beforeChangeStyle = existingAnimation ? downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : currentStyle;
397
398         if (!runningTransitionsByProperty.contains(property)
399             && !CSSPropertyAnimation::propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle)
400             && CSSPropertyAnimation::canPropertyBeInterpolated(property, &beforeChangeStyle, &afterChangeStyle)
401             && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)
402             && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) {
403             // 1. If all of the following are true:
404             //   - the element does not have a running transition for the property,
405             //   - the before-change style is different from and can be interpolated with the after-change style for that property,
406             //   - the element does not have a completed transition for the property or the end value of the completed transition is different from the after-change style for the property,
407             //   - there is a matching transition-property value, and
408             //   - the combined duration is greater than 0s,
409
410             // then implementations must remove the completed transition (if present) from the set of completed transitions
411             completedTransitionsByProperty.remove(property);
412
413             // and start a transition whose:
414             //   - start time is the time of the style change event plus the matching transition delay,
415             //   - end time is the start time plus the matching transition duration,
416             //   - start value is the value of the transitioning property in the before-change style,
417             //   - end value is the value of the transitioning property in the after-change style,
418             //   - reversing-adjusted start value is the same as the start value, and
419             //   - reversing shortening factor is 1.
420             auto delay = Seconds(matchingBackingAnimation->delay());
421             auto duration = Seconds(matchingBackingAnimation->duration());
422             auto& reversingAdjustedStartStyle = beforeChangeStyle;
423             auto reversingShorteningFactor = 1;
424             runningTransitionsByProperty.set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
425         } else if (completedTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)) {
426             // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from
427             //    the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions.
428             completedTransitionsByProperty.remove(property);
429         }
430
431         bool hasRunningTransition = runningTransitionsByProperty.contains(property);
432         if ((hasRunningTransition || completedTransitionsByProperty.contains(property)) && !matchingBackingAnimation) {
433             // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property
434             //    value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions.
435             if (hasRunningTransition)
436                 runningTransitionsByProperty.take(property)->cancel();
437             else
438                 completedTransitionsByProperty.remove(property);
439         }
440
441         if (matchingBackingAnimation && runningTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, runningTransitionsByProperty)) {
442             auto previouslyRunningTransition = runningTransitionsByProperty.take(property);
443             auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle();
444             // 4. If the element has a running transition for the property, there is a matching transition-property value, and the end value of the running
445             //    transition is not equal to the value of the property in the after-change style, then:
446             if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &currentStyle, &afterChangeStyle)) {
447                 // 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style,
448                 //    or if these two values cannot be interpolated, then implementations must cancel the running transition.
449                 cancelDeclarativeAnimation(*previouslyRunningTransition);
450             } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) {
451                 // 2. Otherwise, if the combined duration is less than or equal to 0s, or if the current value of the property in the running transition
452                 //    cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition.
453                 cancelDeclarativeAnimation(*previouslyRunningTransition);
454             } else if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) {
455                 // 3. Otherwise, if the reversing-adjusted start value of the running transition is the same as the value of the property in the after-change
456                 //    style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition
457                 cancelDeclarativeAnimation(*previouslyRunningTransition);
458
459                 // and start a new transition whose:
460                 //   - reversing-adjusted start value is the end value of the running transition,
461                 //   - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of:
462                 //       1. the output of the timing function of the old transition at the time of the style change event, times the reversing shortening factor of the old transition
463                 //       2. 1 minus the reversing shortening factor of the old transition.
464                 //   - start time is the time of the style change event plus:
465                 //       1. if the matching transition delay is nonnegative, the matching transition delay, or
466                 //       2. if the matching transition delay is negative, the product of the new transition’s reversing shortening factor and the matching transition delay,
467                 //   - end time is the start time plus the product of the matching transition duration and the new transition’s reversing shortening factor,
468                 //   - start value is the current value of the property in the running transition,
469                 //   - end value is the value of the property in the after-change style
470                 auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle();
471                 double transformedProgress = 1;
472                 if (auto* effect = previouslyRunningTransition->effect()) {
473                     if (auto computedTimingProgress = effect->getComputedTiming().progress)
474                         transformedProgress = *computedTimingProgress;
475                 }
476                 auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0);
477                 auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay());
478                 auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor;
479
480                 ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
481             } else {
482                 // 4. Otherwise, implementations must cancel the running transition
483                 cancelDeclarativeAnimation(*previouslyRunningTransition);
484
485                 // and start a new transition whose:
486                 //   - start time is the time of the style change event plus the matching transition delay,
487                 //   - end time is the start time plus the matching transition duration,
488                 //   - start value is the current value of the property in the running transition,
489                 //   - end value is the value of the property in the after-change style,
490                 //   - reversing-adjusted start value is the same as the start value, and
491                 //   - reversing shortening factor is 1.
492                 auto delay = Seconds(matchingBackingAnimation->delay());
493                 auto duration = Seconds(matchingBackingAnimation->duration());
494                 auto& reversingAdjustedStartStyle = currentStyle;
495                 auto reversingShorteningFactor = 1;
496                 ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
497             }
498         }
499     }
500 }
501
502 void AnimationTimeline::cancelDeclarativeAnimation(DeclarativeAnimation& animation)
503 {
504     animation.cancelFromStyle();
505     removeAnimation(animation);
506     m_allAnimations.removeFirst(&animation);
507 }
508
509 } // namespace WebCore