[Web Animations] Refactor cancelDeclarativeAnimationsForElement and willDestroyRender...
[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 "KeyframeEffectStack.h"
40 #include "RenderStyle.h"
41 #include "RenderView.h"
42 #include "StylePropertyShorthand.h"
43 #include "StyleResolver.h"
44 #include "WebAnimationUtilities.h"
45 #include <wtf/text/TextStream.h>
46 #include <wtf/text/WTFString.h>
47
48 namespace WebCore {
49
50 AnimationTimeline::AnimationTimeline()
51 {
52 }
53
54 AnimationTimeline::~AnimationTimeline()
55 {
56 }
57
58 void AnimationTimeline::forgetAnimation(WebAnimation* animation)
59 {
60     m_allAnimations.removeFirst(animation);
61 }
62
63 void AnimationTimeline::animationTimingDidChange(WebAnimation& animation)
64 {
65     updateGlobalPosition(animation);
66
67     if (m_animations.add(&animation)) {
68         m_allAnimations.append(makeWeakPtr(&animation));
69         auto* timeline = animation.timeline();
70         if (timeline && timeline != this)
71             timeline->removeAnimation(animation);
72     }
73 }
74
75 void AnimationTimeline::updateGlobalPosition(WebAnimation& animation)
76 {
77     static uint64_t s_globalPosition = 0;
78     if (!animation.globalPosition() && animation.canHaveGlobalPosition())
79         animation.setGlobalPosition(++s_globalPosition);
80 }
81
82 void AnimationTimeline::removeAnimation(WebAnimation& animation)
83 {
84     ASSERT(!animation.timeline() || animation.timeline() == this);
85     m_animations.remove(&animation);
86     if (is<KeyframeEffect>(animation.effect())) {
87         if (auto* target = downcast<KeyframeEffect>(animation.effect())->target()) {
88             animationWasRemovedFromElement(animation, *target);
89             target->ensureKeyframeEffectStack().removeEffect(*downcast<KeyframeEffect>(animation.effect()));
90         }
91     }
92 }
93
94 Optional<double> AnimationTimeline::bindingsCurrentTime()
95 {
96     auto time = currentTime();
97     if (!time)
98         return WTF::nullopt;
99     return secondsToWebAnimationsAPITime(*time);
100 }
101
102 void AnimationTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element)
103 {
104     if (is<CSSTransition>(animation) && downcast<CSSTransition>(animation).owningElement() == &element)
105         element.ensureTransitions().add(&animation);
106     else if (is<CSSAnimation>(animation) && downcast<CSSAnimation>(animation).owningElement() == &element)
107         element.ensureCSSAnimations().add(&animation);
108     else
109         element.ensureWebAnimations().add(&animation);
110 }
111
112 static inline bool removeCSSTransitionFromMap(CSSTransition& transition, PropertyToTransitionMap& cssTransitionsByProperty)
113 {
114     auto transitionIterator = cssTransitionsByProperty.find(transition.property());
115     if (transitionIterator == cssTransitionsByProperty.end() || transitionIterator->value != &transition)
116         return false;
117
118     cssTransitionsByProperty.remove(transitionIterator);
119     return true;
120 }
121
122 void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
123 {
124     element.ensureTransitions().remove(&animation);
125     element.ensureCSSAnimations().remove(&animation);
126     element.ensureWebAnimations().remove(&animation);
127
128     // Now, if we're dealing with a CSS Transition, we remove it from the m_elementToRunningCSSTransitionByCSSPropertyID map.
129     // We don't need to do this for CSS Animations because their timing can be set via CSS to end, which would cause this
130     // function to be called, but they should remain associated with their owning element until this is changed via a call
131     // to the JS API or changing the target element's animation-name property.
132     if (is<CSSTransition>(animation))
133         removeDeclarativeAnimationFromListsForOwningElement(animation, element);
134 }
135
136 void AnimationTimeline::removeDeclarativeAnimationFromListsForOwningElement(WebAnimation& animation, Element& element)
137 {
138     ASSERT(is<DeclarativeAnimation>(animation));
139
140     if (is<CSSTransition>(animation)) {
141         auto& transition = downcast<CSSTransition>(animation);
142         if (!removeCSSTransitionFromMap(transition, element.ensureRunningTransitionsByProperty()))
143             removeCSSTransitionFromMap(transition, element.ensureCompletedTransitionsByProperty());
144     }
145 }
146
147 Vector<RefPtr<WebAnimation>> AnimationTimeline::animationsForElement(Element& element, Ordering ordering) const
148 {
149     Vector<RefPtr<WebAnimation>> animations;
150
151     if (ordering == Ordering::Sorted) {
152         if (element.hasKeyframeEffects()) {
153             for (auto& effect : element.ensureKeyframeEffectStack().sortedEffects())
154                 animations.append(effect->animation());
155         }
156     } else {
157         if (auto* cssTransitions = element.transitions())
158             animations.appendRange(cssTransitions->begin(), cssTransitions->end());
159         if (auto* cssAnimations = element.cssAnimations())
160             animations.appendRange(cssAnimations->begin(), cssAnimations->end());
161         if (auto* webAnimations = element.webAnimations())
162             animations.appendRange(webAnimations->begin(), webAnimations->end());
163     }
164
165     return animations;
166 }
167
168 void AnimationTimeline::removeCSSAnimationCreatedByMarkup(Element& element, CSSAnimation& cssAnimation)
169 {
170     element.animationsCreatedByMarkup().remove(&cssAnimation);
171
172     if (!element.hasKeyframeEffects())
173         return;
174
175     auto& keyframeEffectStack = element.ensureKeyframeEffectStack();
176     auto* cssAnimationList = keyframeEffectStack.cssAnimationList();
177     if (!cssAnimationList || cssAnimationList->isEmpty())
178         return;
179
180     auto& backingAnimation = cssAnimation.backingAnimation();
181     for (size_t i = 0; i < cssAnimationList->size(); ++i) {
182         if (cssAnimationList->animation(i) == backingAnimation) {
183             auto newAnimationList = cssAnimationList->copy();
184             newAnimationList->remove(i);
185             keyframeEffectStack.setCSSAnimationList(WTFMove(newAnimationList));
186             return;
187         }
188     }
189 }
190
191 void AnimationTimeline::elementWasRemoved(Element& element)
192 {
193     cancelDeclarativeAnimationsForElement(element, WebAnimation::Silently::Yes);
194 }
195
196 void AnimationTimeline::removeAnimationsForElement(Element& element)
197 {
198     for (auto& animation : animationsForElement(element))
199         animation->remove();
200 }
201
202 void AnimationTimeline::willChangeRendererForElement(Element& element)
203 {
204     for (auto& animation : animationsForElement(element))
205         animation->willChangeRenderer();
206 }
207
208 void AnimationTimeline::cancelDeclarativeAnimationsForElement(Element& element, WebAnimation::Silently silently)
209 {
210     if (auto* transitions = element.transitions()) {
211         for (auto& cssTransition : *transitions)
212             cssTransition->cancel(silently);
213     }
214
215     if (auto* cssAnimations = element.cssAnimations()) {
216         for (auto& cssAnimation : *cssAnimations) {
217             if (is<CSSAnimation>(cssAnimation))
218                 removeCSSAnimationCreatedByMarkup(element, downcast<CSSAnimation>(*cssAnimation));
219             cssAnimation->cancel(silently);
220         }
221     }
222 }
223
224 static bool shouldConsiderAnimation(Element& element, const Animation& animation)
225 {
226     if (!animation.isValidAnimation())
227         return false;
228
229     static NeverDestroyed<const String> animationNameNone(MAKE_STATIC_STRING_IMPL("none"));
230
231     auto& name = animation.name();
232     if (name == animationNameNone || name.isEmpty())
233         return false;
234
235     if (auto* styleScope = Style::Scope::forOrdinal(element, animation.nameStyleScopeOrdinal()))
236         return styleScope->resolver().isAnimationNameValid(name);
237
238     return false;
239 }
240
241 void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const RenderStyle* currentStyle, const RenderStyle& afterChangeStyle)
242 {
243     auto& keyframeEffectStack = element.ensureKeyframeEffectStack();
244
245     // In case this element is newly getting a "display: none" we need to cancel all of its animations and disregard new ones.
246     if (currentStyle && currentStyle->display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
247         for (auto& cssAnimation : element.animationsCreatedByMarkup())
248             cssAnimation->cancelFromStyle();
249         keyframeEffectStack.setCSSAnimationList(nullptr);
250         return;
251     }
252
253     auto* currentAnimationList = afterChangeStyle.animations();
254     auto* previousAnimationList = keyframeEffectStack.cssAnimationList();
255     if (previousAnimationList && !previousAnimationList->isEmpty() && afterChangeStyle.hasAnimations() && *(previousAnimationList) == *(afterChangeStyle.animations()))
256         return;
257
258     CSSAnimationCollection newAnimations;
259     auto& previousAnimations = element.animationsCreatedByMarkup();
260
261     // https://www.w3.org/TR/css-animations-1/#animations
262     // The same @keyframes rule name may be repeated within an animation-name. Changes to the animation-name update existing
263     // animations by iterating over the new list of animations from last to first, and, for each animation, finding the last
264     // matching animation in the list of existing animations. If a match is found, the existing animation is updated using the
265     // animation properties corresponding to its position in the new list of animations, whilst maintaining its current playback
266     // time as described above. The matching animation is removed from the existing list of animations such that it will not match
267     // twice. If a match is not found, a new animation is created. As a result, updating animation-name from ‘a’ to ‘a, a’ will
268     // cause the existing animation for ‘a’ to become the second animation in the list and a new animation will be created for the
269     // first item in the list.
270     if (currentAnimationList) {
271         for (size_t i = currentAnimationList->size(); i > 0; --i) {
272             auto& currentAnimation = currentAnimationList->animation(i - 1);
273             if (!shouldConsiderAnimation(element, currentAnimation))
274                 continue;
275
276             bool foundMatchingAnimation = false;
277             for (auto& previousAnimation : previousAnimations) {
278                 if (previousAnimation->animationName() == currentAnimation.name()) {
279                     // Timing properties or play state may have changed so we need to update the backing animation with
280                     // the Animation found in the current style.
281                     previousAnimation->setBackingAnimation(currentAnimation);
282                     newAnimations.add(previousAnimation);
283                     // Remove the matched animation from the list of previous animations so we may not match it again.
284                     previousAnimations.remove(previousAnimation);
285                     foundMatchingAnimation = true;
286                     break;
287                 }
288             }
289
290             if (!foundMatchingAnimation)
291                 newAnimations.add(CSSAnimation::create(element, currentAnimation, currentStyle, afterChangeStyle));
292         }
293     }
294
295     // Any animation found in previousAnimations but not found in newAnimations is not longer current and should be canceled.
296     for (auto& previousAnimation : previousAnimations) {
297         if (!newAnimations.contains(previousAnimation)) {
298             if (previousAnimation->owningElement())
299                 previousAnimation->cancelFromStyle();
300         }
301     }
302
303     element.setAnimationsCreatedByMarkup(WTFMove(newAnimations));
304
305     keyframeEffectStack.setCSSAnimationList(currentAnimationList);
306 }
307
308 static KeyframeEffect* keyframeEffectForElementAndProperty(Element& element, CSSPropertyID property)
309 {
310     if (auto* keyframeEffectStack = element.keyframeEffectStack()) {
311         auto effects = keyframeEffectStack->sortedEffects();
312         for (const auto& effect : makeReversedRange(effects)) {
313             if (effect->animatesProperty(property))
314                 return effect.get();
315         }
316     }
317
318     return nullptr;
319 }
320
321 static bool propertyInStyleMatchesValueForTransitionInMap(CSSPropertyID property, const RenderStyle& style, PropertyToTransitionMap& transitions)
322 {
323     if (auto* transition = transitions.get(property)) {
324         if (CSSPropertyAnimation::propertiesEqual(property, &style, &transition->targetStyle()))
325             return true;
326     }
327     return false;
328 }
329
330 static double transitionCombinedDuration(const Animation* transition)
331 {
332     return std::max(0.0, transition->duration()) + transition->delay();
333 }
334
335 static bool transitionMatchesProperty(const Animation& transition, CSSPropertyID property)
336 {
337     auto mode = transition.animationMode();
338     if (mode == Animation::AnimateNone || mode == Animation::AnimateUnknownProperty)
339         return false;
340     if (mode == Animation::AnimateSingleProperty) {
341         auto transitionProperty = transition.property();
342         if (transitionProperty != property) {
343             auto shorthand = shorthandForProperty(transitionProperty);
344             for (size_t i = 0; i < shorthand.length(); ++i) {
345                 if (shorthand.properties()[i] == property)
346                     return true;
347             }
348             return false;
349         }
350     }
351     return true;
352 }
353
354 static void compileTransitionPropertiesInStyle(const RenderStyle& style, HashSet<CSSPropertyID>& transitionProperties, bool& transitionPropertiesContainAll)
355 {
356     if (transitionPropertiesContainAll)
357         return;
358
359     auto* transitions = style.transitions();
360     if (!transitions)
361         return;
362
363     for (size_t i = 0; i < transitions->size(); ++i) {
364         const auto& animation = transitions->animation(i);
365         auto mode = animation.animationMode();
366         if (mode == Animation::AnimateSingleProperty) {
367             auto property = animation.property();
368             if (isShorthandCSSProperty(property)) {
369                 auto shorthand = shorthandForProperty(property);
370                 for (size_t j = 0; j < shorthand.length(); ++j)
371                     transitionProperties.add(shorthand.properties()[j]);
372             } else if (property != CSSPropertyInvalid)
373                 transitionProperties.add(property);
374         } else if (mode == Animation::AnimateAll) {
375             transitionPropertiesContainAll = true;
376             return;
377         }
378     }
379 }
380
381 void AnimationTimeline::updateCSSTransitionsForElementAndProperty(Element& element, CSSPropertyID property, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle, const MonotonicTime generationTime)
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     bool hasRunningTransition = element.hasRunningTransitionsForProperty(property);
396     auto beforeChangeStyle = [&]() {
397         if (hasRunningTransition && CSSPropertyAnimation::animationOfPropertyIsAccelerated(property)) {
398             // In case we have an accelerated transition running for this element, we need to get its computed style as the before-change style
399             // since otherwise the animated value for that property won't be visible.
400             auto* runningTransition = element.ensureRunningTransitionsByProperty().get(property);
401             if (is<KeyframeEffect>(runningTransition->effect())) {
402                 auto& keyframeEffect = *downcast<KeyframeEffect>(runningTransition->effect());
403                 if (keyframeEffect.isRunningAccelerated()) {
404                     auto animatedStyle = RenderStyle::clone(currentStyle);
405                     runningTransition->resolve(animatedStyle);
406                     return animatedStyle;
407                 }
408             }
409         }
410
411         if (auto* keyframeEffect = keyframeEffectForElementAndProperty(element, property)) {
412             // If we already have a keyframe effect targeting this property, we should use its unanimated style to determine what the potential
413             // start value of the transition shoud be to make sure that we don't account for animated values that would have been blended onto
414             // the style applied during the last style resolution.
415             if (auto* unanimatedStyle = keyframeEffect->unanimatedStyle())
416                 return RenderStyle::clone(*unanimatedStyle);
417
418             // If we have a keyframe effect targeting this property, but it doesn't yet have an unanimated style, this is because it has not
419             // had a chance to apply itself with a non-null progress. In this case, the before-change and after-change styles should be the
420             // same in order to prevent a transition from being triggered as the unanimated style for this keyframe effect will most likely
421             // be this after-change style, or any future style change that may happen before the keyframe effect starts blending animated values.
422             return RenderStyle::clone(afterChangeStyle);
423         }
424
425         // In any other scenario, the before-change style should be the previously resolved style for this element.
426         return RenderStyle::clone(currentStyle);
427     }();
428
429     if (!hasRunningTransition
430         && !CSSPropertyAnimation::propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle)
431         && CSSPropertyAnimation::canPropertyBeInterpolated(property, &beforeChangeStyle, &afterChangeStyle)
432         && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, element.ensureCompletedTransitionsByProperty())
433         && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) {
434         // 1. If all of the following are true:
435         //   - the element does not have a running transition for the property,
436         //   - the before-change style is different from and can be interpolated with the after-change style for that property,
437         //   - 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,
438         //   - there is a matching transition-property value, and
439         //   - the combined duration is greater than 0s,
440
441         // then implementations must remove the completed transition (if present) from the set of completed transitions
442         element.ensureCompletedTransitionsByProperty().remove(property);
443
444         // and start a transition whose:
445         //   - start time is the time of the style change event plus the matching transition delay,
446         //   - end time is the start time plus the matching transition duration,
447         //   - start value is the value of the transitioning property in the before-change style,
448         //   - end value is the value of the transitioning property in the after-change style,
449         //   - reversing-adjusted start value is the same as the start value, and
450         //   - reversing shortening factor is 1.
451         auto delay = Seconds(matchingBackingAnimation->delay());
452         auto duration = Seconds(matchingBackingAnimation->duration());
453         auto& reversingAdjustedStartStyle = beforeChangeStyle;
454         auto reversingShorteningFactor = 1;
455         element.ensureRunningTransitionsByProperty().set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
456     } else if (element.hasCompletedTransitionsForProperty(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, element.ensureCompletedTransitionsByProperty())) {
457         // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from
458         //    the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions.
459         element.ensureCompletedTransitionsByProperty().remove(property);
460     }
461
462     hasRunningTransition = element.hasRunningTransitionsForProperty(property);
463     if ((hasRunningTransition || element.hasCompletedTransitionsForProperty(property)) && !matchingBackingAnimation) {
464         // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property
465         //    value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions.
466         if (hasRunningTransition)
467             element.ensureRunningTransitionsByProperty().take(property)->cancel();
468         else
469             element.ensureCompletedTransitionsByProperty().remove(property);
470     }
471
472     if (matchingBackingAnimation && element.hasRunningTransitionsForProperty(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, element.ensureRunningTransitionsByProperty())) {
473         auto previouslyRunningTransition = element.ensureRunningTransitionsByProperty().take(property);
474         auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle();
475         // 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
476         //    transition is not equal to the value of the property in the after-change style, then:
477         if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &currentStyle, &afterChangeStyle)) {
478             // 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,
479             //    or if these two values cannot be interpolated, then implementations must cancel the running transition.
480             previouslyRunningTransition->cancelFromStyle();
481         } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) {
482             // 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
483             //    cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition.
484             previouslyRunningTransition->cancelFromStyle();
485         } else if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) {
486             // 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
487             //    style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition
488             previouslyRunningTransition->cancelFromStyle();
489
490             // and start a new transition whose:
491             //   - reversing-adjusted start value is the end value of the running transition,
492             //   - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of:
493             //       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
494             //       2. 1 minus the reversing shortening factor of the old transition.
495             //   - start time is the time of the style change event plus:
496             //       1. if the matching transition delay is nonnegative, the matching transition delay, or
497             //       2. if the matching transition delay is negative, the product of the new transition’s reversing shortening factor and the matching transition delay,
498             //   - end time is the start time plus the product of the matching transition duration and the new transition’s reversing shortening factor,
499             //   - start value is the current value of the property in the running transition,
500             //   - end value is the value of the property in the after-change style
501             auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle();
502             double transformedProgress = 1;
503             if (auto* effect = previouslyRunningTransition->effect()) {
504                 if (auto computedTimingProgress = effect->getComputedTiming().progress)
505                     transformedProgress = *computedTimingProgress;
506             }
507             auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0);
508             auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay());
509             auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor;
510
511             element.ensureRunningTransitionsByProperty().set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
512         } else {
513             // 4. Otherwise, implementations must cancel the running transition
514             previouslyRunningTransition->cancelFromStyle();
515
516             // and start a new transition whose:
517             //   - start time is the time of the style change event plus the matching transition delay,
518             //   - end time is the start time plus the matching transition duration,
519             //   - start value is the current value of the property in the running transition,
520             //   - end value is the value of the property in the after-change style,
521             //   - reversing-adjusted start value is the same as the start value, and
522             //   - reversing shortening factor is 1.
523             auto delay = Seconds(matchingBackingAnimation->delay());
524             auto duration = Seconds(matchingBackingAnimation->duration());
525             auto& reversingAdjustedStartStyle = currentStyle;
526             auto reversingShorteningFactor = 1;
527             element.ensureRunningTransitionsByProperty().set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
528         }
529     }
530 }
531
532 void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle)
533 {
534     // In case this element is newly getting a "display: none" we need to cancel all of its transitions and disregard new ones.
535     if (currentStyle.hasTransitions() && currentStyle.display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
536         if (element.hasRunningTransitions()) {
537             auto runningTransitions = element.ensureRunningTransitionsByProperty();
538             for (const auto& cssTransitionsByCSSPropertyIDMapItem : runningTransitions)
539                 cssTransitionsByCSSPropertyIDMapItem.value->cancelFromStyle();
540         }
541         return;
542     }
543
544     // Section 3 "Starting of transitions" from the CSS Transitions Level 1 specification.
545     // https://drafts.csswg.org/css-transitions-1/#starting
546
547     auto generationTime = MonotonicTime::now();
548
549     // First, let's compile the list of all CSS properties found in the current style and the after-change style.
550     bool transitionPropertiesContainAll = false;
551     HashSet<CSSPropertyID> transitionProperties;
552     compileTransitionPropertiesInStyle(currentStyle, transitionProperties, transitionPropertiesContainAll);
553     compileTransitionPropertiesInStyle(afterChangeStyle, transitionProperties, transitionPropertiesContainAll);
554
555     if (transitionPropertiesContainAll) {
556         auto numberOfProperties = CSSPropertyAnimation::getNumProperties();
557         for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) {
558             Optional<bool> isShorthand;
559             auto property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
560             if (isShorthand && *isShorthand)
561                 continue;
562             updateCSSTransitionsForElementAndProperty(element, property, currentStyle, afterChangeStyle, generationTime);
563         }
564         return;
565     }
566
567     for (auto property : transitionProperties)
568         updateCSSTransitionsForElementAndProperty(element, property, currentStyle, afterChangeStyle, generationTime);
569 }
570
571 } // namespace WebCore