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