Bug 485808. Create an iterator class to do forward iteration over the result of GetChildArray(). r+sr=sicking

This commit is contained in:
Boris Zbarsky 2009-04-09 21:36:41 -04:00
Родитель b35a20fcb2
Коммит 46a80c50b9
6 изменённых файлов: 138 добавлений и 157 удалений

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

@ -1589,78 +1589,6 @@ private:
PRUint32 mNestingLevel;
};
/**
* Class used to detect unexpected mutations. To use the class create an
* nsMutationGuard on the stack before unexpected mutations could occur.
* You can then at any time call Mutated to check if any unexpected mutations
* have occured.
*
* When a guard is instantiated sMutationCount is set to 300. It is then
* decremented by every mutation (capped at 0). This means that we can only
* detect 300 mutations during the lifetime of a single guard, however that
* should be more then we ever care about as we usually only care if more then
* one mutation has occured.
*
* When the guard goes out of scope it will adjust sMutationCount so that over
* the lifetime of the guard the guard itself has not affected sMutationCount,
* while mutations that happened while the guard was alive still will. This
* allows a guard to be instantiated even if there is another guard higher up
* on the callstack watching for mutations.
*
* The only thing that has to be avoided is for an outer guard to be used
* while an inner guard is alive. This can be avoided by only ever
* instantiating a single guard per scope and only using the guard in the
* current scope.
*/
class nsMutationGuard {
public:
nsMutationGuard()
{
mDelta = eMaxMutations - sMutationCount;
sMutationCount = eMaxMutations;
}
~nsMutationGuard()
{
sMutationCount =
mDelta > sMutationCount ? 0 : sMutationCount - mDelta;
}
/**
* Returns true if any unexpected mutations have occured. You can pass in
* an 8-bit ignore count to ignore a number of expected mutations.
*/
PRBool Mutated(PRUint8 aIgnoreCount)
{
return sMutationCount < static_cast<PRUint32>(eMaxMutations - aIgnoreCount);
}
// This function should be called whenever a mutation that we want to keep
// track of happen. For now this is only done when children are added or
// removed, but we might do it for attribute changes too in the future.
static void DidMutate()
{
if (sMutationCount) {
--sMutationCount;
}
}
private:
// mDelta is the amount sMutationCount was adjusted when the guard was
// initialized. It is needed so that we can undo that adjustment once
// the guard dies.
PRUint32 mDelta;
// The value 300 is not important, as long as it is bigger then anything
// ever passed to Mutated().
enum { eMaxMutations = 300 };
// sMutationCount is a global mutation counter which is decreased by one at
// every mutation. It is capped at 0 to avoid wrapping.
// Its value is always between 0 and 300, inclusive.
static PRUint32 sMutationCount;
};
#define NS_AUTO_GCROOT_PASTE2(tok,line) tok##line
#define NS_AUTO_GCROOT_PASTE(tok,line) \
NS_AUTO_GCROOT_PASTE2(tok,line)

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

@ -165,6 +165,77 @@ inline nsINode* NODE_FROM(C& aContent, D& aDocument)
return static_cast<nsINode*>(aDocument);
}
/**
* Class used to detect unexpected mutations. To use the class create an
* nsMutationGuard on the stack before unexpected mutations could occur.
* You can then at any time call Mutated to check if any unexpected mutations
* have occured.
*
* When a guard is instantiated sMutationCount is set to 300. It is then
* decremented by every mutation (capped at 0). This means that we can only
* detect 300 mutations during the lifetime of a single guard, however that
* should be more then we ever care about as we usually only care if more then
* one mutation has occured.
*
* When the guard goes out of scope it will adjust sMutationCount so that over
* the lifetime of the guard the guard itself has not affected sMutationCount,
* while mutations that happened while the guard was alive still will. This
* allows a guard to be instantiated even if there is another guard higher up
* on the callstack watching for mutations.
*
* The only thing that has to be avoided is for an outer guard to be used
* while an inner guard is alive. This can be avoided by only ever
* instantiating a single guard per scope and only using the guard in the
* current scope.
*/
class nsMutationGuard {
public:
nsMutationGuard()
{
mDelta = eMaxMutations - sMutationCount;
sMutationCount = eMaxMutations;
}
~nsMutationGuard()
{
sMutationCount =
mDelta > sMutationCount ? 0 : sMutationCount - mDelta;
}
/**
* Returns true if any unexpected mutations have occured. You can pass in
* an 8-bit ignore count to ignore a number of expected mutations.
*/
PRBool Mutated(PRUint8 aIgnoreCount)
{
return sMutationCount < static_cast<PRUint32>(eMaxMutations - aIgnoreCount);
}
// This function should be called whenever a mutation that we want to keep
// track of happen. For now this is only done when children are added or
// removed, but we might do it for attribute changes too in the future.
static void DidMutate()
{
if (sMutationCount) {
--sMutationCount;
}
}
private:
// mDelta is the amount sMutationCount was adjusted when the guard was
// initialized. It is needed so that we can undo that adjustment once
// the guard dies.
PRUint32 mDelta;
// The value 300 is not important, as long as it is bigger then anything
// ever passed to Mutated().
enum { eMaxMutations = 300 };
// sMutationCount is a global mutation counter which is decreased by one at
// every mutation. It is capped at 0 to avoid wrapping.
// Its value is always between 0 and 300, inclusive.
static PRUint32 sMutationCount;
};
// IID for the nsINode interface
#define NS_INODE_IID \
@ -739,6 +810,42 @@ public:
*/
nsIDocument* GetOwnerDocument() const;
/**
* Iterator that can be used to easily iterate over the children. This has
* the same restrictions on its use as GetChildArray does.
*/
class ChildIterator {
public:
ChildIterator(const nsINode* aNode) { Init(aNode); }
ChildIterator(const nsINode* aNode, PRUint32 aOffset) {
Init(aNode);
Advance(aOffset);
}
~ChildIterator() {
NS_ASSERTION(!mGuard.Mutated(0), "Unexpected mutations happened");
}
PRBool IsDone() const { return mCur == mEnd; }
operator nsIContent* const () { return *mCur; }
void Next() { NS_PRECONDITION(mCur != mEnd, "Check IsDone"); ++mCur; }
void Advance(PRUint32 aOffset) {
NS_ASSERTION(mCur + aOffset <= mEnd, "Unexpected offset");
mCur += aOffset;
}
private:
void Init(const nsINode* aNode) {
NS_PRECONDITION(aNode, "Must have node here!");
PRUint32 childCount;
mCur = aNode->GetChildArray(&childCount);
mEnd = mCur + childCount;
}
#ifdef DEBUG
nsMutationGuard mGuard;
#endif
nsIContent* const * mCur;
nsIContent* const * mEnd;
};
protected:
// Override this function to create a custom slots class.

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

@ -623,8 +623,10 @@ nsContentList::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
if (!appendToList) {
// The new stuff is somewhere in the middle of our list; check
// whether we need to invalidate
for (i = aNewIndexInContainer; i <= count-1; ++i) {
if (MatchSelf(aContainer->GetChildAt(i))) {
for (nsINode::ChildIterator iter(aContainer, aNewIndexInContainer);
!iter.IsDone();
iter.Next()) {
if (MatchSelf(iter)) {
// Uh-oh. We're gonna have to add elements into the middle
// of our list. That's not worth the effort.
SetDirty();
@ -649,9 +651,11 @@ nsContentList::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
* We're up to date. That means someone's actively using us; we
* may as well grab this content....
*/
for (i = aNewIndexInContainer; i <= count-1; ++i) {
for (nsINode::ChildIterator iter(aContainer, aNewIndexInContainer);
!iter.IsDone();
iter.Next()) {
PRUint32 limit = PRUint32(-1);
nsIContent* newContent = aContainer->GetChildAt(i);
nsIContent* newContent = iter;
if (newContent->IsNodeOfType(nsINode::eELEMENT)) {
PopulateWith(newContent, limit);
}
@ -747,10 +751,8 @@ nsContentList::MatchSelf(nsIContent *aContent)
if (!mDeep)
return PR_FALSE;
PRUint32 i, count = aContent->GetChildCount();
for (i = 0; i < count; i++) {
if (MatchSelf(aContent->GetChildAt(i))) {
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
if (MatchSelf(iter)) {
return PR_TRUE;
}
}
@ -778,25 +780,15 @@ nsContentList::PopulateWith(nsIContent *aContent, PRUint32& aElementsToAppend)
// Don't recurse down if we're not doing a deep match.
if (!mDeep)
return;
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRUint32 count;
nsIContent* const* curChildPtr = aContent->GetChildArray(&count);
nsIContent* const* stop = curChildPtr + count;
for (; curChildPtr != stop; ++curChildPtr) {
nsIContent* curContent = *curChildPtr;
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
nsIContent* curContent = iter;
if (curContent->IsNodeOfType(nsINode::eELEMENT)) {
PopulateWith(*curChildPtr, aElementsToAppend);
PopulateWith(curContent, aElementsToAppend);
if (aElementsToAppend == 0)
break;
}
}
#ifdef DEBUG
NS_ASSERTION(!debugMutationGuard.Mutated(0),
"Unexpected mutations happened. Check your match function!");
#endif
}
void
@ -820,17 +812,11 @@ nsContentList::PopulateWithStartingAfter(nsINode *aStartRoot,
++i; // move to one past
}
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRUint32 childCount;
nsIContent* const* curChildPtr = aStartRoot->GetChildArray(&childCount);
nsIContent* const* stop = curChildPtr + childCount;
// Now advance curChildPtr to the child we want to be starting with
NS_ASSERTION(i <= childCount, "Unexpected index");
curChildPtr += i;
for ( ; curChildPtr != stop; ++curChildPtr) {
nsIContent* content = *curChildPtr;
// Now start an iterator with the child we want to be starting with
for (nsINode::ChildIterator iter(aStartRoot, i);
!iter.IsDone();
iter.Next()) {
nsIContent* content = iter;
if (content->IsNodeOfType(nsINode::eELEMENT)) {
PopulateWith(content, aElementsToAppend);
@ -840,10 +826,6 @@ nsContentList::PopulateWithStartingAfter(nsINode *aStartRoot,
break;
}
}
#ifdef DEBUG
NS_ASSERTION(!debugMutationGuard.Mutated(0),
"Unexpected mutations happened. Check your match function!");
#endif
}
if (aElementsToAppend == 0) {

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

@ -220,8 +220,6 @@ nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
PRUint32 nsMutationGuard::sMutationCount = 0;
PRBool nsContentUtils::sInitialized = PR_FALSE;
static PLDHashTable sEventListenerManagersHash;

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

@ -2327,18 +2327,9 @@ nsDocument::UnregisterNamedItems(nsIContent *aContent)
RemoveFromNameTable(aContent);
RemoveFromIdTable(aContent);
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRUint32 count;
nsIContent * const * kidSlot = aContent->GetChildArray(&count);
nsIContent * const * end = kidSlot + count;
for (; kidSlot != end; ++kidSlot) {
UnregisterNamedItems(*kidSlot);
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
UnregisterNamedItems(iter);
}
NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
}
void
@ -2352,18 +2343,9 @@ nsDocument::RegisterNamedItems(nsIContent *aContent)
UpdateNameTableEntry(aContent);
UpdateIdTableEntry(aContent);
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRUint32 count;
nsIContent * const * kidSlot = aContent->GetChildArray(&count);
nsIContent * const * end = kidSlot + count;
for (; kidSlot != end; ++kidSlot) {
RegisterNamedItems(*kidSlot);
for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
RegisterNamedItems(iter);
}
NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
}
void
@ -2373,18 +2355,11 @@ nsDocument::ContentAppended(nsIDocument* aDocument,
{
NS_ASSERTION(aDocument == this, "unexpected doc");
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRUint32 count;
nsIContent * const * kidSlot = aContainer->GetChildArray(&count);
nsIContent * const * end = kidSlot + count;
for (kidSlot += aNewIndexInContainer; kidSlot != end; ++kidSlot) {
RegisterNamedItems(*kidSlot);
for (nsINode::ChildIterator iter(aContainer, aNewIndexInContainer);
!iter.IsDone();
iter.Next()) {
RegisterNamedItems(iter);
}
NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
}
void

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

@ -173,6 +173,8 @@ NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
PRInt32 nsIContent::sTabFocusModel = eTabFocus_any;
PRBool nsIContent::sTabFocusModelAppliesToXUL = PR_FALSE;
PRUint32 nsMutationGuard::sMutationCount = 0;
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
//----------------------------------------------------------------------
@ -5123,17 +5125,10 @@ TryMatchingElementsInSubtree(nsINode* aRoot,
char databuf[2 * sizeof(RuleProcessorData)];
RuleProcessorData* prevSibling = nsnull;
RuleProcessorData* data = reinterpret_cast<RuleProcessorData*>(databuf);
PRUint32 count;
nsIContent * const * kidSlot = aRoot->GetChildArray(&count);
nsIContent * const * end = kidSlot + count;
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRBool continueIteration = PR_TRUE;
for (; kidSlot != end; ++kidSlot) {
nsIContent* kid = *kidSlot;
for (nsINode::ChildIterator iter(aRoot); !iter.IsDone(); iter.Next()) {
nsIContent* kid = iter;
if (!kid->IsNodeOfType(nsINode::eELEMENT)) {
continue;
}
@ -5193,10 +5188,6 @@ TryMatchingElementsInSubtree(nsINode* aRoot,
prevSibling->~RuleProcessorData();
}
#ifdef DEBUG
NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
#endif
return continueIteration;
}