Make rule nodes know their level in the cascade. b=374907 r+sr=bzbarsky

This commit is contained in:
dbaron%dbaron.org 2007-05-16 21:08:51 +00:00
Родитель 182f9ea34e
Коммит b7e4c0ad1d
6 изменённых файлов: 132 добавлений и 44 удалений

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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