Fix for 99344, inline styles not being recognized under certain DHTML circumstances. r=dbaron, sr=waterson, a=asa

This commit is contained in:
hyatt%netscape.com 2002-04-05 08:05:33 +00:00
parent 4ebecbc88a
commit 08d1853958
12 changed files with 200 additions and 13 deletions

View File

@ -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<nsIPresShell> shell;
mPresContext->GetShell(getter_AddRefs(shell));
nsCOMPtr<nsIStyleSet> styleSet;
shell->GetStyleSet(getter_AddRefs(styleSet));
styleSet->AddRuleNodeMapping(next);
}
*aResult = next;
return NS_OK;
}

View File

@ -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);

View File

@ -2288,7 +2288,7 @@ nsGenericHTMLElement::WalkInlineStyleRules(nsRuleWalker* aRuleWalker)
}
if (rule)
aRuleWalker->Forward(rule);
aRuleWalker->Forward(rule, PR_TRUE);
return result;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -471,7 +471,7 @@ nsSVGElement::WalkInlineStyleRules(nsRuleWalker* aRuleWalker)
nsCOMPtr<nsIStyleRule> rule;
mStyle->GetStyleRule(mDocument, getter_AddRefs(rule));
if (aRuleWalker && rule) {
aRuleWalker->Forward(rule);
aRuleWalker->Forward(rule, PR_TRUE);
}
return NS_OK;
}

View File

@ -3882,7 +3882,7 @@ nsXULElement::WalkInlineStyleRules(nsRuleWalker* aRuleWalker)
}
if (rule)
aRuleWalker->Forward(rule);
aRuleWalker->Forward(rule, PR_TRUE);
return result;
}

View File

@ -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

View File

@ -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<nsIPresShell> shell;
mPresContext->GetShell(getter_AddRefs(shell));
nsCOMPtr<nsIStyleSet> styleSet;
shell->GetStyleSet(getter_AddRefs(styleSet));
styleSet->AddRuleNodeMapping(next);
}
*aResult = next;
return NS_OK;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);