diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 50fe5cfd667..dc0fe5928a5 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -111,27 +111,41 @@ public: */ struct ChildrenHashEntry : public PLDHashEntryHdr { - // key (the rule) is |mRuleNode->GetRule()| + // key is |mRuleNode->GetKey()| nsRuleNode *mRuleNode; }; -PR_STATIC_CALLBACK(PRBool) -ChildrenHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, - const void *key) +/* static */ PR_CALLBACK PLDHashNumber +nsRuleNode::ChildrenHashHashKey(PLDHashTable *aTable, const void *aKey) { - const ChildrenHashEntry *entry = - NS_STATIC_CAST(const ChildrenHashEntry*, hdr); - return entry->mRuleNode->GetRule() == key; + const nsRuleNode::Key *key = + NS_STATIC_CAST(const nsRuleNode::Key*, aKey); + // Disagreement on importance and level for the same rule is extremely + // rare, so hash just on the rule. + return PL_DHashVoidPtrKeyStub(aTable, key->mRule); } -static PLDHashTableOps ChildrenHashOps = { +/* static */ PR_CALLBACK PRBool +nsRuleNode::ChildrenHashMatchEntry(PLDHashTable *aTable, + const PLDHashEntryHdr *aHdr, + const void *aKey) +{ + const ChildrenHashEntry *entry = + NS_STATIC_CAST(const ChildrenHashEntry*, aHdr); + const nsRuleNode::Key *key = + NS_STATIC_CAST(const nsRuleNode::Key*, aKey); + return entry->mRuleNode->GetKey() == *key; +} + +/* static */ PLDHashTableOps +nsRuleNode::ChildrenHashOps = { // It's probably better to allocate the table itself using malloc and // free rather than the pres shell's arena because the table doesn't // grow very often and the pres shell's arena doesn't recycle very // large size allocations. PL_DHashAllocTable, PL_DHashFreeTable, - PL_DHashVoidPtrKeyStub, + ChildrenHashHashKey, ChildrenHashMatchEntry, PL_DHashMoveEntryStub, PL_DHashClearEntryStub, @@ -401,21 +415,28 @@ nsRuleNode::Destroy() nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext) { - return new (aPresContext) nsRuleNode(aPresContext, nsnull, nsnull); + return new (aPresContext) + nsRuleNode(aPresContext, nsnull, nsnull, 0xff, PR_FALSE); } nsILanguageAtomService* nsRuleNode::gLangService = nsnull; -nsRuleNode::nsRuleNode(nsPresContext* aContext, nsIStyleRule* aRule, nsRuleNode* aParent) +nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, + nsIStyleRule* aRule, PRUint8 aLevel, + PRBool aIsImportant) : mPresContext(aContext), mParent(aParent), mRule(aRule), mChildrenTaggedPtr(nsnull), - mDependentBits(0), + mDependentBits((PRUint32(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) | + (aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)), mNoneBits(0) { MOZ_COUNT_CTOR(nsRuleNode); NS_IF_ADDREF(mRule); + + NS_ASSERTION(IsRoot() || GetLevel() == aLevel, "not enough bits"); + NS_ASSERTION(IsRoot() || IsImportantRule() == aIsImportant, "yikes"); } PR_STATIC_CALLBACK(PLDHashOperator) @@ -441,15 +462,17 @@ nsRuleNode::~nsRuleNode() NS_IF_RELEASE(mRule); } -nsresult -nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) +nsRuleNode* +nsRuleNode::Transition(nsIStyleRule* aRule, PRUint8 aLevel, + PRPackedBool aIsImportantRule) { nsRuleNode* next = nsnull; + nsRuleNode::Key key(aRule, aLevel, aIsImportantRule); if (HaveChildren() && !ChildrenAreHashed()) { PRInt32 numKids = 0; nsRuleList* curr = ChildrenList(); - while (curr && curr->mRuleNode->mRule != aRule) { + while (curr && curr->mRuleNode->GetKey() != key) { curr = curr->mNext; ++numKids; } @@ -461,40 +484,36 @@ nsRuleNode::Transition(nsIStyleRule* aRule, nsRuleNode** aResult) if (ChildrenAreHashed()) { ChildrenHashEntry *entry = NS_STATIC_CAST(ChildrenHashEntry*, - PL_DHashTableOperate(ChildrenHash(), aRule, PL_DHASH_ADD)); + PL_DHashTableOperate(ChildrenHash(), &key, PL_DHASH_ADD)); if (!entry) { - *aResult = nsnull; - return NS_ERROR_OUT_OF_MEMORY; + return nsnull; } if (entry->mRuleNode) next = entry->mRuleNode; else { - next = entry->mRuleNode = - new (mPresContext) nsRuleNode(mPresContext, aRule, this); + next = entry->mRuleNode = new (mPresContext) + nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); if (!next) { PL_DHashTableRawRemove(ChildrenHash(), entry); - *aResult = nsnull; - return NS_ERROR_OUT_OF_MEMORY; + return nsnull; } } } else if (!next) { // Create the new entry in our list. - next = new (mPresContext) nsRuleNode(mPresContext, aRule, this); + next = new (mPresContext) + nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); if (!next) { - *aResult = nsnull; - return NS_ERROR_OUT_OF_MEMORY; + return nsnull; } nsRuleList* newChildrenList = new (mPresContext) nsRuleList(next, ChildrenList()); if (NS_UNLIKELY(!newChildrenList)) { next->Destroy(); - *aResult = nsnull; - return NS_ERROR_OUT_OF_MEMORY; + return nsnull; } SetChildrenList(newChildrenList); } - *aResult = next; - return NS_OK; + return next; } void diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 482d3f11327..cfe744eeffd 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -332,6 +332,42 @@ private: // use for lookups of style properties. nsIStyleRule* mRule; // [STRONG] A pointer to our specific rule. + struct Key { + nsIStyleRule* mRule; + PRUint8 mLevel; + PRPackedBool mIsImportantRule; + + Key(nsIStyleRule* aRule, PRUint8 aLevel, PRPackedBool aIsImportantRule) + : mRule(aRule), mLevel(aLevel), mIsImportantRule(aIsImportantRule) + {} + + PRBool operator==(const Key& aOther) const + { + return mRule == aOther.mRule && + mLevel == aOther.mLevel && + mIsImportantRule == aOther.mIsImportantRule; + } + + PRBool operator!=(const Key& aOther) const + { + return !(*this == aOther); + } + }; + + static PR_CALLBACK PLDHashNumber + ChildrenHashHashKey(PLDHashTable *aTable, const void *aKey); + + static PR_CALLBACK PRBool + ChildrenHashMatchEntry(PLDHashTable *aTable, + const PLDHashEntryHdr *aHdr, + const void *aKey); + + static PLDHashTableOps ChildrenHashOps; + + Key GetKey() const { + return Key(mRule, GetLevel(), IsImportantRule()); + } + // The children of this node are stored in either a hashtable or list // that maps from rules to our nsRuleNode children. When matching // rules, we use this mapping to transition from node to node @@ -629,17 +665,30 @@ protected: #endif private: - nsRuleNode(nsPresContext* aPresContext, nsIStyleRule* aRule, - nsRuleNode* aParent) NS_HIDDEN; + nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent, + nsIStyleRule* aRule, PRUint8 aLevel, PRBool aIsImportant) + NS_HIDDEN; ~nsRuleNode() NS_HIDDEN; public: static NS_HIDDEN_(nsRuleNode*) CreateRootNode(nsPresContext* aPresContext); - NS_HIDDEN_(nsresult) Transition(nsIStyleRule* aRule, nsRuleNode** aResult); + NS_HIDDEN_(nsRuleNode*) Transition(nsIStyleRule* aRule, PRUint8 aLevel, + PRPackedBool aIsImportantRule); nsRuleNode* GetParent() const { return mParent; } PRBool IsRoot() const { return mParent == nsnull; } + // These PRUint8s are really nsStyleSet::sheetType values. + PRUint8 GetLevel() const { + NS_ASSERTION(!IsRoot(), "can't call on root"); + return (mDependentBits & NS_RULE_NODE_LEVEL_MASK) >> + NS_RULE_NODE_LEVEL_SHIFT; + } + PRBool IsImportantRule() const { + NS_ASSERTION(!IsRoot(), "can't call on root"); + return (mDependentBits & NS_RULE_NODE_IS_IMPORTANT) != 0; + } + // NOTE: Does not |AddRef|. nsIStyleRule* GetRule() const { return mRule; } // NOTE: Does not |AddRef|. diff --git a/layout/style/nsRuleWalker.h b/layout/style/nsRuleWalker.h index f443978f65d..17ebd6484ec 100644 --- a/layout/style/nsRuleWalker.h +++ b/layout/style/nsRuleWalker.h @@ -49,23 +49,27 @@ public: void SetCurrentNode(nsRuleNode* aNode) { mCurrent = aNode; } void Forward(nsIStyleRule* aRule) { - nsRuleNode* next; - mCurrent->Transition(aRule, &next); - mCurrent = next; - } - - void Back() { - if (mCurrent != mRoot) - mCurrent = mCurrent->GetParent(); + if (mCurrent) { // check for OOM from previous step + mCurrent = mCurrent->Transition(aRule, mLevel, mImportance); + } } void Reset() { mCurrent = mRoot; } PRBool AtRoot() { return mCurrent == mRoot; } + void SetLevel(PRUint8 aLevel, PRBool aImportance) { + mLevel = aLevel; + mImportance = aImportance; + } + PRUint8 GetLevel() const { return mLevel; } + PRBool GetImportance() const { return mImportance; } + private: nsRuleNode* mCurrent; // Our current position. nsRuleNode* mRoot; // The root of the tree we're walking. + PRUint8 mLevel; // an nsStyleSet::sheetType + PRPackedBool mImportance; public: nsRuleWalker(nsRuleNode* aRoot) :mCurrent(aRoot), mRoot(aRoot) { MOZ_COUNT_CTOR(nsRuleWalker); } diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index b65c5399cc7..8b079646464 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -454,14 +454,17 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, SheetCount(eHTMLPresHintSheet) == 0, "Can't have both types of preshint sheets at once!"); + mRuleWalker->SetLevel(eAgentSheet, PR_FALSE); if (mRuleProcessors[eAgentSheet]) (*aCollectorFunc)(mRuleProcessors[eAgentSheet], aData); nsRuleNode* lastAgentRN = mRuleWalker->GetCurrentNode(); + mRuleWalker->SetLevel(ePresHintSheet, PR_FALSE); if (mRuleProcessors[ePresHintSheet]) (*aCollectorFunc)(mRuleProcessors[ePresHintSheet], aData); nsRuleNode* lastPresHintRN = mRuleWalker->GetCurrentNode(); + mRuleWalker->SetLevel(eUserSheet, PR_FALSE); PRBool skipUserStyles = aData->mContent && aData->mContent == aData->mContent->GetBindingParent(); NS_ASSERTION(!skipUserStyles || aData->mContent->IsNativeAnonymous() || @@ -471,10 +474,12 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, (*aCollectorFunc)(mRuleProcessors[eUserSheet], aData); nsRuleNode* lastUserRN = mRuleWalker->GetCurrentNode(); + mRuleWalker->SetLevel(eHTMLPresHintSheet, PR_FALSE); if (mRuleProcessors[eHTMLPresHintSheet]) (*aCollectorFunc)(mRuleProcessors[eHTMLPresHintSheet], aData); nsRuleNode* lastHTMLPresHintRN = mRuleWalker->GetCurrentNode(); + mRuleWalker->SetLevel(eDocSheet, PR_FALSE); PRBool cutOffInheritance = PR_FALSE; if (mBindingManager) { // We can supply additional document-level sheets that should be walked. @@ -484,24 +489,31 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, if (!skipUserStyles && !cutOffInheritance && mRuleProcessors[eDocSheet]) // NOTE: different (*aCollectorFunc)(mRuleProcessors[eDocSheet], aData); + mRuleWalker->SetLevel(eStyleAttrSheet, PR_FALSE); if (mRuleProcessors[eStyleAttrSheet]) (*aCollectorFunc)(mRuleProcessors[eStyleAttrSheet], aData); + nsRuleNode* lastDocRN = mRuleWalker->GetCurrentNode(); + mRuleWalker->SetLevel(eOverrideSheet, PR_FALSE); if (mRuleProcessors[eOverrideSheet]) (*aCollectorFunc)(mRuleProcessors[eOverrideSheet], aData); nsRuleNode* lastOvrRN = mRuleWalker->GetCurrentNode(); - // There should be no important rules in the preshint or HTMLpreshint level - AddImportantRules(lastOvrRN, lastHTMLPresHintRN); // doc and override + mRuleWalker->SetLevel(eDocSheet, PR_TRUE); + AddImportantRules(lastDocRN, lastHTMLPresHintRN); // doc + mRuleWalker->SetLevel(eOverrideSheet, PR_TRUE); + AddImportantRules(lastOvrRN, lastDocRN); // override #ifdef DEBUG AssertNoCSSRules(lastHTMLPresHintRN, lastUserRN); AssertNoImportantRules(lastHTMLPresHintRN, lastUserRN); // HTML preshints #endif + mRuleWalker->SetLevel(eUserSheet, PR_TRUE); AddImportantRules(lastUserRN, lastPresHintRN); //user #ifdef DEBUG AssertNoCSSRules(lastPresHintRN, lastAgentRN); AssertNoImportantRules(lastPresHintRN, lastAgentRN); // preshints #endif + mRuleWalker->SetLevel(eAgentSheet, PR_TRUE); AddImportantRules(lastAgentRN, nsnull); //agent } diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index c8134dbea5b..7a15cc62aa8 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -161,8 +161,9 @@ class nsStyleSet eStyleAttrSheet, eOverrideSheet, // CSS eSheetTypeCount - // be sure to keep the number of bits in |mDirty| below updated when - // changing the number of sheet types + // be sure to keep the number of bits in |mDirty| below and in + // NS_RULE_NODE_LEVEL_MASK updated when changing the number of sheet + // types }; // APIs to manipulate the style sheet lists. The sheets in each diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index d004a90aa0c..1dfb73541c1 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -78,6 +78,9 @@ class imgIRequest; // Additional bits for nsRuleNode's mDependentBits: #define NS_RULE_NODE_GC_MARK 0x02000000 +#define NS_RULE_NODE_IS_IMPORTANT 0x08000000 +#define NS_RULE_NODE_LEVEL_MASK 0xf0000000 +#define NS_RULE_NODE_LEVEL_SHIFT 28 // The actual structs start here struct nsStyleStruct {