diff --git a/mozilla/content/base/src/nsRuleNode.cpp b/mozilla/content/base/src/nsRuleNode.cpp index f9025e2cae0..53e0c6c74f2 100644 --- a/mozilla/content/base/src/nsRuleNode.cpp +++ b/mozilla/content/base/src/nsRuleNode.cpp @@ -458,7 +458,7 @@ nsRuleNode::GetBits(PRInt32 aType, PRUint32* aResult) } nsresult -nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) +nsRuleNode::Transition(nsIStyleRule* aRule, PRBool aIsInlineStyle, nsRuleNode** aResult) { nsRuleNode* next = nsnull; @@ -475,6 +475,7 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) ConvertChildrenToHash(); } + PRBool createdNode = PR_FALSE; if (ChildrenAreHashed()) { ChildrenHashEntry *entry = NS_STATIC_CAST(ChildrenHashEntry*, PL_DHashTableOperate(ChildrenHash(), aRule, PL_DHASH_ADD)); @@ -488,6 +489,7 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) *aResult = nsnull; return NS_ERROR_OUT_OF_MEMORY; } + createdNode = PR_TRUE; } } else if (!next) { // Create the new entry in our list. @@ -497,8 +499,23 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) return NS_ERROR_OUT_OF_MEMORY; } SetChildrenList(new (mPresContext) nsRuleList(next, ChildrenList())); + createdNode = PR_TRUE; } + if (aIsInlineStyle && createdNode) { + // We just made a new rule node for an inline style rule (e.g., for + // the style attribute on an HTML, SVG, or XUL element). In order to + // fix bug 99344, we have to maintain a mapping from inline style rules + // to all the rule nodes in a rule tree that correspond to the inline + // style rule. + // Obtain our style set and then add this rule node to our mapping. + nsCOMPtr shell; + mPresContext->GetShell(getter_AddRefs(shell)); + nsCOMPtr styleSet; + shell->GetStyleSet(getter_AddRefs(styleSet)); + styleSet->AddRuleNodeMapping(next); + } + *aResult = next; return NS_OK; } diff --git a/mozilla/content/base/src/nsStyleSet.cpp b/mozilla/content/base/src/nsStyleSet.cpp index dce2380f429..8130871b67b 100644 --- a/mozilla/content/base/src/nsStyleSet.cpp +++ b/mozilla/content/base/src/nsStyleSet.cpp @@ -59,6 +59,7 @@ #include "nsIHTMLDocument.h" #include "nsIDOMHTMLBodyElement.h" #include "nsHTMLAtoms.h" +#include "nsHashtable.h" #ifdef MOZ_PERF_METRICS #include "nsITimeRecorder.h" @@ -73,6 +74,34 @@ #include "nsISizeOfHandler.h" +// ===================================================== +// nsRuleNodeList +// A class that represents a chain of rule nodes + +struct nsRuleNodeList +{ + nsRuleNodeList(nsRuleNode* aRuleNode, nsRuleNodeList* aNext = nsnull) + :mRuleNode(aRuleNode), mNext(aNext) + {}; + + void* operator new(size_t sz, nsIPresContext* aContext) { + void* result = nsnull; + aContext->AllocateFromShell(sz, &result); + return result; + }; + + void Destroy() { + if (mNext) + mNext->Destroy(); + mRuleNode->PresContext()->FreeToShell(sizeof(nsRuleNodeList), this); + }; + + nsRuleNode* mRuleNode; + nsRuleNodeList* mNext; +}; + +// ===================================================== + static NS_DEFINE_IID(kIStyleFrameConstructionIID, NS_ISTYLE_FRAME_CONSTRUCTION_IID); class StyleSetImpl : public nsIStyleSet @@ -160,6 +189,8 @@ public: virtual nsresult GetRuleTree(nsRuleNode** aResult); virtual nsresult ClearCachedDataInRuleTree(nsIStyleRule* aRule); + + virtual nsresult AddRuleNodeMapping(nsRuleNode* aRuleNode); virtual nsresult ClearStyleData(nsIPresContext* aPresContext, nsIStyleRule* aRule, nsIStyleContext* aContext); @@ -336,6 +367,7 @@ protected: nsRuleNode* mOldRuleTree; // Used during rule tree reconstruction. nsRuleWalker* mRuleWalker; // This is an instance of a rule walker that can be used // to navigate through our tree. + nsHashtable mRuleMappings; // A hashtable from rules to rule node lists. MOZ_TIMER_DECLARE(mStyleResolutionWatch) @@ -353,7 +385,8 @@ StyleSetImpl::StyleSetImpl() mQuirkStyleSheet(nsnull), mRuleTree(nsnull), mOldRuleTree(nsnull), - mRuleWalker(nsnull) + mRuleWalker(nsnull), + mRuleMappings(32) #ifdef MOZ_PERF_METRICS ,mTimerEnabled(PR_FALSE) #endif @@ -1218,9 +1251,19 @@ nsIStyleContext* StyleSetImpl::ProbePseudoStyleFor(nsIPresContext* aPresContext, return result; } +PRBool PR_CALLBACK DeleteRuleNodeLists(nsHashKey* aKey, void* aData, void* aClosure) +{ + nsRuleNodeList* ruleNodeList = (nsRuleNodeList*)aData; + ruleNodeList->Destroy(); + return PR_TRUE; +} + NS_IMETHODIMP StyleSetImpl::Shutdown() { + mRuleMappings.Enumerate(DeleteRuleNodeLists); + mRuleMappings.Reset(); + delete mRuleWalker; if (mRuleTree) { @@ -1237,6 +1280,17 @@ StyleSetImpl::GetRuleTree(nsRuleNode** aResult) return NS_OK; } +nsresult +StyleSetImpl::AddRuleNodeMapping(nsRuleNode* aRuleNode) +{ + nsVoidKey key(aRuleNode->Rule()); + nsRuleNodeList* ruleList = + new (aRuleNode->PresContext()) nsRuleNodeList(aRuleNode, + NS_STATIC_CAST(nsRuleNodeList*, mRuleMappings.Get(&key))); + mRuleMappings.Put(&key, ruleList); + return NS_OK; +} + nsresult StyleSetImpl::BeginRuleTreeReconstruct() { @@ -1244,6 +1298,8 @@ StyleSetImpl::BeginRuleTreeReconstruct() mRuleWalker = nsnull; mOldRuleTree = mRuleTree; mRuleTree = nsnull; + mRuleMappings.Enumerate(DeleteRuleNodeLists); + mRuleMappings.Reset(); return NS_OK; } @@ -1274,6 +1330,20 @@ StyleSetImpl::ClearStyleData(nsIPresContext* aPresContext, nsIStyleRule* aRule, // style.left, we really only need to blow away cached // data in the position struct. if (aContext) { + // |aRule| should never be null, but we'll sanity check it just in case. + if (aRule) { + // Obtain our rule node list and clear out the cached data in all rule nodes + // that correspond to this rule. See bug 99344 for more details as to how + // inline style rules can end up with multiple rule nodes in a rule tree. + nsVoidKey key(aRule); + nsRuleNodeList* ruleList = NS_STATIC_CAST(nsRuleNodeList*, mRuleMappings.Get(&key)); + for ( ; ruleList; ruleList = ruleList->mNext) + ruleList->mRuleNode->ClearCachedData(aRule); + } + + // XXXdwh I'm just being paranoid here. Also clear out the data starting at the style + // context's rule node. This really should always be done in the for loop above, + // but I'm going to leave this here just in case (for now). nsRuleNode* ruleNode; aContext->GetRuleNode(&ruleNode); ruleNode->ClearCachedData(aRule); diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp index edc648048d5..5deee4c1c62 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp @@ -2288,7 +2288,7 @@ nsGenericHTMLElement::WalkInlineStyleRules(nsRuleWalker* aRuleWalker) } if (rule) - aRuleWalker->Forward(rule); + aRuleWalker->Forward(rule, PR_TRUE); return result; } diff --git a/mozilla/content/shared/public/nsRuleNode.h b/mozilla/content/shared/public/nsRuleNode.h index fd879c6673f..d0233b7b336 100644 --- a/mozilla/content/shared/public/nsRuleNode.h +++ b/mozilla/content/shared/public/nsRuleNode.h @@ -579,7 +579,7 @@ public: static void CreateRootNode(nsIPresContext* aPresContext, nsRuleNode** aResult); nsresult GetBits(PRInt32 aType, PRUint32* aResult); - nsresult Transition(nsIStyleRule* aRule, nsRuleNode** aResult); + nsresult Transition(nsIStyleRule* aRule, PRBool aIsInlineStyle, nsRuleNode** aResult); nsRuleNode* GetParent() { return mParent; } PRBool IsRoot() { return mParent == nsnull; } nsresult GetRule(nsIStyleRule** aResult) @@ -592,6 +592,10 @@ public: // NOTE: Does not |AddRef|. return mRule; } + nsIPresContext* PresContext() { + // NOTE: Does not |AddRef|. + return mPresContext; + } nsresult ClearCachedData(nsIStyleRule* aRule); nsresult ClearCachedDataInSubtree(nsIStyleRule* aRule); diff --git a/mozilla/content/shared/public/nsRuleWalker.h b/mozilla/content/shared/public/nsRuleWalker.h index 71fd2e09e33..0d73ab21ec3 100644 --- a/mozilla/content/shared/public/nsRuleWalker.h +++ b/mozilla/content/shared/public/nsRuleWalker.h @@ -44,9 +44,9 @@ public: nsRuleNode* GetCurrentNode() { return mCurrent; } void SetCurrentNode(nsRuleNode* aNode) { mCurrent = aNode; } - void Forward(nsIStyleRule* aRule) { + void Forward(nsIStyleRule* aRule, PRBool aIsInlineStyle = PR_FALSE) { nsRuleNode* next; - mCurrent->Transition(aRule, &next); + mCurrent->Transition(aRule, aIsInlineStyle, &next); mCurrent = next; } diff --git a/mozilla/content/svg/content/src/nsSVGElement.cpp b/mozilla/content/svg/content/src/nsSVGElement.cpp index 683966b3e37..01415bd3869 100644 --- a/mozilla/content/svg/content/src/nsSVGElement.cpp +++ b/mozilla/content/svg/content/src/nsSVGElement.cpp @@ -471,7 +471,7 @@ nsSVGElement::WalkInlineStyleRules(nsRuleWalker* aRuleWalker) nsCOMPtr rule; mStyle->GetStyleRule(mDocument, getter_AddRefs(rule)); if (aRuleWalker && rule) { - aRuleWalker->Forward(rule); + aRuleWalker->Forward(rule, PR_TRUE); } return NS_OK; } diff --git a/mozilla/content/xul/content/src/nsXULElement.cpp b/mozilla/content/xul/content/src/nsXULElement.cpp index 468fbdbd2f0..a43f0880fb7 100644 --- a/mozilla/content/xul/content/src/nsXULElement.cpp +++ b/mozilla/content/xul/content/src/nsXULElement.cpp @@ -3882,7 +3882,7 @@ nsXULElement::WalkInlineStyleRules(nsRuleWalker* aRuleWalker) } if (rule) - aRuleWalker->Forward(rule); + aRuleWalker->Forward(rule, PR_TRUE); return result; } diff --git a/mozilla/layout/base/public/nsIStyleSet.h b/mozilla/layout/base/public/nsIStyleSet.h index 635f069adcc..837c3b8867a 100644 --- a/mozilla/layout/base/public/nsIStyleSet.h +++ b/mozilla/layout/base/public/nsIStyleSet.h @@ -109,6 +109,11 @@ public: virtual nsresult GetRuleTree(nsRuleNode** aResult) = 0; virtual nsresult ClearCachedDataInRuleTree(nsIStyleRule* aRule) = 0; + + // This method is used to add a mapping from rule to rule node so that all the rule nodes + // in use for a given rule can be accessed efficiently. This is currently only used + // for inline style rules in order to conserve footprint. + virtual nsresult AddRuleNodeMapping(nsRuleNode* aRuleNode) = 0; // The following two methods can be used to tear down and reconstruct a rule tree. The idea // is to first call BeginRuleTreeReconstruct, which will set aside the old rule diff --git a/mozilla/layout/style/nsRuleNode.cpp b/mozilla/layout/style/nsRuleNode.cpp index f9025e2cae0..53e0c6c74f2 100644 --- a/mozilla/layout/style/nsRuleNode.cpp +++ b/mozilla/layout/style/nsRuleNode.cpp @@ -458,7 +458,7 @@ nsRuleNode::GetBits(PRInt32 aType, PRUint32* aResult) } nsresult -nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) +nsRuleNode::Transition(nsIStyleRule* aRule, PRBool aIsInlineStyle, nsRuleNode** aResult) { nsRuleNode* next = nsnull; @@ -475,6 +475,7 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) ConvertChildrenToHash(); } + PRBool createdNode = PR_FALSE; if (ChildrenAreHashed()) { ChildrenHashEntry *entry = NS_STATIC_CAST(ChildrenHashEntry*, PL_DHashTableOperate(ChildrenHash(), aRule, PL_DHASH_ADD)); @@ -488,6 +489,7 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) *aResult = nsnull; return NS_ERROR_OUT_OF_MEMORY; } + createdNode = PR_TRUE; } } else if (!next) { // Create the new entry in our list. @@ -497,8 +499,23 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) return NS_ERROR_OUT_OF_MEMORY; } SetChildrenList(new (mPresContext) nsRuleList(next, ChildrenList())); + createdNode = PR_TRUE; } + if (aIsInlineStyle && createdNode) { + // We just made a new rule node for an inline style rule (e.g., for + // the style attribute on an HTML, SVG, or XUL element). In order to + // fix bug 99344, we have to maintain a mapping from inline style rules + // to all the rule nodes in a rule tree that correspond to the inline + // style rule. + // Obtain our style set and then add this rule node to our mapping. + nsCOMPtr shell; + mPresContext->GetShell(getter_AddRefs(shell)); + nsCOMPtr styleSet; + shell->GetStyleSet(getter_AddRefs(styleSet)); + styleSet->AddRuleNodeMapping(next); + } + *aResult = next; return NS_OK; } diff --git a/mozilla/layout/style/nsRuleNode.h b/mozilla/layout/style/nsRuleNode.h index fd879c6673f..d0233b7b336 100644 --- a/mozilla/layout/style/nsRuleNode.h +++ b/mozilla/layout/style/nsRuleNode.h @@ -579,7 +579,7 @@ public: static void CreateRootNode(nsIPresContext* aPresContext, nsRuleNode** aResult); nsresult GetBits(PRInt32 aType, PRUint32* aResult); - nsresult Transition(nsIStyleRule* aRule, nsRuleNode** aResult); + nsresult Transition(nsIStyleRule* aRule, PRBool aIsInlineStyle, nsRuleNode** aResult); nsRuleNode* GetParent() { return mParent; } PRBool IsRoot() { return mParent == nsnull; } nsresult GetRule(nsIStyleRule** aResult) @@ -592,6 +592,10 @@ public: // NOTE: Does not |AddRef|. return mRule; } + nsIPresContext* PresContext() { + // NOTE: Does not |AddRef|. + return mPresContext; + } nsresult ClearCachedData(nsIStyleRule* aRule); nsresult ClearCachedDataInSubtree(nsIStyleRule* aRule); diff --git a/mozilla/layout/style/nsRuleWalker.h b/mozilla/layout/style/nsRuleWalker.h index 71fd2e09e33..0d73ab21ec3 100644 --- a/mozilla/layout/style/nsRuleWalker.h +++ b/mozilla/layout/style/nsRuleWalker.h @@ -44,9 +44,9 @@ public: nsRuleNode* GetCurrentNode() { return mCurrent; } void SetCurrentNode(nsRuleNode* aNode) { mCurrent = aNode; } - void Forward(nsIStyleRule* aRule) { + void Forward(nsIStyleRule* aRule, PRBool aIsInlineStyle = PR_FALSE) { nsRuleNode* next; - mCurrent->Transition(aRule, &next); + mCurrent->Transition(aRule, aIsInlineStyle, &next); mCurrent = next; } diff --git a/mozilla/layout/style/nsStyleSet.cpp b/mozilla/layout/style/nsStyleSet.cpp index dce2380f429..8130871b67b 100644 --- a/mozilla/layout/style/nsStyleSet.cpp +++ b/mozilla/layout/style/nsStyleSet.cpp @@ -59,6 +59,7 @@ #include "nsIHTMLDocument.h" #include "nsIDOMHTMLBodyElement.h" #include "nsHTMLAtoms.h" +#include "nsHashtable.h" #ifdef MOZ_PERF_METRICS #include "nsITimeRecorder.h" @@ -73,6 +74,34 @@ #include "nsISizeOfHandler.h" +// ===================================================== +// nsRuleNodeList +// A class that represents a chain of rule nodes + +struct nsRuleNodeList +{ + nsRuleNodeList(nsRuleNode* aRuleNode, nsRuleNodeList* aNext = nsnull) + :mRuleNode(aRuleNode), mNext(aNext) + {}; + + void* operator new(size_t sz, nsIPresContext* aContext) { + void* result = nsnull; + aContext->AllocateFromShell(sz, &result); + return result; + }; + + void Destroy() { + if (mNext) + mNext->Destroy(); + mRuleNode->PresContext()->FreeToShell(sizeof(nsRuleNodeList), this); + }; + + nsRuleNode* mRuleNode; + nsRuleNodeList* mNext; +}; + +// ===================================================== + static NS_DEFINE_IID(kIStyleFrameConstructionIID, NS_ISTYLE_FRAME_CONSTRUCTION_IID); class StyleSetImpl : public nsIStyleSet @@ -160,6 +189,8 @@ public: virtual nsresult GetRuleTree(nsRuleNode** aResult); virtual nsresult ClearCachedDataInRuleTree(nsIStyleRule* aRule); + + virtual nsresult AddRuleNodeMapping(nsRuleNode* aRuleNode); virtual nsresult ClearStyleData(nsIPresContext* aPresContext, nsIStyleRule* aRule, nsIStyleContext* aContext); @@ -336,6 +367,7 @@ protected: nsRuleNode* mOldRuleTree; // Used during rule tree reconstruction. nsRuleWalker* mRuleWalker; // This is an instance of a rule walker that can be used // to navigate through our tree. + nsHashtable mRuleMappings; // A hashtable from rules to rule node lists. MOZ_TIMER_DECLARE(mStyleResolutionWatch) @@ -353,7 +385,8 @@ StyleSetImpl::StyleSetImpl() mQuirkStyleSheet(nsnull), mRuleTree(nsnull), mOldRuleTree(nsnull), - mRuleWalker(nsnull) + mRuleWalker(nsnull), + mRuleMappings(32) #ifdef MOZ_PERF_METRICS ,mTimerEnabled(PR_FALSE) #endif @@ -1218,9 +1251,19 @@ nsIStyleContext* StyleSetImpl::ProbePseudoStyleFor(nsIPresContext* aPresContext, return result; } +PRBool PR_CALLBACK DeleteRuleNodeLists(nsHashKey* aKey, void* aData, void* aClosure) +{ + nsRuleNodeList* ruleNodeList = (nsRuleNodeList*)aData; + ruleNodeList->Destroy(); + return PR_TRUE; +} + NS_IMETHODIMP StyleSetImpl::Shutdown() { + mRuleMappings.Enumerate(DeleteRuleNodeLists); + mRuleMappings.Reset(); + delete mRuleWalker; if (mRuleTree) { @@ -1237,6 +1280,17 @@ StyleSetImpl::GetRuleTree(nsRuleNode** aResult) return NS_OK; } +nsresult +StyleSetImpl::AddRuleNodeMapping(nsRuleNode* aRuleNode) +{ + nsVoidKey key(aRuleNode->Rule()); + nsRuleNodeList* ruleList = + new (aRuleNode->PresContext()) nsRuleNodeList(aRuleNode, + NS_STATIC_CAST(nsRuleNodeList*, mRuleMappings.Get(&key))); + mRuleMappings.Put(&key, ruleList); + return NS_OK; +} + nsresult StyleSetImpl::BeginRuleTreeReconstruct() { @@ -1244,6 +1298,8 @@ StyleSetImpl::BeginRuleTreeReconstruct() mRuleWalker = nsnull; mOldRuleTree = mRuleTree; mRuleTree = nsnull; + mRuleMappings.Enumerate(DeleteRuleNodeLists); + mRuleMappings.Reset(); return NS_OK; } @@ -1274,6 +1330,20 @@ StyleSetImpl::ClearStyleData(nsIPresContext* aPresContext, nsIStyleRule* aRule, // style.left, we really only need to blow away cached // data in the position struct. if (aContext) { + // |aRule| should never be null, but we'll sanity check it just in case. + if (aRule) { + // Obtain our rule node list and clear out the cached data in all rule nodes + // that correspond to this rule. See bug 99344 for more details as to how + // inline style rules can end up with multiple rule nodes in a rule tree. + nsVoidKey key(aRule); + nsRuleNodeList* ruleList = NS_STATIC_CAST(nsRuleNodeList*, mRuleMappings.Get(&key)); + for ( ; ruleList; ruleList = ruleList->mNext) + ruleList->mRuleNode->ClearCachedData(aRule); + } + + // XXXdwh I'm just being paranoid here. Also clear out the data starting at the style + // context's rule node. This really should always be done in the for loop above, + // but I'm going to leave this here just in case (for now). nsRuleNode* ruleNode; aContext->GetRuleNode(&ruleNode); ruleNode->ClearCachedData(aRule);