Bug 847287 patch 6 - Set mWinsInCascade for CSS Animations. r=birtles

This is the main patch for the bug; it makes us use the mechanism added
in bug 1125455 to avoid sending animations that aren't currently
applying to the compositor.

Patch 7 is needed to make this code rerun in all the cases where we need
to rerun it, though.
This commit is contained in:
L. David Baron 2015-03-31 15:05:54 -07:00
parent 538784230b
commit a7bdf3d859
6 changed files with 170 additions and 42 deletions

View File

@ -160,9 +160,12 @@ struct AnimationProperty
// then use this to decide whether to apply the style both in the CSS
// cascade and for OMTA.
//
// FIXME (bug 847287): For CSS Animations, which are overridden by
// !important rules in the cascade, we actually determine this from
// the CSS cascade computations, and then use it for OMTA.
// For CSS Animations, which are overridden by !important rules in the
// cascade, we actually determine this from the CSS cascade
// computations, and then use it for OMTA.
// **NOTE**: For CSS animations, we only bother setting mWinsInCascade
// accurately for properties that we can animate on the compositor.
// For other properties, we make it always be true.
bool mWinsInCascade;
InfallibleTArray<AnimationPropertySegment> mSegments;

View File

@ -69,6 +69,9 @@ public:
static void Initialize();
#endif
// NOTE: This can return null after Disconnect().
nsPresContext* PresContext() const { return mPresContext; }
/**
* Notify the manager that the pres context is going away.
*/

View File

@ -391,6 +391,8 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
newPlayers[newPlayerIdx]->Cancel();
}
UpdateCascadeResults(aStyleContext, collection);
TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
UpdateStyleAndEvents(collection, refreshTime,
EnsureStyleRule_IsNotThrottled);
@ -706,6 +708,115 @@ nsAnimationManager::BuildSegment(InfallibleTArray<AnimationPropertySegment>&
return true;
}
/* static */ void
nsAnimationManager::UpdateCascadeResults(
nsStyleContext* aStyleContext,
AnimationPlayerCollection* aElementAnimations)
{
/*
* Figure out which properties we need to examine.
*/
// size of 2 since we only currently have 2 properties we animate on
// the compositor
nsAutoTArray<nsCSSProperty, 2> propertiesToTrack;
{
nsCSSPropertySet propertiesToTrackAsSet;
for (size_t playerIdx = aElementAnimations->mPlayers.Length();
playerIdx-- != 0; ) {
const AnimationPlayer* player = aElementAnimations->mPlayers[playerIdx];
const Animation* anim = player->GetSource();
if (!anim) {
continue;
}
for (size_t propIdx = 0, propEnd = anim->Properties().Length();
propIdx != propEnd; ++propIdx) {
const AnimationProperty& prop = anim->Properties()[propIdx];
// We only bother setting mWinsInCascade for properties that we
// can animate on the compositor.
if (nsCSSProps::PropHasFlags(prop.mProperty,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR)) {
if (!propertiesToTrackAsSet.HasProperty(prop.mProperty)) {
propertiesToTrack.AppendElement(prop.mProperty);
propertiesToTrackAsSet.AddProperty(prop.mProperty);
}
}
}
}
}
/*
* Determine whether those properties are set in things that
* override animations.
*/
nsCSSPropertySet propertiesOverridden;
nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
aStyleContext,
propertiesOverridden);
/*
* Set mWinsInCascade based both on what is overridden at levels
* higher than animations and based on one animation overriding
* another.
*
* We iterate from the last animation to the first, just like we do
* when calling ComposeStyle from
* AnimationPlayerCollection::EnsureStyleRuleFor. Later animations
* override earlier ones, so we add properties to the set of
* overridden properties as we encounter them, if the animation is
* currently in effect.
*/
bool changed = false;
for (size_t playerIdx = aElementAnimations->mPlayers.Length();
playerIdx-- != 0; ) {
AnimationPlayer* player = aElementAnimations->mPlayers[playerIdx];
Animation* anim = player->GetSource();
if (!anim) {
continue;
}
for (size_t propIdx = 0, propEnd = anim->Properties().Length();
propIdx != propEnd; ++propIdx) {
AnimationProperty& prop = anim->Properties()[propIdx];
// We only bother setting mWinsInCascade for properties that we
// can animate on the compositor.
if (nsCSSProps::PropHasFlags(prop.mProperty,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR)) {
bool newWinsInCascade =
!propertiesOverridden.HasProperty(prop.mProperty);
if (newWinsInCascade != prop.mWinsInCascade) {
changed = true;
}
prop.mWinsInCascade = newWinsInCascade;
if (prop.mWinsInCascade && anim->IsInEffect()) {
// This animation is in effect right now, so it overrides
// earlier animations. (For animations that aren't in effect,
// we set mWinsInCascade as though they were, but they don't
// suppress animations lower in the cascade.)
propertiesOverridden.AddProperty(prop.mProperty);
}
}
}
}
if (changed) {
nsPresContext* presContext = aElementAnimations->mManager->PresContext();
presContext->RestyleManager()->IncrementAnimationGeneration();
aElementAnimations->UpdateAnimationGeneration(presContext);
aElementAnimations->PostUpdateLayerAnimations();
// Invalidate our style rule.
aElementAnimations->mNeedsRefreshes = true;
aElementAnimations->mStyleRuleRefreshTime = TimeStamp();
}
}
/* virtual */ void
nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime)
{

View File

@ -237,6 +237,10 @@ private:
mozilla::css::Declaration* aFromDeclaration,
float aToKey, nsStyleContext* aToContext);
static void UpdateCascadeResults(nsStyleContext* aStyleContext,
mozilla::AnimationPlayerCollection*
aElementAnimations);
// The guts of DispatchEvents
void DoDispatchEvents();

View File

@ -685,7 +685,9 @@ nsTransitionManager::UpdateCascadeResults(
}
if (changed) {
mPresContext->RestyleManager()->IncrementAnimationGeneration();
aTransitions->UpdateAnimationGeneration(mPresContext);
aTransitions->PostUpdateLayerAnimations();
// Invalidate our style rule.
aTransitions->mStyleRuleRefreshTime = TimeStamp();

View File

@ -430,48 +430,42 @@ addAsyncAnimTest(function *() {
yield waitForPaintsFlushed();
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
"anim2 + anim1 + anim2, translate at 2s");
// Bug 980769
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
"anim2 + anim1 + anim2, opacity at 2s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
"anim2 + anim1 + anim2, opacity at 2s");
// drop one of the anim2, and list anim3 as well, which animates
// the same property as anim2
gDiv.style.animation = "anim1 linear 10s, anim2 linear 20s, anim3 linear 10s";
yield waitForPaintsFlushed();
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
"anim1 + anim2 + anim3, translate at 2s");
// Bug 980769
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0",
"anim1 + anim2 + anim3, opacity at 2s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0",
"anim1 + anim2 + anim3, opacity at 2s");
advance_clock(1000);
omta_is("transform", { tx: 48 }, RunningOn.Compositor,
"anim1 + anim2 + anim3, translate at 3s");
// Bug 980769
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
"anim1 + anim2 + anim3, opacity at 3s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
"anim1 + anim2 + anim3, opacity at 3s");
// now swap the anim3 and anim2 order
gDiv.style.animation = "anim1 linear 10s, anim3 linear 10s, anim2 linear 20s";
yield waitForPaintsFlushed();
omta_is("transform", { tx: 48 }, RunningOn.Compositor,
"anim1 + anim3 + anim2, translate at 3s");
// Bug 980769
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.15",
"anim1 + anim3 + anim2, opacity at 3s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.15",
"anim1 + anim3 + anim2, opacity at 3s");
advance_clock(2000); // (unlike test_animations.html, we seek 2s forwards here
// since at 4s anim2 and anim3 produce the same result so
// we can't tell which won.)
omta_is("transform", { tx: 80 }, RunningOn.Compositor,
"anim1 + anim3 + anim2, translate at 5s");
// Bug 980769
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.25",
"anim1 + anim3 + anim2, opacity at 5s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.25",
"anim1 + anim3 + anim2, opacity at 5s");
// swap anim3 and anim2 back
gDiv.style.animation = "anim1 linear 10s, anim2 linear 20s, anim3 linear 10s";
yield waitForPaintsFlushed();
omta_is("transform", { tx: 80 }, RunningOn.Compositor,
"anim1 + anim2 + anim3, translate at 5s");
// Bug 980769
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.3",
"anim1 + anim2 + anim3, opacity at 5s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.3",
"anim1 + anim2 + anim3, opacity at 5s");
// seek past end of anim1
advance_clock(5100);
yield waitForPaints();
@ -494,9 +488,8 @@ addAsyncAnimTest(function *() {
yield waitForPaintsFlushed();
omta_is("transform", { tx: 82 }, RunningOn.Compositor,
"anim1 + anim2 + anim3, translate at 11s with fill mode");
// Bug 980769 - We should get 0.9 but instead
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.9",
"anim1 + anim2 + anim3, opacity at 11s");
is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.9",
"anim1 + anim2 + anim3, opacity at 11s");
done_div();
});
@ -1527,9 +1520,8 @@ addAsyncAnimTest(function *() {
new_div("animation: always_fifty 1s linear infinite; " +
"transform: translate(200px) ! important;");
yield waitForPaintsFlushed();
// Bug 847287 - off main thread animations don't cascade correctly
omta_todo_is("transform", { tx: 200 }, RunningOn.TodoMainThread,
"important author rules override animations");
omta_is("transform", { tx: 200 }, RunningOn.MainThread,
"important author rules override animations");
done_div();
});
@ -1721,11 +1713,9 @@ addAsyncAnimTest(function *() {
"animation-timing-function: linear; " +
"animation-delay: -250ms, -250ms, -750ms, -500ms;");
yield waitForPaintsFlushed();
// Bug 980769 - off main thread animations incorrectly handle multiple
// animations of the same property and element
omta_todo_is("opacity", 0.75, RunningOn.Compositor,
"animation-name list length is the length that matters, " +
"and the last occurrence of a name wins");
omta_is("opacity", 0.75, RunningOn.Compositor,
"animation-name list length is the length that matters, " +
"and the last occurrence of a name wins");
omta_is("transform", { ty: 25 }, RunningOn.Compositor,
"animation-name list length is the length that matters");
done_div();
@ -2170,41 +2160,44 @@ addAsyncAnimTest(function *() {
new_div("animation: anim2 1s linear forwards; opacity: 0.5 ! important");
yield waitForPaintsFlushed();
omta_todo_is("opacity", 0.5, RunningOn.TodoMainThread,
omta_is("opacity", 0.5, RunningOn.TodoMainThread,
"opacity overriding animation at start (0s)");
advance_clock(750);
omta_todo_is("opacity", 0.5, RunningOn.TodoMainThread,
omta_is("opacity", 0.5, RunningOn.TodoMainThread,
"opacity overriding animation while running (750ms)");
advance_clock(1000);
omta_todo_is("opacity", 0.5, RunningOn.TodoMainThread,
omta_is("opacity", 0.5, RunningOn.TodoMainThread,
"opacity overriding animation while filling (1750ms)");
done_div();
new_div("animation: anim2 1s linear; opacity: 0.5 ! important");
yield waitForPaintsFlushed();
omta_todo_is("opacity", 0.5, RunningOn.TodoMainThread,
omta_is("opacity", 0.5, RunningOn.TodoMainThread,
"opacity overriding animation at start (0s)");
advance_clock(750);
omta_todo_is("opacity", 0.5, RunningOn.TodoMainThread,
omta_is("opacity", 0.5, RunningOn.TodoMainThread,
"opacity overriding animation while running (750ms)");
advance_clock(1000);
omta_todo_is("opacity", 0.5, RunningOn.TodoMainThread,
omta_is("opacity", 0.5, RunningOn.TodoMainThread,
"opacity overriding animation after complete (1750ms)");
done_div();
// One animation overriding another, and then not.
new_div("animation: anim2 1s linear, anim3 500ms linear reverse");
yield waitForPaintsFlushed();
omta_todo_is("opacity", 1, RunningOn.Compositor,
omta_is("opacity", 1, RunningOn.Compositor,
"anim3 overriding anim2 at start (0s)");
advance_clock(400);
omta_todo_is("opacity", 0.2, RunningOn.Compositor,
omta_is("opacity", 0.2, RunningOn.Compositor,
"anim3 overriding anim2 at 400ms");
advance_clock(200);
// Wait for paints because we're resending animations to the
// compositor via an UpdateOpacityLayer hint, which does the resending
// via painting.
yield waitForPaints();
// FIXME: Half of this test fails, which means that omta_todo_is fails
// just as much as omta_is. (The value is wrong on both compositor and
// main threads.)
omta_is("opacity", 0.6, RunningOn.Compositor,
"anim2 at 600ms");
done_div();
@ -2220,7 +2213,7 @@ addAsyncAnimTest(function *() {
"anim2 at 300ms");
gDiv.style.animation = "anim2 1s steps(8, end), anim3 500ms steps(4, end)";
yield waitForPaintsFlushed();
omta_todo_is("opacity", 0, RunningOn.Compositor,
omta_is("opacity", 0, RunningOn.Compositor,
"anim3 overriding anim2 at 300ms");
advance_clock(475);
omta_is("opacity", 0.75, RunningOn.Compositor,
@ -2230,9 +2223,15 @@ addAsyncAnimTest(function *() {
// compositor via an UpdateOpacityLayer hint, which does the resending
// via painting.
yield waitForPaints();
// FIXME: Half of this test fails, which means that omta_todo_is fails
// just as much as omta_is. (The value is wrong on both compositor and
// main threads.)
omta_is("opacity", 0.75, RunningOn.Compositor,
"anim2 at 825ms");
advance_clock(75);
// FIXME: Half of this test fails, which means that omta_todo_is fails
// just as much as omta_is. (The value is wrong on both compositor and
// main threads.)
omta_is("opacity", 0.875, RunningOn.Compositor,
"anim2 at 900ms");
done_div();
@ -2248,7 +2247,7 @@ addAsyncAnimTest(function *() {
"anim2 at 300ms");
gDiv.style.animation = "anim2 1s steps(8, end), anim3 500ms steps(4, end)";
yield waitForPaintsFlushed();
omta_todo_is("opacity", 0, RunningOn.Compositor,
omta_is("opacity", 0, RunningOn.Compositor,
"anim3 overriding anim2 at 300ms");
advance_clock(475);
omta_is("opacity", 0.75, RunningOn.Compositor,
@ -2260,9 +2259,15 @@ addAsyncAnimTest(function *() {
// compositor via an UpdateOpacityLayer hint, which does the resending
// via painting.
yield waitForPaints();
// FIXME: Half of this test fails, which means that omta_todo_is fails
// just as much as omta_is. (The value is wrong on both compositor and
// main threads.)
omta_is("opacity", 0.75, RunningOn.Compositor,
"anim2 at 825ms");
advance_clock(75);
// FIXME: Half of this test fails, which means that omta_todo_is fails
// just as much as omta_is. (The value is wrong on both compositor and
// main threads.)
omta_is("opacity", 0.875, RunningOn.Compositor,
"anim2 at 900ms");
done_div();