mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1004377 - Dispatch events for CSS Animations with empty keyframes rules; r=dholbert
This patch removes the check that skipped queueing events for animations without keyframes since the spec indicates such animations should dispatch events. There is a further correctness fix here for the case where a keyframes rule is modified using the CSSOM so that it becomes empty. Previously when we came to create the new animation rules we would end up setting ElementAnimations::mNeedRefreshes to false since we check if the keyframes rule is empty and if it is we would skip all further processing (including setting mNeedsRefreshes). That means that: (a) We may end up unregistering from the refresh observer so we would never dispatch the end event for such an animation. (b) If the animation was running on the compositor we may never remove it from the compositor or may not do it in a timely fashion. To fix both these problems, this patch removes the check for an empty keyframes rule so that mNeedsRefreshes is set in this case.
This commit is contained in:
parent
c39677d5f1
commit
a3321a71fe
@ -69,6 +69,7 @@ ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime,
|
||||
ElementAnimation* anim = mAnimations[animIdx];
|
||||
|
||||
if (anim->mProperties.IsEmpty()) {
|
||||
// Empty @keyframes rule.
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -112,11 +113,6 @@ ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime,
|
||||
for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
|
||||
ElementAnimation* anim = mAnimations[animIdx];
|
||||
|
||||
if (anim->mProperties.IsEmpty()) {
|
||||
// Empty keyframes rule.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The ElapsedDurationAt() call here handles pausing. But:
|
||||
// FIXME: avoid recalculating every time when paused.
|
||||
TimeDuration elapsedDuration = anim->ElapsedDurationAt(aRefreshTime);
|
||||
@ -131,8 +127,9 @@ ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime,
|
||||
|
||||
// If the time fraction is null, we don't have fill data for the current
|
||||
// time so we shouldn't animate.
|
||||
if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction)
|
||||
if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(0.0 <= computedTiming.mTimeFraction &&
|
||||
computedTiming.mTimeFraction <= 1.0,
|
||||
@ -216,12 +213,6 @@ ElementAnimations::GetEventsAt(TimeStamp aRefreshTime,
|
||||
for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
|
||||
ElementAnimation* anim = mAnimations[animIdx];
|
||||
|
||||
// We should *not* skip animations with no keyframes (bug 1004377).
|
||||
if (anim->mProperties.IsEmpty()) {
|
||||
// Empty keyframes rule.
|
||||
continue;
|
||||
}
|
||||
|
||||
TimeDuration elapsedDuration = anim->ElapsedDurationAt(aRefreshTime);
|
||||
ComputedTiming computedTiming =
|
||||
ElementAnimation::GetComputedTimingAt(elapsedDuration, anim->mTiming);
|
||||
|
@ -61,6 +61,19 @@ function is_approx(float1, float2, error, desc) {
|
||||
desc + ": " + float1 + " and " + float2 + " should be within " + error);
|
||||
}
|
||||
|
||||
function findKeyframesRule(name) {
|
||||
for (var i = 0; i < document.styleSheets.length; i++) {
|
||||
var match = [].find.call(document.styleSheets[i].cssRules, function(rule) {
|
||||
return rule.type == CSSRule.KEYFRAMES_RULE &&
|
||||
rule.name == name;
|
||||
});
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Checks if off-main thread animation (OMTA) is available, and if it is, runs
|
||||
// the provided callback function. If OMTA is not available or is not
|
||||
// functioning correctly, the second callback, aOnSkip, is run instead.
|
||||
|
@ -136,6 +136,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=435442
|
||||
to { margin-top: 150px !important; /* ignored */
|
||||
margin-bottom: 50px; }
|
||||
}
|
||||
|
||||
@keyframes empty { }
|
||||
@keyframes nearlyempty { to { margin-left: 100px; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -1776,6 +1779,83 @@ check_events([{ type: 'animationstart', target: div,
|
||||
+ " animation");
|
||||
done_div();
|
||||
|
||||
/*
|
||||
* Bug 1004377 - Animations with empty keyframes rule
|
||||
*/
|
||||
|
||||
new_div("margin-right: 200px; animation: empty 2s 1s both");
|
||||
listen();
|
||||
advance_clock(0);
|
||||
check_events([], "events during delay");
|
||||
advance_clock(2000); // Skip to middle of animation
|
||||
div.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationstart', target: div,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" }],
|
||||
"middle of animation with empty keyframes rule");
|
||||
advance_clock(1000); // Skip to end of animation
|
||||
div.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationend', target: div,
|
||||
animationName: 'empty', elapsedTime: 2,
|
||||
pseudoElement: "" }],
|
||||
"end of animation with empty keyframes rule");
|
||||
done_div();
|
||||
|
||||
// Test with a zero-duration animation and empty @keyframes rule
|
||||
new_div("margin-right: 200px; animation: empty 0s 1s both");
|
||||
listen();
|
||||
advance_clock(0);
|
||||
advance_clock(1000);
|
||||
div.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationstart', target: div,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" },
|
||||
{ type: 'animationend', target: div,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" }],
|
||||
"end of zero-duration animation with empty keyframes rule");
|
||||
done_div();
|
||||
|
||||
// Test with a keyframes rule that becomes empty
|
||||
new_div("animation: nearlyempty 1s both linear");
|
||||
advance_clock(0);
|
||||
advance_clock(500);
|
||||
is(cs.getPropertyValue("margin-left"), "50px",
|
||||
"margin-left for animation that is about to be emptied");
|
||||
listen();
|
||||
findKeyframesRule("nearlyempty").deleteRule("to");
|
||||
is(cs.getPropertyValue("margin-left"), "0px",
|
||||
"margin-left for animation with (now) empty keyframes rule");
|
||||
check_events([], "events after emptying keyframes rule");
|
||||
advance_clock(500);
|
||||
div.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationend', target: div,
|
||||
animationName: 'nearlyempty', elapsedTime: 1,
|
||||
pseudoElement: "" }],
|
||||
"events at end of animation with newly " +
|
||||
"empty keyframes rule");
|
||||
done_div();
|
||||
|
||||
// Test when we update to point to an empty animation
|
||||
new_div("animation: always_fifty 1s both linear");
|
||||
advance_clock(0);
|
||||
advance_clock(500);
|
||||
is(cs.getPropertyValue("margin-left"), "50px",
|
||||
"margin-left for animation that will soon point to an empty keyframes rule");
|
||||
listen();
|
||||
div.style.animationName = "empty";
|
||||
is(cs.getPropertyValue("margin-left"), "0px",
|
||||
"margin-left for animation now points to empty keyframes rule");
|
||||
advance_clock(500);
|
||||
div.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationstart', target: div,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" }],
|
||||
"events at start of animation updated to use " +
|
||||
"empty keyframes rule");
|
||||
|
||||
done_div();
|
||||
|
||||
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
|
||||
|
||||
</script>
|
||||
|
@ -138,6 +138,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=964646
|
||||
transform: translate(50px); }
|
||||
}
|
||||
|
||||
@keyframes empty { }
|
||||
@keyframes nearlyempty {
|
||||
to {
|
||||
transform: translate(100px);
|
||||
}
|
||||
}
|
||||
|
||||
.target {
|
||||
/* The animation target needs geometry in order to qualify for OMTA */
|
||||
width: 100px;
|
||||
@ -1915,6 +1922,105 @@ addAsyncAnimTest(function *() {
|
||||
done_div();
|
||||
});
|
||||
|
||||
/*
|
||||
* Bug 1004377 - Animations with empty keyframes rule
|
||||
*/
|
||||
|
||||
addAsyncAnimTest(function *() {
|
||||
new_div("margin-right: 200px; animation: empty 2s 1s both");
|
||||
listen();
|
||||
advance_clock(0);
|
||||
yield waitForPaintsFlushed();
|
||||
check_events([], "events during delay");
|
||||
advance_clock(2000); // Skip to middle of animation
|
||||
gDiv.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationstart', target: gDiv,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" }],
|
||||
"events during middle of animation with empty keyframes rule");
|
||||
advance_clock(1000); // Skip to end of animation
|
||||
gDiv.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationend', target: gDiv,
|
||||
animationName: 'empty', elapsedTime: 2,
|
||||
pseudoElement: "" }],
|
||||
"events at end of animation with empty keyframes rule");
|
||||
done_div();
|
||||
});
|
||||
|
||||
// Test with a zero-duration animation and empty @keyframes rule
|
||||
addAsyncAnimTest(function *() {
|
||||
new_div("margin-right: 200px; animation: empty 0s 1s both");
|
||||
listen();
|
||||
yield waitForPaintsFlushed();
|
||||
advance_clock(1000);
|
||||
gDiv.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationstart', target: gDiv,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" },
|
||||
{ type: 'animationend', target: gDiv,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" }],
|
||||
"events at end of zero-duration animation with " +
|
||||
"empty keyframes rule");
|
||||
done_div();
|
||||
});
|
||||
|
||||
// Test with a keyframes rule that becomes empty
|
||||
addAsyncAnimTest(function *() {
|
||||
new_div("animation: nearlyempty 1s both linear");
|
||||
yield waitForPaintsFlushed();
|
||||
advance_clock(500);
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor,
|
||||
"Animation is animating on compositor");
|
||||
|
||||
// Update keyframes rule and check the result gets removed
|
||||
listen();
|
||||
findKeyframesRule("nearlyempty").deleteRule("to");
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { }, RunningOn.MainThread,
|
||||
"Animation with (now) empty keyframes rule is cleared " +
|
||||
"from compositor");
|
||||
|
||||
// Check we still dispatch the end event however
|
||||
advance_clock(500);
|
||||
gDiv.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationend', target: gDiv,
|
||||
animationName: 'nearlyempty', elapsedTime: 1,
|
||||
pseudoElement: "" }],
|
||||
"events at end of animation with newly " +
|
||||
"empty keyframes rule");
|
||||
|
||||
done_div();
|
||||
});
|
||||
|
||||
// Test when we update to point to an empty animation
|
||||
addAsyncAnimTest(function *() {
|
||||
new_div("animation: always_fifty 1s both linear");
|
||||
yield waitForPaintsFlushed();
|
||||
advance_clock(500);
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor,
|
||||
"Animation is animating on compositor");
|
||||
|
||||
// Update animation name
|
||||
listen();
|
||||
gDiv.style.animationName = "empty";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { }, RunningOn.MainThread,
|
||||
"Animation updated to use empty keyframes rule is cleared " +
|
||||
"from compositor");
|
||||
|
||||
// Check events
|
||||
advance_clock(500);
|
||||
gDiv.clientTop; // Trigger events
|
||||
check_events([{ type: 'animationstart', target: gDiv,
|
||||
animationName: 'empty', elapsedTime: 0,
|
||||
pseudoElement: "" }],
|
||||
"events at start of animation updated to use " +
|
||||
"empty keyframes rule");
|
||||
|
||||
done_div();
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// Helper functions from test_animations.html
|
||||
|
Loading…
Reference in New Issue
Block a user