Bug 1181011 - Don't use cached rule node structs for animations within pseudo-elements. r=dbaron a=abillings

This commit is contained in:
Cameron McCormack 2015-07-27 16:43:44 +10:00
parent c8f4c9b8ba
commit d8b0ee0385
5 changed files with 81 additions and 10 deletions

View File

@ -231,6 +231,7 @@ CommonAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
nsCSSPseudoElements::ePseudo_NotPseudoElement);
if (rule) {
aData->mRuleWalker->Forward(rule);
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
}
}
@ -250,6 +251,7 @@ CommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
if (rule) {
aData->mRuleWalker->Forward(rule);
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
}
}
@ -486,6 +488,15 @@ AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
// Don't apply transitions or animations to things inside of
// pseudo-elements.
// FIXME (Bug 522599): Add tests for this.
// Prevent structs from being cached on the rule node since we're inside
// a pseudo-element, as we could determine cacheability differently
// when walking the rule tree for a style context that is not inside
// a pseudo-element. Note that nsRuleNode::GetStyle##name_ and GetStyleData
// will never look at cached structs when we're animating things inside
// a pseduo-element, so that we don't incorrectly return a struct that
// is only appropriate for non-pseudo-elements.
aRuleData->mConditions.SetUncacheable();
return;
}

View File

@ -1475,7 +1475,8 @@ nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent,
mNextSibling(nullptr),
mDependentBits((uint32_t(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) |
(aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)),
mNoneBits(0),
mNoneBits(aParent ? aParent->mNoneBits & NS_RULE_NODE_HAS_ANIMATION_DATA :
0),
mRefCnt(0)
{
MOZ_ASSERT(aContext);
@ -9321,9 +9322,14 @@ nsRuleNode::GetStyleData(nsStyleStructID aSID,
"in some way.");
const void *data;
data = mStyleData.GetStyleData(aSID, aContext);
if (MOZ_LIKELY(data != nullptr))
return data; // We have a fully specified struct. Just return it.
// Never use cached data for animated style inside a pseudo-element;
// see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto.
if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) {
data = mStyleData.GetStyleData(aSID, aContext);
if (MOZ_LIKELY(data != nullptr))
return data; // We have a fully specified struct. Just return it.
}
if (MOZ_UNLIKELY(!aComputeData))
return nullptr;
@ -9779,3 +9785,10 @@ nsRuleNode::ComputeColor(const nsCSSValue& aValue, nsPresContext* aPresContext,
MOZ_ASSERT(ok || !(aPresContext && aStyleContext));
return ok;
}
/* static */ bool
nsRuleNode::ParentHasPseudoElementData(nsStyleContext* aContext)
{
nsStyleContext* parent = aContext->GetParent();
return parent && parent->HasPseudoElementData();
}

View File

@ -843,6 +843,29 @@ public:
return (mDependentBits & NS_RULE_NODE_USED_DIRECTLY) != 0;
}
/**
* Is the mRule of this rule node an AnimValuesStyleRule?
*/
void SetIsAnimationRule() {
MOZ_ASSERT(!HaveChildren() ||
(mDependentBits & NS_RULE_NODE_IS_ANIMATION_RULE),
"SetIsAnimationRule must only set the IS_ANIMATION_RULE bit "
"before the rule node has children");
mDependentBits |= NS_RULE_NODE_IS_ANIMATION_RULE;
mNoneBits |= NS_RULE_NODE_HAS_ANIMATION_DATA;
}
bool IsAnimationRule() const {
return (mDependentBits & NS_RULE_NODE_IS_ANIMATION_RULE) != 0;
}
/**
* Is the mRule of this rule node or any of its ancestors an
* AnimValuesStyleRule?
*/
bool HasAnimationData() const {
return (mNoneBits & NS_RULE_NODE_HAS_ANIMATION_DATA) != 0;
}
// NOTE: Does not |AddRef|. Null only for the root.
nsIStyleRule* GetRule() const { return mRule; }
// NOTE: Does not |AddRef|. Never null.
@ -866,9 +889,14 @@ public:
"in some way."); \
\
const nsStyle##name_ *data; \
data = mStyleData.GetStyle##name_(); \
if (MOZ_LIKELY(data != nullptr)) \
return data; \
\
/* Never use cached data for animated style inside a pseudo-element; */ \
/* see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto */ \
if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { \
data = mStyleData.GetStyle##name_(); \
if (MOZ_LIKELY(data != nullptr)) \
return data; \
} \
\
if (!aComputeData) \
return nullptr; \
@ -891,9 +919,14 @@ public:
"in some way."); \
\
const nsStyle##name_ *data; \
data = mStyleData.GetStyle##name_(aContext); \
if (MOZ_LIKELY(data != nullptr)) \
return data; \
\
/* Never use cached data for animated style inside a pseudo-element; */ \
/* see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto */ \
if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { \
data = mStyleData.GetStyle##name_(aContext); \
if (MOZ_LIKELY(data != nullptr)) \
return data; \
} \
\
if (!aComputeData) \
return nullptr; \
@ -1017,6 +1050,8 @@ public:
nsPresContext* aPresContext,
nsStyleContext* aStyleContext,
nscolor& aResult);
static bool ParentHasPseudoElementData(nsStyleContext* aContext);
};
#endif

View File

@ -845,6 +845,7 @@ ReplaceAnimationRule(nsRuleNode *aOldRuleNode,
if (aNewAnimRule) {
n = n->Transition(aNewAnimRule, nsStyleSet::eAnimationSheet, false);
n->SetIsAnimationRule();
}
for (uint32_t i = moreSpecificNodes.Length(); i-- != 0; ) {
@ -1439,6 +1440,7 @@ struct RuleNodeInfo {
nsIStyleRule* mRule;
uint8_t mLevel;
bool mIsImportant;
bool mIsAnimationRule;
};
struct CascadeLevel {
@ -1508,6 +1510,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
curRule->mRule = ruleNode->GetRule();
curRule->mLevel = ruleNode->GetLevel();
curRule->mIsImportant = ruleNode->IsImportantRule();
curRule->mIsAnimationRule = ruleNode->IsAnimationRule();
}
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
@ -1538,6 +1541,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
GetAnimationRule(aElement, aPseudoType);
if (rule) {
ruleWalker.ForwardOnPossiblyCSSRule(rule);
ruleWalker.CurrentNode()->SetIsAnimationRule();
}
}
break;
@ -1550,6 +1554,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
GetAnimationRule(aElement, aPseudoType);
if (rule) {
ruleWalker.ForwardOnPossiblyCSSRule(rule);
ruleWalker.CurrentNode()->SetIsAnimationRule();
}
}
break;
@ -1615,6 +1620,9 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
if (!doReplace) {
ruleWalker.ForwardOnPossiblyCSSRule(ruleInfo.mRule);
if (ruleInfo.mIsAnimationRule) {
ruleWalker.CurrentNode()->SetIsAnimationRule();
}
}
}
}

View File

@ -67,12 +67,16 @@ class imgIContainer;
#define NS_STYLE_CONTEXT_TYPE_SHIFT 34
// Additional bits for nsRuleNode's mDependentBits:
#define NS_RULE_NODE_IS_ANIMATION_RULE 0x01000000
#define NS_RULE_NODE_GC_MARK 0x02000000
#define NS_RULE_NODE_USED_DIRECTLY 0x04000000
#define NS_RULE_NODE_IS_IMPORTANT 0x08000000
#define NS_RULE_NODE_LEVEL_MASK 0xf0000000
#define NS_RULE_NODE_LEVEL_SHIFT 28
// Additional bits for nsRuleNode's mNoneBits:
#define NS_RULE_NODE_HAS_ANIMATION_DATA 0x80000000
// The lifetime of these objects is managed by the presshell's arena.
struct nsStyleFont {