WebKit Bugzilla
Attachment 341114 Details for
Bug 185917
: [Web Animations] WebAnimation objects never get destroyed
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-185917-20180523211609.patch (text/plain), 31.83 KB, created by
Antoine Quint
on 2018-05-23 12:16:11 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Antoine Quint
Created:
2018-05-23 12:16:11 PDT
Size:
31.83 KB
patch
obsolete
>Subversion Revision: 232048 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 7fc488ecf86ee7fddf378526d89c3798d04f5433..8ae8061bc3866c2e1cb7f02e9c3ed06ed39f0c6c 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,62 @@ >+2018-05-23 Antoine Quint <graouts@apple.com> >+ >+ [Web Animations] WebAnimation objects never get destroyed >+ https://bugs.webkit.org/show_bug.cgi?id=185917 >+ <rdar://problem/39539371> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ The AnimationTimeline class keeps references to WebAnimation objects organized in various ways. First, there >+ are three main maps across which all animations are stored, one for non-subclass WebAnimation objects >+ (m_elementToAnimationsMap), one for CSSSAnimation objects (m_elementToCSSAnimationsMap) and one for CSSTranstion >+ objects (m_elementToCSSTransitionsMap). On top of that, we also keep a map to access CSSAnimation objects for >+ a given element by CSS animation name (m_elementToCSSAnimationByName) and another map to access CSSTransition >+ objects for a given element by CSS property (m_elementToCSSTransitionByCSSPropertyID). >+ >+ None of the RefPtr<WebAnimation> stored in these maps would get cleared when the document would get torn down, >+ which would also prevent the AnimationTimeline (and its DocumentTimeline subclass) from being destroyed. >+ >+ We now ensure that element and document tear-down correctly removes animations and clears those maps, which >+ in turn allows the DocumentTimeline to be destroyed, fixing the significant memory leak introduced by Web Animations >+ so far. >+ >+ * animation/AnimationTimeline.cpp: >+ (WebCore::AnimationTimeline::animationWasAddedToElement): Ensure we only add animations once since we might now try to >+ add the same animation twice if a declarative animation's timeline is set from its original timeline to null and to the >+ original timeline again (see WebAnimation::setTimeline()). >+ (WebCore::AnimationTimeline::animationWasRemovedFromElement): When an animation is removed from an element, ensure that >+ references to this animation stored in the m_elementToCSSAnimationByName and m_elementToCSSTransitionByCSSPropertyID maps >+ are cleared. >+ (WebCore::AnimationTimeline::removeAnimationsForElement): Instead of just calling cancel() on all known declarative animations >+ (this method used to be called cancelDeclarativeAnimationsForElement()), we now set the effect of known animations, declarative >+ or not, for the provided element which will in turn call animationWasRemovedFromElement() and remove the animation from all >+ maps that might keep a reference to it. >+ (WebCore::AnimationTimeline::updateCSSTransitionsForElement): Replace call to removeDeclarativeAnimation() with a simple call >+ to removeAnimation() which will remove references for this animation from the relevant maps. >+ (WebCore::AnimationTimeline::cancelOrRemoveDeclarativeAnimation): Ditto. >+ (WebCore::AnimationTimeline::cancelDeclarativeAnimationsForElement): Deleted. >+ (WebCore::AnimationTimeline::removeDeclarativeAnimation): Deleted. >+ * animation/AnimationTimeline.h: >+ (WebCore::AnimationTimeline::elementToCSSTransitionsMap): >+ * animation/WebAnimation.cpp: >+ (WebCore::WebAnimation::setEffect): >+ (WebCore::WebAnimation::setEffectInternal): Factor parts of setEffect() out into a new method that can be called from >+ AnimationTimeline::removeAnimationsForElement() to reset the m_effect member and correctly call animationWasRemovedFromElement() >+ without all the Web Animations machinery of setEffect(), which is a public API that has unwanted side effects (such as rejecting >+ promises). >+ (WebCore::WebAnimation::setTimeline): In the case of a declarative animation, we don't want to remove the animation from the >+ relevant maps because, while the timeline was set via the API, the element still has a transition or animation set up and we must >+ not break the relationship. >+ * animation/WebAnimation.h: >+ * dom/Document.cpp: >+ (WebCore::Document::prepareForDestruction): >+ * dom/Element.cpp: >+ (WebCore::Element::removedFromAncestor): >+ * dom/PseudoElement.cpp: >+ (WebCore::PseudoElement::clearHostElement): >+ * rendering/updating/RenderTreeUpdater.cpp: >+ (WebCore::RenderTreeUpdater::tearDownRenderers): >+ > 2018-05-21 Zalan Bujtas <zalan@apple.com> > > [LFC] Box::isDescendantOf() should work with out-of-flow elements. >diff --git a/Source/WebCore/animation/AnimationTimeline.cpp b/Source/WebCore/animation/AnimationTimeline.cpp >index 1aa8dbc7bb0f5b0780bc741174d705580a871dd1..a7f74bc3f23ddd0cb61261a845aba1ec8c823cf7 100644 >--- a/Source/WebCore/animation/AnimationTimeline.cpp >+++ b/Source/WebCore/animation/AnimationTimeline.cpp >@@ -96,14 +96,18 @@ HashMap<Element*, Vector<RefPtr<WebAnimation>>>& AnimationTimeline::relevantMapF > > void AnimationTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element) > { >- auto result = relevantMapForAnimation(animation).ensure(&element, [] { >+ auto& animations = relevantMapForAnimation(animation).ensure(&element, [] { > return Vector<RefPtr<WebAnimation>> { }; >- }); >- result.iterator->value.append(&animation); >+ }).iterator->value; >+ >+ if (!animations.contains(&animation)) >+ animations.append(&animation); > } > > void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element) > { >+ // First, we clear this animation from one of the m_elementToCSSAnimationsMap, m_elementToCSSTransitionsMap >+ // or m_elementToAnimationsMap map, whichever is relevant to this type of animation. > auto& map = relevantMapForAnimation(animation); > auto iterator = map.find(&element); > if (iterator == map.end()) >@@ -113,6 +117,28 @@ void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation, > animations.removeFirst(&animation); > if (!animations.size()) > map.remove(iterator); >+ >+ // Now, if we're dealing with a declarative animation, we remove it from either the m_elementToCSSAnimationByName >+ // or the m_elementToCSSTransitionByCSSPropertyID map, whichever is relevant to this type of animation. >+ if (is<CSSAnimation>(animation)) { >+ auto iterator = m_elementToCSSAnimationByName.find(&element); >+ if (iterator != m_elementToCSSAnimationByName.end()) { >+ auto& cssAnimationsByName = iterator->value; >+ auto& name = downcast<CSSAnimation>(animation).animationName(); >+ cssAnimationsByName.remove(name); >+ if (cssAnimationsByName.isEmpty()) >+ m_elementToCSSAnimationByName.remove(&element); >+ } >+ } else if (is<CSSTransition>(animation)) { >+ auto iterator = m_elementToCSSTransitionByCSSPropertyID.find(&element); >+ if (iterator != m_elementToCSSTransitionByCSSPropertyID.end()) { >+ auto& cssTransitionsByProperty = iterator->value; >+ auto property = downcast<CSSTransition>(animation).property(); >+ cssTransitionsByProperty.remove(property); >+ if (cssTransitionsByProperty.isEmpty()) >+ m_elementToCSSTransitionByCSSPropertyID.remove(&element); >+ } >+ } > } > > Vector<RefPtr<WebAnimation>> AnimationTimeline::animationsForElement(Element& element) const >@@ -127,11 +153,11 @@ Vector<RefPtr<WebAnimation>> AnimationTimeline::animationsForElement(Element& el > return animations; > } > >-void AnimationTimeline::cancelDeclarativeAnimationsForElement(Element& element) >+void AnimationTimeline::removeAnimationsForElement(Element& element) > { >- for (const auto& animation : animationsForElement(element)) { >- if (is<DeclarativeAnimation>(animation)) >- animation->cancel(); >+ for (auto& animation : animationsForElement(element)) { >+ animation->setEffectInternal(nullptr); >+ removeAnimation(animation.releaseNonNull()); > } > } > >@@ -289,7 +315,7 @@ void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const R > if (cssTransitionsByProperty.contains(property)) { > if (cssTransitionsByProperty.get(property)->matchesBackingAnimationAndStyles(backingAnimation, oldStyle, newStyle)) > continue; >- removeDeclarativeAnimation(cssTransitionsByProperty.take(property)); >+ removeAnimation(cssTransitionsByProperty.take(property).releaseNonNull()); > } > // Now we can create a new CSSTransition with the new backing animation provided it has a valid > // duration and the from and to values are distinct. >@@ -315,19 +341,13 @@ void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const R > m_elementToCSSTransitionByCSSPropertyID.remove(&element); > } > >-void AnimationTimeline::removeDeclarativeAnimation(RefPtr<DeclarativeAnimation> animation) >-{ >- animation->setEffect(nullptr); >- removeAnimation(animation.releaseNonNull()); >-} >- > void AnimationTimeline::cancelOrRemoveDeclarativeAnimation(RefPtr<DeclarativeAnimation> animation) > { > auto phase = animation->effect()->phase(); > if (phase != AnimationEffectReadOnly::Phase::Idle && phase != AnimationEffectReadOnly::Phase::After) > animation->cancel(); > else >- removeDeclarativeAnimation(animation); >+ removeAnimation(animation.releaseNonNull()); > } > > String AnimationTimeline::description() >diff --git a/Source/WebCore/animation/AnimationTimeline.h b/Source/WebCore/animation/AnimationTimeline.h >index f3968fe24adff7d3902e2c24569eef5a5a1a0d4f..baddc5ee855002b4a7f85dd3c30dfd4ead447aac 100644 >--- a/Source/WebCore/animation/AnimationTimeline.h >+++ b/Source/WebCore/animation/AnimationTimeline.h >@@ -59,7 +59,7 @@ public: > > const ListHashSet<RefPtr<WebAnimation>>& animations() const { return m_animations; } > Vector<RefPtr<WebAnimation>> animationsForElement(Element&) const; >- void cancelDeclarativeAnimationsForElement(Element&); >+ void removeAnimationsForElement(Element&); > void animationWasAddedToElement(WebAnimation&, Element&); > void animationWasRemovedFromElement(WebAnimation&, Element&); > >@@ -82,7 +82,6 @@ protected: > const HashMap<Element*, Vector<RefPtr<WebAnimation>>>& elementToAnimationsMap() { return m_elementToAnimationsMap; } > const HashMap<Element*, Vector<RefPtr<WebAnimation>>>& elementToCSSAnimationsMap() { return m_elementToCSSAnimationsMap; } > const HashMap<Element*, Vector<RefPtr<WebAnimation>>>& elementToCSSTransitionsMap() { return m_elementToCSSTransitionsMap; } >- void removeDeclarativeAnimation(RefPtr<DeclarativeAnimation>); > > private: > HashMap<Element*, Vector<RefPtr<WebAnimation>>>& relevantMapForAnimation(WebAnimation&); >diff --git a/Source/WebCore/animation/WebAnimation.cpp b/Source/WebCore/animation/WebAnimation.cpp >index 4dcb5e2b3829c8834a15c3d0795437bef5f0d956..296b19fb9f9415538377c05ca00800b9ebfb1470 100644 >--- a/Source/WebCore/animation/WebAnimation.cpp >+++ b/Source/WebCore/animation/WebAnimation.cpp >@@ -119,30 +119,41 @@ void WebAnimation::setEffect(RefPtr<AnimationEffectReadOnly>&& newEffect) > newEffect->animation()->setEffect(nullptr); > > // 7. Let the target effect of animation be new effect. >- m_effect = WTFMove(newEffect); >+ setEffectInternal(WTFMove(newEffect)); > > // 8. Run the procedure to update an animationâs finished state for animation with the did seek flag set to false, > // and the synchronously notify flag set to false. > updateFinishedState(DidSeek::No, SynchronouslyNotify::No); > >+ timingModelDidChange(); >+} >+ >+void WebAnimation::setEffectInternal(RefPtr<AnimationEffectReadOnly>&& newEffect) >+{ >+ auto oldEffect = m_effect; >+ >+ m_effect = WTFMove(newEffect); >+ >+ Element* previousTarget; >+ if (is<KeyframeEffectReadOnly>(oldEffect)) >+ previousTarget = downcast<KeyframeEffectReadOnly>(oldEffect.get())->target(); >+ >+ Element* newTarget; >+ if (is<KeyframeEffectReadOnly>(m_effect)) >+ newTarget = downcast<KeyframeEffectReadOnly>(m_effect.get())->target(); >+ > // Update the effect-to-animation relationships and the timeline's animation map. > if (oldEffect) { > oldEffect->setAnimation(nullptr); >- if (m_timeline && is<KeyframeEffectReadOnly>(oldEffect)) { >- if (auto* target = downcast<KeyframeEffectReadOnly>(oldEffect.get())->target()) >- m_timeline->animationWasRemovedFromElement(*this, *target); >- } >+ if (m_timeline && previousTarget && previousTarget != newTarget) >+ m_timeline->animationWasRemovedFromElement(*this, *previousTarget); > } > > if (m_effect) { > m_effect->setAnimation(this); >- if (m_timeline && is<KeyframeEffectReadOnly>(m_effect)) { >- if (auto* target = downcast<KeyframeEffectReadOnly>(m_effect.get())->target()) >- m_timeline->animationWasAddedToElement(*this, *target); >- } >+ if (m_timeline && newTarget && previousTarget != newTarget) >+ m_timeline->animationWasAddedToElement(*this, *newTarget); > } >- >- timingModelDidChange(); > } > > void WebAnimation::setTimeline(RefPtr<AnimationTimeline>&& timeline) >@@ -168,7 +179,10 @@ void WebAnimation::setTimeline(RefPtr<AnimationTimeline>&& timeline) > auto* keyframeEffect = downcast<KeyframeEffectReadOnly>(m_effect.get()); > auto* target = keyframeEffect->target(); > if (target) { >- if (m_timeline) >+ // In the case of a declarative animation, we don't want to remove the animation from the relevant maps because >+ // while the timeline was set via the API, the element still has a transition or animation set up and we must >+ // not break the relationship. >+ if (m_timeline && !isDeclarativeAnimation()) > m_timeline->animationWasRemovedFromElement(*this, *target); > if (timeline) > timeline->animationWasAddedToElement(*this, *target); >diff --git a/Source/WebCore/animation/WebAnimation.h b/Source/WebCore/animation/WebAnimation.h >index 3ab99762a60429862a435b8c0ccce978780245c8..dd621732c8f1961c27cc8d55878107e68c65bb67 100644 >--- a/Source/WebCore/animation/WebAnimation.h >+++ b/Source/WebCore/animation/WebAnimation.h >@@ -63,6 +63,7 @@ public: > > AnimationEffectReadOnly* effect() const { return m_effect.get(); } > void setEffect(RefPtr<AnimationEffectReadOnly>&&); >+ void setEffectInternal(RefPtr<AnimationEffectReadOnly>&&); > AnimationTimeline* timeline() const { return m_timeline.get(); } > virtual void setTimeline(RefPtr<AnimationTimeline>&&); > >diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp >index ca879472076d8b2b09d1572a7aaffca9d89adbb6..82c7b353abd3c0157603f3fb82e30465515885cc 100644 >--- a/Source/WebCore/dom/Document.cpp >+++ b/Source/WebCore/dom/Document.cpp >@@ -2336,11 +2336,6 @@ void Document::prepareForDestruction() > if (m_hasPreparedForDestruction) > return; > >- if (m_timeline) { >- m_timeline->detachFromDocument(); >- m_timeline = nullptr; >- } >- > if (m_frame) > m_frame->animation().detachFromDocument(this); > >@@ -2433,6 +2428,11 @@ void Document::prepareForDestruction() > > detachFromFrame(); > >+ if (m_timeline) { >+ m_timeline->detachFromDocument(); >+ m_timeline = nullptr; >+ } >+ > m_hasPreparedForDestruction = true; > > // Note that m_pageCacheState can be Document::AboutToEnterPageCache if our frame >diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp >index 06c4a06302984513da02fdad0b49da7348fb39da..c21729132c54ab65d82e5b3adc93b52529d1b109 100644 >--- a/Source/WebCore/dom/Element.cpp >+++ b/Source/WebCore/dom/Element.cpp >@@ -1797,7 +1797,7 @@ void Element::removedFromAncestor(RemovalType removalType, ContainerNode& oldPar > RefPtr<Frame> frame = document().frame(); > if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) { > if (auto* timeline = document().existingTimeline()) >- timeline->cancelDeclarativeAnimationsForElement(*this); >+ timeline->removeAnimationsForElement(*this); > } else if (frame) > frame->animation().cancelAnimations(*this); > >diff --git a/Source/WebCore/dom/PseudoElement.cpp b/Source/WebCore/dom/PseudoElement.cpp >index e82b4475543ceec48ab9a0a0f88abfaa9600eafd..a68567f248d1d8a4fe0034def04a583f390bdf0b 100644 >--- a/Source/WebCore/dom/PseudoElement.cpp >+++ b/Source/WebCore/dom/PseudoElement.cpp >@@ -92,7 +92,7 @@ void PseudoElement::clearHostElement() > > if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) { > if (auto* timeline = document().existingTimeline()) >- timeline->cancelDeclarativeAnimationsForElement(*this); >+ timeline->removeAnimationsForElement(*this); > } else if (auto* frame = document().frame()) > frame->animation().cancelAnimations(*this); > >diff --git a/Source/WebCore/rendering/updating/RenderTreeUpdater.cpp b/Source/WebCore/rendering/updating/RenderTreeUpdater.cpp >index 1c8c61267d097f3612c2b7fe24474e926f378d8d..282e58712ce4b8ae4406e65a612c13d3c7019e39 100644 >--- a/Source/WebCore/rendering/updating/RenderTreeUpdater.cpp >+++ b/Source/WebCore/rendering/updating/RenderTreeUpdater.cpp >@@ -555,7 +555,7 @@ void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownTy > if (teardownType == TeardownType::Full || teardownType == TeardownType::RendererUpdateCancelingAnimations) { > if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) { > if (timeline) >- timeline->cancelDeclarativeAnimationsForElement(element); >+ timeline->removeAnimationsForElement(element); > } else > animationController.cancelAnimations(element); > } >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 1a85f3184699b84efd464e44b01ea4c76631b5f5..7346f0cb7c37cba2f4d40ee32c7f6a11eba1326f 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,17 @@ >+2018-05-23 Antoine Quint <graouts@apple.com> >+ >+ [Web Animations] WebAnimation objects never get destroyed >+ https://bugs.webkit.org/show_bug.cgi?id=185917 >+ <rdar://problem/39539371> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Remove a homegrown test that was not correct and is no longer relevant thanks to the tests under imported/mozilla. >+ >+ * platform/win/TestExpectations: >+ * webanimations/css-transitions-expected.txt: Removed. >+ * webanimations/css-transitions.html: Removed. >+ > 2018-05-21 Chris Dumez <cdumez@apple.com> > > File's structured serialization should serialize lastModified attribute >diff --git a/LayoutTests/platform/win/TestExpectations b/LayoutTests/platform/win/TestExpectations >index a1d828ea449fcd9a2468a68dbe0d8e1da7c052e6..9957062b34ce647ba3c4936c6882a21aca05738a 100644 >--- a/LayoutTests/platform/win/TestExpectations >+++ b/LayoutTests/platform/win/TestExpectations >@@ -4023,7 +4023,6 @@ webkit.org/b/183393 fast/loader/redirect-to-invalid-url-using-meta-refresh-disal > webkit.org/b/183393 fast/loader/window-open-to-invalid-url-disallowed.html [ Skip ] > > webkit.org/b/183569 webanimations/css-animations.html [ Failure ] >-webkit.org/b/183569 webanimations/css-transitions.html [ Failure ] > > webkit.org/b/183953 imported/mozilla/css-animations/test_animation-cancel.html [ Failure ] > webkit.org/b/183953 imported/mozilla/css-animations/test_animation-finish.html [ Failure ] >diff --git a/LayoutTests/webanimations/css-transitions-expected.txt b/LayoutTests/webanimations/css-transitions-expected.txt >deleted file mode 100644 >index 2cbae40ad0886355f325cf1f1160b48a37366234..0000000000000000000000000000000000000000 >--- a/LayoutTests/webanimations/css-transitions-expected.txt >+++ /dev/null >@@ -1,11 +0,0 @@ >- >-PASS A CSS Transition should be reflected entirely as a CSSTransition object on the timeline. >-PASS Web Animations should reflect the transition-delay property. >-PASS Web Animations should reflect the transition-duration property. >-PASS Web Animations should reflect the transition-property property. >-PASS Web Animations should not reflect the transition-timing-function property on the effect's timing. >-PASS Calling finish() on the animation no longer lists the animation after it has been running. >-PASS Seeking the animation to its end time no longer lists the animation after it has been running. >-PASS Setting the target's transition-property to none no longer lists the animation after it has been running. >-PASS Seeking the target's transition-duration to 0 no longer lists the animation after it has been running. >- >diff --git a/LayoutTests/webanimations/css-transitions.html b/LayoutTests/webanimations/css-transitions.html >deleted file mode 100644 >index 052e4c4c39c5121b871ea7960d4fd48a51a38c87..0000000000000000000000000000000000000000 >--- a/LayoutTests/webanimations/css-transitions.html >+++ /dev/null >@@ -1,188 +0,0 @@ >-<!DOCTYPE html><!-- webkit-test-runner [ enableWebAnimationsCSSIntegration=true ] --> >-<meta charset=utf-8> >-<title>CSS Transitions</title> >-<body> >-<script src="../resources/testharness.js"></script> >-<script src="../resources/testharnessreport.js"></script> >-<script> >- >-'use strict'; >- >-function targetTest(testCallback, description) >-{ >- test(() => { >- const target = document.body.appendChild(document.createElement("div")); >- testCallback(target); >- target.remove(); >- }, description); >-} >- >-function forceStyleUpdate(element) >-{ >- element.offsetLeft; >-} >- >-targetTest(target => { >- target.style.left = "50px"; >- >- assert_array_equals(target.getAnimations(), [], "An element should not have any animations initially."); >- >- target.style.transitionProperty = "left"; >- target.style.transitionDelay = "-1s"; >- target.style.transitionDuration = "2s"; >- target.style.transitionTimingFunction = "linear"; >- >- assert_array_equals(target.getAnimations(), [], "Setting CSS transitions properties should not yield a CSSTransition until a new target value is specified."); >- >- forceStyleUpdate(target); >- target.style.left = "100px"; >- >- const animation = target.getAnimations()[0]; >- assert_true(animation instanceof CSSTransition, "The animation is a CSSTransition."); >- assert_true(animation instanceof Animation, "The animation is an Animation."); >- assert_equals(animation.timeline, target.ownerDocument.timeline, "The animation's timeline is set to the element's document timeline."); >- assert_equals(animation.playState, "running", "The animation is running as soon as it's created."); >- assert_equals(animation.transitionProperty, "left", "The animation's transitionProperty is set."); >- >- const effect = animation.effect; >- assert_true(effect instanceof KeyframeEffectReadOnly, "The animation's effect is a KeyframeEffectReadOnly."); >- assert_false(effect instanceof KeyframeEffect, "The animation's effect is not a KeyframeEffect."); >- assert_equals(effect.target, target, "The animation's effect is targeting the target."); >- >- const timing = animation.effect.timing; >- assert_equals(timing.delay, -1000, "The animation's delay property matches the transition-delay property."); >- assert_equals(timing.duration, 2000, "The animation's duration property matches the transition-duration property."); >- >- const computedTiming = animation.effect.getComputedTiming(); >- assert_equals(computedTiming.activeDuration, 2000, "The animations's computed timing activeDuration property matches the properties set by CSS"); >- assert_equals(computedTiming.currentIteration, 0, "The animations's computed timing currentIteration property matches the properties set by CSS"); >- assert_equals(computedTiming.delay, -1000, "The animations's computed timing delay property matches the properties set by CSS"); >- assert_equals(computedTiming.duration, 2000, "The animations's computed timing duration property matches the properties set by CSS"); >- assert_equals(computedTiming.endDelay, 0, "The animations's computed timing endDelay property matches the properties set by CSS"); >- assert_equals(computedTiming.endTime, 1000, "The animations's computed timing endTime property matches the properties set by CSS"); >- assert_equals(computedTiming.iterationStart, 0, "The animations's computed timing iterationStart property matches the properties set by CSS"); >- assert_equals(computedTiming.iterations, 1, "The animations's computed timing iterations property matches the properties set by CSS"); >- assert_equals(computedTiming.localTime, 0, "The animations's computed timing localTime property matches the properties set by CSS"); >- assert_equals(computedTiming.progress, 0.5, "The animations's computed timing progress property matches the properties set by CSS"); >- assert_equals(computedTiming.fill, "backwards", "The animations's computed timing fill property matches the default value set for transitions"); >- assert_equals(computedTiming.easing, "linear", "The animations's computed timing easing property matches the properties set by CSS"); >- assert_equals(computedTiming.direction, "normal", "The animations's computed timing direction property matches the properties set by CSS"); >- >- const keyframes = animation.effect.getKeyframes(); >- assert_equals(keyframes.length, 2, "The animation's effect has two keyframes."); >- assert_equals(keyframes[0].offset, 0, "The animation's effect's first keyframe has a 0 offset."); >- assert_equals(keyframes[0].left, "50px", "The animation's effect's first keyframe has its left property set to 50px."); >- assert_equals(keyframes[1].offset, 1, "The animation's effect's first keyframe has a 1 offset."); >- assert_equals(keyframes[1].left, "100px", "The animation's effect's first keyframe has its left property set to 100px."); >- >- assert_equals(getComputedStyle(effect.target).left, "75px", "The animation's target's computed style reflects the animation state."); >-}, "A CSS Transition should be reflected entirely as a CSSTransition object on the timeline."); >- >-function transitionProperty(target, propertyName, from, to) >-{ >- target.style[propertyName] = from; >- forceStyleUpdate(target); >- target.style[propertyName] = to; >-} >- >-function transitionPropertyUpdateTest(testCallback, description) >-{ >- targetTest(target => { >- target.style.transitionProperty = "left"; >- target.style.transitionDelay = "-500ms"; >- target.style.transitionDuration = "2s"; >- transitionProperty(target, "left", "50px", "100px"); >- testCallback(target); >- }, description); >-} >- >-transitionPropertyUpdateTest(target => { >- const initialAnimation = target.getAnimations()[0]; >- assert_equals(initialAnimation.effect.timing.delay, -500, "The animation's delay matches the initial transition-delay property."); >- >- target.style.transitionDelay = 0; >- const updatedAnimation = target.getAnimations()[0]; >- assert_equals(updatedAnimation.effect.timing.delay, 0, "The animation's delay matches the updated transition-delay property."); >- >- assert_not_equals(initialAnimation, updatedAnimation, "The animations before and after updating the transition-delay property differ."); >-}, "Web Animations should reflect the transition-delay property."); >- >-transitionPropertyUpdateTest(target => { >- const initialAnimation = target.getAnimations()[0]; >- assert_equals(initialAnimation.effect.timing.duration, 2000, "The animation's duration matches the initial transition-duration property."); >- >- target.style.transitionDuration = "1s"; >- const updatedAnimation = target.getAnimations()[0]; >- assert_equals(updatedAnimation.effect.timing.duration, 1000, "The animation's duration matches the updated transition-duration property."); >- >- assert_not_equals(initialAnimation, updatedAnimation, "The animations before and after updating the transition-duration property differ."); >-}, "Web Animations should reflect the transition-duration property."); >- >-targetTest(target => { >- target.style.transitionDuration = "2s"; >- >- target.style.transitionProperty = "left"; >- transitionProperty(target, "left", "50px", "100px"); >- >- const initialAnimation = target.getAnimations()[0]; >- assert_equals(target.getAnimations()[0].transitionProperty, "left", "The animation's property matches the initial transition-property CSS property."); >- >- const initialKeyframes = initialAnimation.effect.getKeyframes(); >- assert_equals(initialKeyframes.length, 2); >- assert_equals(initialKeyframes[0].offset, 0); >- assert_equals(initialKeyframes[0].left, "50px"); >- assert_equals(initialKeyframes[1].offset, 1); >- assert_equals(initialKeyframes[1].left, "100px"); >- >- target.style.transitionProperty = "top"; >- transitionProperty(target, "top", "50px", "100px"); >- >- const updatedAnimation = target.getAnimations()[0]; >- assert_equals(updatedAnimation.transitionProperty, "top", "The animation's property matches the updated transition-property CSS property."); >- >- const updatedKeyframes = updatedAnimation.effect.getKeyframes(); >- assert_equals(updatedKeyframes.length, 2); >- assert_equals(updatedKeyframes[0].offset, 0); >- assert_equals(updatedKeyframes[0].top, "50px"); >- assert_equals(updatedKeyframes[1].offset, 1); >- assert_equals(updatedKeyframes[1].top, "100px"); >- >- assert_not_equals(updatedAnimation, initialAnimation, "Changing the transition-property property generates a different CSSTransition object."); >-}, "Web Animations should reflect the transition-property property."); >- >-transitionPropertyUpdateTest(target => { >- const initialAnimation = target.getAnimations()[0]; >- assert_equals(initialAnimation.effect.timing.easing, "linear", "The animation's easing does not match the default transition-timing-function property."); >- >- target.style.transitionTimingFunction = "ease-in"; >- const updatedAnimation = target.getAnimations()[0]; >- assert_equals(updatedAnimation.effect.timing.easing, "linear", "The animation's easing does not match the updated transition-timing-function property."); >- >- target.style.removeProperty("transition-timing-function"); >- const finalAnimation = target.getAnimations()[0]; >- assert_equals(target.getAnimations()[0].effect.timing.easing, "linear", "The animation's easing does not match the default transition-timing-function value when the property is not set."); >-}, "Web Animations should not reflect the transition-timing-function property on the effect's timing."); >- >-function runAnimationCompletionTest(finalAssertionCallback, description) >-{ >- targetTest(target => { >- target.style.transitionProperty = "left"; >- target.style.transitionDuration = "1s"; >- transitionProperty(target, "left", "50px", "100px"); >- >- assert_equals(target.getAnimations().length, 1, "Seting the transition-duration property on top of the transition-property yields a CSSTransition."); >- assert_equals(target.getAnimations()[0].playState, "running", "Seting the transition-duration property on top of the transition-property yields a running CSSTransition."); >- >- finalAssertionCallback(target.getAnimations()[0]); >- >- assert_array_equals(target.getAnimations(), [], `${description} no longer lists the animation.`); >- }, `${description} no longer lists the animation after it has been running.`); >-} >- >-runAnimationCompletionTest(animation => animation.finish(), "Calling finish() on the animation"); >-runAnimationCompletionTest(animation => animation.currentTime = animation.effect.timing.duration, "Seeking the animation to its end time"); >-runAnimationCompletionTest(animation => animation.effect.target.style.transitionProperty = "none", "Setting the target's transition-property to none"); >-runAnimationCompletionTest(animation => animation.effect.target.style.transitionDuration = 0, "Seeking the target's transition-duration to 0"); >- >-</script> >-</body> >\ No newline at end of file
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 185917
:
341114
|
341120
|
341122
|
341128
|
341201
|
341213
|
341273
|
341275