From: L. David Baron Bug 1086937 patch 1 - Add eRestyle_ChangeAnimationPhaseDescendants restyle hint that is like eRestyle_ChangeAnimationPhase, but for a whole subtree. diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -2515,16 +2515,17 @@ ElementRestyler::Restyle(nsRestyleHint a } } // If we are restyling this frame with eRestyle_Self, we restyle // children with nsRestyleHint(0). But we pass the eRestyle_ForceDescendants // flag down too. nsRestyleHint childRestyleHint = nsRestyleHint(aRestyleHint & (eRestyle_Subtree | + eRestyle_ChangeAnimationPhaseDescendants | eRestyle_ForceDescendants)); nsRefPtr oldContext = mFrame->StyleContext(); // TEMPORARY (until bug 918064): Call RestyleSelf for each // continuation or block-in-inline sibling. // We must make a single decision on how to process this frame and @@ -3731,17 +3732,19 @@ RestyleManager::StructsToLog() #ifdef DEBUG /* static */ nsCString RestyleManager::RestyleHintToString(nsRestyleHint aHint) { nsCString result; bool any = false; const char* names[] = { "Self", "Subtree", "LaterSiblings", "CSSTransitions", "CSSAnimations", "SVGAttrAnimations", "StyleAttribute", - "ChangeAnimationPhase", "Force", "ForceDescendants" }; + "ChangeAnimationPhase", + "ChangeAnimationPhaseDescendants", + "Force", "ForceDescendants" }; uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1); uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1); for (uint32_t i = 0; i < ArrayLength(names); i++) { if (hint & (1 << i)) { if (any) { result.AppendLiteral(" | "); } result.AppendPrintf("eRestyle_%s", names[i]); diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -341,26 +341,30 @@ enum nsRestyleHint { // indicates that along with the replacement given, appropriate // switching between the style with animation and style without // animation should be performed by adding or removing rules that // should be present only in the style with animation. // This is implied by eRestyle_Self or eRestyle_Subtree. // FIXME: Remove this as part of bug 960465. eRestyle_ChangeAnimationPhase = (1 << 7), + // Same as the previous, except this applies to the entire subtree. + // FIXME: Remove this as part of bug 960465. + eRestyle_ChangeAnimationPhaseDescendants = (1 << 8), + // Continue the restyling process to the current frame's children even // if this frame's restyling resulted in no style changes. - eRestyle_Force = (1<<8), + eRestyle_Force = (1<<9), // Continue the restyling process to all of the current frame's // descendants, even if any frame's restyling resulted in no style // changes. (Implies eRestyle_Force.) Note that this is weaker than // eRestyle_Subtree, which makes us rerun selector matching on all // descendants rather than just continuing the restyling process. - eRestyle_ForceDescendants = (1<<9), + eRestyle_ForceDescendants = (1<<10), }; // The functions below need an integral type to cast to to avoid // infinite recursion. typedef decltype(nsRestyleHint(0) + nsRestyleHint(0)) nsRestyleHint_size_t; inline nsRestyleHint operator|(nsRestyleHint aLeft, nsRestyleHint aRight) { diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1355,30 +1355,44 @@ nsStyleSet::RuleNodeWithReplacement(Elem nsCSSPseudoElements::Type aPseudoType, nsRestyleHint aReplacements) { NS_ABORT_IF_FALSE(!(aReplacements & ~(eRestyle_CSSTransitions | eRestyle_CSSAnimations | eRestyle_SVGAttrAnimations | eRestyle_StyleAttribute | eRestyle_ChangeAnimationPhase | + eRestyle_ChangeAnimationPhaseDescendants | eRestyle_Force | eRestyle_ForceDescendants)), // FIXME: Once bug 979133 lands we'll have a better // way to print these. nsPrintfCString("unexpected replacement bits 0x%lX", uint32_t(aReplacements)).get()); // If we're changing animation phase, we have to reconsider what rules // are in these four levels. - if (aReplacements & eRestyle_ChangeAnimationPhase) { - aReplacements |= eRestyle_CSSTransitions | - eRestyle_CSSAnimations | - eRestyle_SVGAttrAnimations | - eRestyle_StyleAttribute; + if (aReplacements & (eRestyle_ChangeAnimationPhase | + eRestyle_ChangeAnimationPhaseDescendants)) { + // Animations are only on elements and on :before and :after + // pseudo-elements. Style attributes are only on elements and on + // pseudo-elements other than :before and :after, and we assert + // below for pseudo-elements, so we need to avoid adding + // eRestyle_StyleAttribute for pseudo-elements. + if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { + aReplacements |= eRestyle_CSSTransitions | + eRestyle_CSSAnimations | + eRestyle_SVGAttrAnimations | + eRestyle_StyleAttribute; + } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before || + aPseudoType == nsCSSPseudoElements::ePseudo_after) { + aReplacements |= eRestyle_CSSTransitions | + eRestyle_CSSAnimations | + eRestyle_SVGAttrAnimations; + } } // FIXME (perf): This should probably not rebuild the whole path, but // only the path from the last change in the rule tree, like // ReplaceAnimationRule in nsStyleSet.cpp does. (That could then // perhaps share this code, too?) // But if we do that, we'll need to pass whether we are rebuilding the // rule tree from ElementRestyler::RestyleSelf to avoid taking that @@ -1430,30 +1444,32 @@ nsStyleSet::RuleNodeWithReplacement(Elem aPseudoType == nsCSSPseudoElements::ePseudo_before || aPseudoType == nsCSSPseudoElements::ePseudo_after) { PresContext()->TransitionManager()-> WalkTransitionRule(aElement, aPseudoType, &ruleWalker); } break; } case eRestyle_SVGAttrAnimations: { - MOZ_ASSERT(aReplacements & eRestyle_ChangeAnimationPhase, + MOZ_ASSERT(aReplacements & (eRestyle_ChangeAnimationPhase | + eRestyle_ChangeAnimationPhaseDescendants), "don't know how to do this level without phase change"); SVGAttrAnimationRuleProcessor* ruleProcessor = static_cast( mRuleProcessors[eSVGAttrAnimationSheet].get()); if (ruleProcessor && aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { ruleProcessor->ElementRulesMatching(aElement, &ruleWalker); } break; } case eRestyle_StyleAttribute: { - MOZ_ASSERT(aReplacements & eRestyle_ChangeAnimationPhase, + MOZ_ASSERT(aReplacements & (eRestyle_ChangeAnimationPhase | + eRestyle_ChangeAnimationPhaseDescendants), "don't know how to do this level without phase change"); if (!level->mIsImportant) { // First time through, we handle the non-!important rule. MOZ_ASSERT(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement, "this code doesn't know how to replace " "pseudo-element rules");