Bug 822721: Call CalcStyleDifference and process the style change list resulting from the miniflush we do to update throttled animations prior to seeing if we need to start transitions. r=bzbarsky a=blocking-basecamp

The CalcStyleDifference call is absolutely necessary even if we didn't
need to process the change list, because it causes the new style
context to have cached structs that we might need for a later
comparison.  This is important because, as an optimization, we only
compare structs that have been retrieved.  This optimization requires
that when we replace a style context, we fetch all the structs on the
new style context that had been fetched on the old style context (which
is normally necessary anyway in order to do comparison so we can process
the changes appropriately).

However, actually processing the change list is also necessary to fix
the bug; it's the actual change from the miniflush that matters here.
Based on dholbert's debugging information, I think it's mostly likely
because we were failing to process the UpdateOverflow hint.
This commit is contained in:
L. David Baron 2013-01-08 20:37:29 -08:00
parent efcbedeeba
commit d4f14d0985
2 changed files with 34 additions and 7 deletions

View File

@ -28,6 +28,7 @@
#include "Layers.h"
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"
#include "nsStyleChangeList.h"
using mozilla::TimeStamp;
using mozilla::TimeDuration;
@ -251,7 +252,8 @@ ForceLayerRerendering(nsIFrame* aFrame, CommonElementAnimationData* aData)
nsStyleContext*
nsTransitionManager::UpdateThrottledStyle(dom::Element* aElement,
nsStyleContext* aParentStyle)
nsStyleContext* aParentStyle,
nsStyleChangeList& aChangeList)
{
NS_ASSERTION(GetElementTransitions(aElement,
nsCSSPseudoElements::ePseudo_NotPseudoElement,
@ -283,6 +285,7 @@ nsTransitionManager::UpdateThrottledStyle(dom::Element* aElement,
mPresContext->AnimationManager()->EnsureStyleRuleFor(ea);
curRule.mRule = ea->mStyleRule;
// FIXME: maybe not needed anymore:
ForceLayerRerendering(primaryFrame, ea);
} else if (curRule.mLevel == nsStyleSet::eTransitionSheet) {
ElementTransitions *et =
@ -292,6 +295,7 @@ nsTransitionManager::UpdateThrottledStyle(dom::Element* aElement,
et->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh());
curRule.mRule = et->mStyleRule;
// FIXME: maybe not needed anymore:
ForceLayerRerendering(primaryFrame, et);
} else {
curRule.mRule = ruleNode->GetRule();
@ -304,6 +308,21 @@ nsTransitionManager::UpdateThrottledStyle(dom::Element* aElement,
nsRefPtr<nsStyleContext> newStyle = mPresContext->PresShell()->StyleSet()->
ResolveStyleForRules(aParentStyle, oldStyle, rules);
// We absolutely must call CalcStyleDifference in order to ensure the
// new context has all the structs cached that the old context had.
// We also need it for processing of the changes.
nsChangeHint styleChange =
oldStyle->CalcStyleDifference(newStyle, nsChangeHint(0));
// This isn't particularly dangerous, but I want to catch if it happens:
NS_ABORT_IF_FALSE(NS_IsHintSubset(styleChange,
NS_CombineHint(nsChangeHint_UpdateOpacityLayer,
NS_CombineHint(nsChangeHint_UpdateTransformLayer,
nsChangeHint_UpdateOverflow))),
"unexpected change hint");
aChangeList.AppendChange(primaryFrame, primaryFrame->GetContent(),
styleChange);
primaryFrame->SetStyleContextWithoutNotification(newStyle);
ReparentBeforeAndAfter(aElement, primaryFrame, newStyle, mPresContext->PresShell()->StyleSet());
@ -313,7 +332,8 @@ nsTransitionManager::UpdateThrottledStyle(dom::Element* aElement,
void
nsTransitionManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
nsStyleContext* aParentStyle)
nsStyleContext* aParentStyle,
nsStyleChangeList& aChangeList)
{
dom::Element* element;
if (aContent->IsElement()) {
@ -330,7 +350,7 @@ nsTransitionManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
nsCSSPseudoElements::ePseudo_NotPseudoElement,
false))) {
// re-resolve our style
newStyle = UpdateThrottledStyle(element, aParentStyle);
newStyle = UpdateThrottledStyle(element, aParentStyle, aChangeList);
// remove the current transition from the working set
et->mFlushGeneration = mPresContext->RefreshDriver()->MostRecentRefresh();
} else {
@ -351,7 +371,7 @@ nsTransitionManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
if (newStyle) {
for (nsIContent *child = aContent->GetFirstChild(); child;
child = child->GetNextSibling()) {
UpdateThrottledStylesForSubtree(child, newStyle);
UpdateThrottledStylesForSubtree(child, newStyle, aChangeList);
}
}
}
@ -373,6 +393,8 @@ nsTransitionManager::UpdateAllThrottledStyles()
mPresContext->TickLastUpdateThrottledStyle();
TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
nsStyleChangeList changeList;
// update each transitioning element by finding its root-most ancestor with a
// transition, and flushing the style on that ancestor and all its descendants
PRCList *next = PR_LIST_HEAD(&mElementData);
@ -409,9 +431,12 @@ nsTransitionManager::UpdateAllThrottledStyles()
if (element &&
(primaryFrame = element->GetPrimaryFrame())) {
UpdateThrottledStylesForSubtree(element,
primaryFrame->GetStyleContext()->GetParent());
primaryFrame->GetStyleContext()->GetParent(), changeList);
}
}
mPresContext->PresShell()->FrameConstructor()->
ProcessRestyledFrames(changeList);
}
already_AddRefed<nsIStyleRule>

View File

@ -220,12 +220,14 @@ private:
// If the element has a transition, it is flushed back to its primary frame.
// If the element does not have a transition, then its style is reparented.
void UpdateThrottledStylesForSubtree(nsIContent* aContent,
nsStyleContext* aParentStyle);
nsStyleContext* aParentStyle,
nsStyleChangeList &aChangeList);
// Update the style on aElement from the transition stored in this manager and
// the new parent style - aParentStyle. aElement must be transitioning or
// animated. Returns the updated style.
nsStyleContext* UpdateThrottledStyle(mozilla::dom::Element* aElement,
nsStyleContext* aParentStyle);
nsStyleContext* aParentStyle,
nsStyleChangeList &aChangeList);
};
#endif /* !defined(nsTransitionManager_h_) */