зеркало из https://github.com/mozilla/pjs.git
Bug 704623, part 1. Track orphan DOM nodes so that they can be reported in about:memory. r=smaug
This commit is contained in:
Родитель
677790442c
Коммит
51055b1e7a
|
@ -311,18 +311,24 @@ public:
|
|||
friend class nsAttrAndChildArray;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
static nsINode *sOrphanNodeHead;
|
||||
|
||||
nsINode(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: mNodeInfo(aNodeInfo),
|
||||
mParent(nsnull),
|
||||
mFlags(0),
|
||||
mBoolFlags(0),
|
||||
mNextSibling(nsnull),
|
||||
mPreviousSibling(nsnull),
|
||||
mBoolFlags(1 << NodeIsOrphan),
|
||||
mNextOrphanNode(sOrphanNodeHead->mNextOrphanNode),
|
||||
mPreviousOrphanNode(sOrphanNodeHead),
|
||||
mFirstChild(nsnull),
|
||||
mSlots(nsnull)
|
||||
{
|
||||
}
|
||||
NS_ASSERTION(GetBoolFlag(NodeIsOrphan),
|
||||
"mBoolFlags not initialized correctly!");
|
||||
|
||||
mNextOrphanNode->mPreviousOrphanNode = this;
|
||||
sOrphanNodeHead->mNextOrphanNode = this;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual ~nsINode();
|
||||
|
@ -1103,8 +1109,63 @@ public:
|
|||
nsresult IsEqualNode(nsIDOMNode* aOther, bool* aReturn);
|
||||
bool IsEqualTo(nsINode* aOther);
|
||||
|
||||
nsIContent* GetNextSibling() const { return mNextSibling; }
|
||||
nsIContent* GetPreviousSibling() const { return mPreviousSibling; }
|
||||
nsIContent* GetNextSibling() const
|
||||
{
|
||||
return NS_UNLIKELY(IsOrphan()) ? nsnull : mNextSibling;
|
||||
}
|
||||
|
||||
nsIContent* GetPreviousSibling() const
|
||||
{
|
||||
return NS_UNLIKELY(IsOrphan()) ? nsnull : mPreviousSibling;
|
||||
}
|
||||
|
||||
// Returns true if this node is an orphan node
|
||||
bool IsOrphan() const
|
||||
{
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head orphan check?!");
|
||||
#endif
|
||||
|
||||
return GetBoolFlag(NodeIsOrphan);
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// Mark this node as an orphan node. This marking is only relevant
|
||||
// for this node itself, not its children. Its children are not
|
||||
// considered orphan until they themselves are removed from their
|
||||
// parent and get marked as orphans.
|
||||
void MarkAsOrphan()
|
||||
{
|
||||
NS_ASSERTION(!IsOrphan(), "Orphan node orphaned again?");
|
||||
NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head orphaned?!");
|
||||
|
||||
mNextOrphanNode = sOrphanNodeHead->mNextOrphanNode;
|
||||
mPreviousOrphanNode = sOrphanNodeHead;
|
||||
mNextOrphanNode->mPreviousOrphanNode = this;
|
||||
sOrphanNodeHead->mNextOrphanNode = this;
|
||||
|
||||
SetBoolFlag(NodeIsOrphan);
|
||||
}
|
||||
|
||||
// Unmark this node as an orphan node. Do this before inserting this
|
||||
// node into a parent or otherwise associating it with some other
|
||||
// owner.
|
||||
void MarkAsNonOrphan()
|
||||
{
|
||||
NS_ASSERTION(IsOrphan(), "Non-orphan node un-orphaned");
|
||||
NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head unorphaned?!");
|
||||
NS_ASSERTION(!mParent, "Must not have a parent here!");
|
||||
|
||||
mPreviousOrphanNode->mNextOrphanNode = mNextOrphanNode;
|
||||
mNextOrphanNode->mPreviousOrphanNode = mPreviousOrphanNode;
|
||||
mPreviousOrphanNode = nsnull;
|
||||
mNextOrphanNode = nsnull;
|
||||
|
||||
ClearBoolFlag(NodeIsOrphan);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Init();
|
||||
|
||||
/**
|
||||
* Get the next node in the pre-order tree traversal of the DOM. If
|
||||
|
@ -1251,6 +1312,8 @@ private:
|
|||
NodeHasExplicitBaseURI,
|
||||
// Set if the element has some style states locked
|
||||
ElementHasLockedStyleStates,
|
||||
// Set if the node is an orphan node.
|
||||
NodeIsOrphan,
|
||||
// Guard value
|
||||
BooleanFlagCount
|
||||
};
|
||||
|
@ -1466,8 +1529,24 @@ private:
|
|||
PRUint32 mBoolFlags;
|
||||
|
||||
protected:
|
||||
nsIContent* mNextSibling;
|
||||
nsIContent* mPreviousSibling;
|
||||
union {
|
||||
// mNextSibling is used when this node is part of a DOM tree
|
||||
nsIContent* mNextSibling;
|
||||
|
||||
// mNextOrphanNode is used when this is in the linked list of
|
||||
// orphan nodes.
|
||||
nsINode *mNextOrphanNode;
|
||||
};
|
||||
|
||||
union {
|
||||
// mPreviousSibling is used when this node is part of a DOM tree
|
||||
nsIContent* mPreviousSibling;
|
||||
|
||||
// mPreviousOrphanNode is used when this is in the linked list of
|
||||
// orphan nodes.
|
||||
nsINode* mPreviousOrphanNode;
|
||||
};
|
||||
|
||||
nsIContent* mFirstChild;
|
||||
|
||||
// Storage for more members that are usually not needed; allocated lazily.
|
||||
|
|
|
@ -232,13 +232,19 @@ nsAttrAndChildArray::TakeChildAt(PRUint32 aPos)
|
|||
PRUint32 childCount = ChildCount();
|
||||
void** pos = mImpl->mBuffer + AttrSlotsSize() + aPos;
|
||||
nsIContent* child = static_cast<nsIContent*>(*pos);
|
||||
|
||||
MOZ_ASSERT(!child->IsOrphan(), "Child should not be an orphan here");
|
||||
|
||||
if (child->mPreviousSibling) {
|
||||
child->mPreviousSibling->mNextSibling = child->mNextSibling;
|
||||
}
|
||||
if (child->mNextSibling) {
|
||||
child->mNextSibling->mPreviousSibling = child->mPreviousSibling;
|
||||
}
|
||||
child->mPreviousSibling = child->mNextSibling = nsnull;
|
||||
|
||||
// Mark the child as an orphan now that it's no longer associated
|
||||
// with its old parent.
|
||||
child->MarkAsOrphan();
|
||||
|
||||
memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*));
|
||||
SetChildCount(childCount - 1);
|
||||
|
@ -657,8 +663,12 @@ nsAttrAndChildArray::Clear()
|
|||
// making this false so tree teardown doesn't end up being
|
||||
// O(N*D) (number of nodes times average depth of tree).
|
||||
child->UnbindFromTree(false); // XXX is it better to let the owner do this?
|
||||
// Make sure to unlink our kids from each other, since someone
|
||||
// else could stil be holding references to some of them.
|
||||
// Mark the child as an orphan now that it's no longer a child of
|
||||
// its old parent, and make sure to unlink our kids from each
|
||||
// other, since someone else could stil be holding references to
|
||||
// some of them.
|
||||
|
||||
child->MarkAsOrphan();
|
||||
|
||||
// XXXbz We probably can't push this assignment down into the |aNullParent|
|
||||
// case of UnbindFromTree because we still need the assignment in
|
||||
|
@ -668,7 +678,6 @@ nsAttrAndChildArray::Clear()
|
|||
// to point to each other but keep the kid being removed pointing to them
|
||||
// through ContentRemoved so consumers can find where it used to be in the
|
||||
// list?
|
||||
child->mPreviousSibling = child->mNextSibling = nsnull;
|
||||
NS_RELEASE(child);
|
||||
}
|
||||
|
||||
|
@ -822,8 +831,16 @@ inline void
|
|||
nsAttrAndChildArray::SetChildAtPos(void** aPos, nsIContent* aChild,
|
||||
PRUint32 aIndex, PRUint32 aChildCount)
|
||||
{
|
||||
NS_PRECONDITION(!aChild->GetNextSibling(), "aChild with next sibling?");
|
||||
NS_PRECONDITION(!aChild->GetPreviousSibling(), "aChild with prev sibling?");
|
||||
MOZ_ASSERT(aChild->IsOrphan(), "aChild should be an orphan here");
|
||||
|
||||
NS_PRECONDITION(aChild->IsOrphan() || !aChild->GetNextSibling(),
|
||||
"aChild should be orphan and have no next sibling!");
|
||||
NS_PRECONDITION(aChild->IsOrphan() || !aChild->GetPreviousSibling(),
|
||||
"aChild should be orphan and have no prev sibling!");
|
||||
|
||||
// Unmark this child as an orphan now that it's a child of its new
|
||||
// parent.
|
||||
aChild->MarkAsNonOrphan();
|
||||
|
||||
*aPos = aChild;
|
||||
NS_ADDREF(aChild);
|
||||
|
|
|
@ -362,6 +362,8 @@ nsContentUtils::Init()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsINode::Init();
|
||||
|
||||
nsresult rv = NS_GetNameSpaceManager(&sNameSpaceManager);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ nsDOMAttribute::~nsDOMAttribute()
|
|||
{
|
||||
if (mChild) {
|
||||
static_cast<nsTextNode*>(mChild)->UnbindFromAttribute();
|
||||
mChild->MarkAsOrphan();
|
||||
NS_RELEASE(mChild);
|
||||
mFirstChild = nsnull;
|
||||
}
|
||||
|
@ -121,6 +122,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttribute)
|
|||
nsINode::Unlink(tmp);
|
||||
if (tmp->mChild) {
|
||||
static_cast<nsTextNode*>(tmp->mChild)->UnbindFromAttribute();
|
||||
tmp->mChild->MarkAsOrphan();
|
||||
NS_RELEASE(tmp->mChild);
|
||||
tmp->mFirstChild = nsnull;
|
||||
}
|
||||
|
@ -726,6 +728,7 @@ nsDOMAttribute::EnsureChildState()
|
|||
if (!value.IsEmpty()) {
|
||||
NS_NewTextNode(&mChild, mNodeInfo->NodeInfoManager());
|
||||
|
||||
mChild->MarkAsNonOrphan();
|
||||
static_cast<nsTextNode*>(mChild)->BindToAttribute(this);
|
||||
mFirstChild = mChild;
|
||||
|
||||
|
@ -793,5 +796,6 @@ nsDOMAttribute::doRemoveChild(bool aNotify)
|
|||
}
|
||||
|
||||
child->UnbindFromAttribute();
|
||||
child->MarkAsOrphan();
|
||||
}
|
||||
|
||||
|
|
|
@ -1579,7 +1579,8 @@ nsDocument::~nsDocument()
|
|||
nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this));
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
|
||||
NS_ASSERTION(!mIsShowing, "Deleting a currently-showing document");
|
||||
NS_ASSERTION(IsOrphan(), "Deleted document not an orphan?");
|
||||
|
||||
mInDestructor = true;
|
||||
mInUnlinkOrDeletion = true;
|
||||
|
|
|
@ -213,9 +213,16 @@ nsINode::nsSlots::Unlink()
|
|||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
nsINode *nsINode::sOrphanNodeHead = nsnull;
|
||||
|
||||
nsINode::~nsINode()
|
||||
{
|
||||
NS_ASSERTION(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
|
||||
|
||||
MOZ_ASSERT(IsOrphan(), "Node should be orphan by the time it's deleted!");
|
||||
|
||||
mPreviousOrphanNode->mNextOrphanNode = mNextOrphanNode;
|
||||
mNextOrphanNode->mPreviousOrphanNode = mPreviousOrphanNode;
|
||||
}
|
||||
|
||||
void*
|
||||
|
@ -3228,7 +3235,7 @@ nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
|
||||
// Unset this since that's what the old code effectively did.
|
||||
UnsetFlags(NODE_FORCE_XBL_BINDINGS);
|
||||
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsXULElement* xulElem = nsXULElement::FromContent(this);
|
||||
if (xulElem) {
|
||||
|
@ -3822,7 +3829,22 @@ nsGenericElement::SetTextContent(const nsAString& aTextContent)
|
|||
return nsContentUtils::SetNodeTextContent(this, aTextContent, false);
|
||||
}
|
||||
|
||||
/* static */
|
||||
// static
|
||||
void
|
||||
nsINode::Init()
|
||||
{
|
||||
// Allocate static storage for the head of the list of orphan nodes
|
||||
static MOZ_ALIGNED_DECL(char orphanNodeListHead[sizeof(nsINode)], 8);
|
||||
sOrphanNodeHead = reinterpret_cast<nsINode *>(&orphanNodeListHead[0]);
|
||||
|
||||
sOrphanNodeHead->mNextOrphanNode = sOrphanNodeHead;
|
||||
sOrphanNodeHead->mPreviousOrphanNode = sOrphanNodeHead;
|
||||
|
||||
sOrphanNodeHead->mFirstChild = reinterpret_cast<nsIContent *>(0xdeadbeef);
|
||||
sOrphanNodeHead->mParent = reinterpret_cast<nsIContent *>(0xdeadbeef);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsGenericElement::DispatchEvent(nsPresContext* aPresContext,
|
||||
nsEvent* aEvent,
|
||||
|
|
|
@ -1049,6 +1049,10 @@ nsGlobalWindow::~nsGlobalWindow()
|
|||
}
|
||||
}
|
||||
|
||||
if (IsInnerWindow() && mDocument) {
|
||||
mDoc->MarkAsOrphan();
|
||||
}
|
||||
|
||||
mDocument = nsnull; // Forces Release
|
||||
mDoc = nsnull;
|
||||
|
||||
|
@ -1311,6 +1315,8 @@ nsGlobalWindow::FreeInnerObjects(bool aClearScope)
|
|||
|
||||
// Remember the document's principal.
|
||||
mDocumentPrincipal = mDoc->NodePrincipal();
|
||||
|
||||
mDoc->MarkAsOrphan();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -2262,13 +2268,14 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
html_doc);
|
||||
}
|
||||
|
||||
if (aDocument) {
|
||||
aDocument->SetScriptGlobalObject(newInnerWindow);
|
||||
}
|
||||
aDocument->SetScriptGlobalObject(newInnerWindow);
|
||||
|
||||
if (!aState) {
|
||||
if (reUseInnerWindow) {
|
||||
if (newInnerWindow->mDoc != aDocument) {
|
||||
newInnerWindow->mDoc->MarkAsOrphan();
|
||||
aDocument->MarkAsNonOrphan();
|
||||
|
||||
newInnerWindow->mDocument = do_QueryInterface(aDocument);
|
||||
newInnerWindow->mDoc = aDocument;
|
||||
|
||||
|
@ -2387,6 +2394,9 @@ nsGlobalWindow::InnerSetNewDocument(nsIDocument* aDocument)
|
|||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(aDocument->IsOrphan(), "New document must be orphan!");
|
||||
aDocument->MarkAsNonOrphan();
|
||||
|
||||
mDocument = do_QueryInterface(aDocument);
|
||||
mDoc = aDocument;
|
||||
mLocalStorage = nsnull;
|
||||
|
|
Загрузка…
Ссылка в новой задаче