Bug 1073336 part 14b - Make ElementRestyler detect changes to the animation generation; r=dbaron

For some kinds of changes we need to update the layer tree even though there is
no change to style. For example, if an animation is paused via the Web
Animations API, we need to remove the animation from the layer even though the
style will not change.

This patch detects such changes by making ElementRestyler check for an
out-of-date animation generation on layers. This is complicated by the fact that
we currently maintain *two* animation generation numbers: one for the set of
animations and one for the set of transitions, but we only have *one* animation
generation number on each layer. This is a known issue (bug 847286).

As a result, until bug 847286 is fixed, we need to be careful to compare against
the greater of the two numbers.
This commit is contained in:
Brian Birtles 2014-11-17 13:46:00 +09:00
Родитель dc417c2fec
Коммит d5678b7ad6
3 изменённых файлов: 90 добавлений и 2 удалений

Просмотреть файл

@ -8,9 +8,11 @@
* changes need to happen, scheduling them, and doing them.
*/
#include <algorithm> // For std::max
#include "RestyleManager.h"
#include "mozilla/EventStates.h"
#include "nsLayoutUtils.h"
#include "FrameLayerBuilder.h"
#include "GeckoProfiler.h"
#include "nsStyleChangeList.h"
#include "nsRuleProcessorData.h"
@ -1149,6 +1151,27 @@ RestyleManager::AttributeChanged(Element* aElement,
PostRestyleEvent(aElement, rshint, hint);
}
/* static */ uint64_t
RestyleManager::GetMaxAnimationGenerationForFrame(nsIFrame* aFrame)
{
nsIContent* content = aFrame->GetContent();
if (!content || !content->IsElement()) {
return 0;
}
nsCSSPseudoElements::Type pseudoType =
aFrame->StyleContext()->GetPseudoType();
AnimationPlayerCollection* transitions =
aFrame->PresContext()->TransitionManager()->GetAnimationPlayers(
content->AsElement(), pseudoType, false /* don't create */);
AnimationPlayerCollection* animations =
aFrame->PresContext()->AnimationManager()->GetAnimationPlayers(
content->AsElement(), pseudoType, false /* don't create */);
return std::max(transitions ? transitions->mAnimationGeneration : 0,
animations ? animations->mAnimationGeneration : 0);
}
void
RestyleManager::RestyleForEmptyChange(Element* aContainer)
{
@ -2408,6 +2431,47 @@ ElementRestyler::ElementRestyler(ParentContextFromChildFrame,
{
}
void
ElementRestyler::AddLayerChangesForAnimation()
{
static const nsDisplayItem::Type sLayerTypes[] =
{ nsDisplayItem::TYPE_TRANSFORM,
nsDisplayItem::TYPE_OPACITY };
static const nsChangeHint sHints[] = { nsChangeHint_UpdateTransformLayer,
nsChangeHint_UpdateOpacityLayer };
static_assert(MOZ_ARRAY_LENGTH(sLayerTypes) == MOZ_ARRAY_LENGTH(sHints),
"Parallel layer type and hint arrays should have same length");
// Bug 847286 - We should have separate animation generation counters
// on layers for transitions and animations and use != comparison below
// rather than a > comparison.
uint64_t frameGeneration =
RestyleManager::GetMaxAnimationGenerationForFrame(mFrame);
nsChangeHint hint = nsChangeHint(0);
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sLayerTypes); i++) {
Layer* layer =
FrameLayerBuilder::GetDedicatedLayer(mFrame, sLayerTypes[i]);
if (layer && frameGeneration > layer->GetAnimationGeneration()) {
// If we have a transform layer but don't have any transform style, we
// probably just removed the transform but haven't destroyed the layer
// yet. In this case we will add the appropriate change hint
// (nsChangeHint_AddOrRemoveTransform) when we compare style contexts
// so we can skip adding any change hint here. (If we *were* to add
// nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would
// complain that we're updating a transform layer without a transform).
if (sLayerTypes[i] == nsDisplayItem::TYPE_TRANSFORM &&
!mFrame->StyleDisplay()->HasTransformStyle()) {
continue;
}
NS_UpdateHint(hint, sHints[i]);
}
}
if (hint) {
mChangeList->AppendChange(mFrame, mContent, hint);
}
}
void
ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
nsStyleContext* aNewContext,
@ -2522,6 +2586,12 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
}
}
// Some changes to animations don't affect the computed style and yet still
// require the layer to be updated. For example, pausing an animation via
// the Web Animations API won't affect an element's style but still
// requires us to pull the animation off the layer.
AddLayerChangesForAnimation();
// If we are restyling this frame with eRestyle_Self or weaker hints,
// we restyle children with nsRestyleHint(0). But we pass the
// eRestyle_ChangeAnimationPhaseDescendants and eRestyle_ForceDescendants

Просмотреть файл

@ -92,6 +92,11 @@ public:
// track whether off-main-thread animations are up-to-date.
uint64_t GetAnimationGeneration() const { return mAnimationGeneration; }
// A workaround until bug 847286 lands that gets the maximum of the animation
// generation counters stored on the set of animations and transitions
// respectively for |aFrame|.
static uint64_t GetMaxAnimationGenerationForFrame(nsIFrame* aFrame);
// Update the animation generation count to mark that animation state
// has changed.
//
@ -601,6 +606,11 @@ private:
*/
void RestyleChildren(nsRestyleHint aChildRestyleHint);
/**
* Helpers for Restyle().
*/
void AddLayerChangesForAnimation();
/**
* Helpers for RestyleSelf().
*/

Просмотреть файл

@ -62,6 +62,7 @@
#include "UnitTransforms.h"
#include "LayersLogging.h"
#include "FrameLayerBuilder.h"
#include "RestyleManager.h"
#include "nsCaret.h"
#include "nsISelection.h"
@ -440,6 +441,15 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
aLayer->ClearAnimations();
}
// Update the animation generation on the layer. We need to do this before
// any early returns since even if we don't add any animations to the
// layer, we still need to mark it as up-to-date with regards to animations.
// Otherwise, in RestyleManager we'll notice the discrepancy between the
// animation generation numbers and update the layer indefinitely.
uint64_t animationGeneration =
RestyleManager::GetMaxAnimationGenerationForFrame(aFrame);
aLayer->SetAnimationGeneration(animationGeneration);
nsIContent* content = aFrame->GetContent();
if (!content) {
return;
@ -507,13 +517,11 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
if (transitions) {
AddAnimationsForProperty(aFrame, aProperty, transitions->mPlayers,
aLayer, data, pending);
aLayer->SetAnimationGeneration(transitions->mAnimationGeneration);
}
if (animations) {
AddAnimationsForProperty(aFrame, aProperty, animations->mPlayers,
aLayer, data, pending);
aLayer->SetAnimationGeneration(animations->mAnimationGeneration);
}
}