Bug 730587: Stash a pointer to the subtree root on DOM nodes. r=smaug, sr=jst

This commit is contained in:
Kyle Huey 2012-03-14 13:14:02 -07:00
parent 71ca779083
commit fd1ce47377
8 changed files with 110 additions and 19 deletions

View File

@ -93,8 +93,7 @@ public:
// nsIContent is that it exists with an IID
nsIContent(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsINode(aNodeInfo),
mPrimaryFrame(nsnull)
: nsINode(aNodeInfo)
{
NS_ASSERTION(mNodeInfo,
"No nsINodeInfo passed to nsIContent, PREPARE TO CRASH!!!");
@ -859,8 +858,12 @@ public:
* In the case of absolutely positioned elements and floated elements, this
* frame is the out of flow frame, not the placeholder.
*/
nsIFrame* GetPrimaryFrame() const { return mPrimaryFrame; }
nsIFrame* GetPrimaryFrame() const
{
return IsInDoc() ? mPrimaryFrame : nsnull;
}
void SetPrimaryFrame(nsIFrame* aFrame) {
NS_ASSERTION(IsInDoc(), "This will end badly!");
NS_PRECONDITION(!aFrame || !mPrimaryFrame || aFrame == mPrimaryFrame,
"Losing track of existing primary frame");
mPrimaryFrame = aFrame;
@ -962,11 +965,6 @@ private:
*/
virtual const nsAttrValue* DoGetClasses() const = 0;
/**
* Pointer to our primary frame. Might be null.
*/
nsIFrame* mPrimaryFrame;
public:
#ifdef DEBUG
/**

View File

@ -1994,4 +1994,10 @@ nsINode::GetOwnerDocument() const
return ownerDoc != this ? ownerDoc : nsnull;
}
inline nsINode*
nsINode::OwnerDocAsNode() const
{
return OwnerDoc();
}
#endif /* nsIDocument_h___ */

View File

@ -345,6 +345,11 @@ public:
friend class nsAttrAndChildArray;
#ifdef MOZILLA_INTERNAL_API
#ifdef _MSC_VER
#pragma warning(push)
// Disable annoying warning about 'this' in initializers.
#pragma warning(disable:4355)
#endif
nsINode(already_AddRefed<nsINodeInfo> aNodeInfo)
: mNodeInfo(aNodeInfo),
mParent(nsnull),
@ -353,10 +358,14 @@ public:
mNextSibling(nsnull),
mPreviousSibling(nsnull),
mFirstChild(nsnull),
mSubtreeRoot(this),
mSlots(nsnull)
{
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif
virtual ~nsINode();
@ -463,6 +472,12 @@ public:
return mNodeInfo->GetDocument();
}
/**
* Return the "owner document" of this node as an nsINode*. Implemented
* in nsIDocument.h.
*/
inline nsINode *OwnerDocAsNode() const;
/**
* Returns true if the content has an ancestor that is a document.
*
@ -761,6 +776,35 @@ public:
return mParent && mParent->IsElement() ? mParent : nsnull;
}
/**
* Get the root of the subtree this node belongs to. This never returns
* null. It may return 'this' (e.g. for document nodes, and nodes that
* are the roots of disconnected subtrees).
*/
nsINode* SubtreeRoot() const
{
// There are three cases of interest here. nsINodes that are really:
// 1. nsIDocument nodes - Are always in the document.
// 2. nsIContent nodes - Are either in the document, or mSubtreeRoot
// is updated in BindToTree/UnbindFromTree.
// 3. nsIAttribute nodes - Are never in the document, and mSubtreeRoot
// is always 'this' (as set in nsINode's ctor).
nsINode* node = IsInDoc() ? OwnerDocAsNode() : mSubtreeRoot;
NS_ASSERTION(node, "Should always have a node here!");
#ifdef DEBUG
{
const nsINode* slowNode = this;
const nsINode* iter = slowNode;
while ((iter = iter->GetNodeParent())) {
slowNode = iter;
}
NS_ASSERTION(slowNode == node, "These should always be in sync!");
}
#endif
return node;
}
/**
* See nsIDOMEventTarget
*/
@ -1363,6 +1407,18 @@ protected:
bool HasLockedStyleStates() const
{ return GetBoolFlag(ElementHasLockedStyleStates); }
void SetSubtreeRootPointer(nsINode* aSubtreeRoot)
{
NS_ASSERTION(aSubtreeRoot, "aSubtreeRoot can never be null!");
NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()), "Shouldn't be here!");
mSubtreeRoot = aSubtreeRoot;
}
void ClearSubtreeRootPointer()
{
mSubtreeRoot = nsnull;
}
public:
// Optimized way to get classinfo.
virtual nsXPCClassInfo* GetClassInfo() = 0;
@ -1506,6 +1562,14 @@ protected:
nsIContent* mPreviousSibling;
nsIContent* mFirstChild;
union {
// Pointer to our primary frame. Might be null.
nsIFrame* mPrimaryFrame;
// Pointer to the root of our subtree. Might be null.
nsINode* mSubtreeRoot;
};
// Storage for more members that are usually not needed; allocated lazily.
nsSlots* mSlots;
};

View File

@ -524,6 +524,10 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// Set document
if (aDocument) {
// We no longer need to track the subtree pointer (and in fact we'll assert
// if we do this any later).
ClearSubtreeRootPointer();
// XXX See the comment in nsGenericElement::BindToTree
SetInDocument();
if (mText.IsBidi()) {
@ -531,6 +535,9 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
// Clear the lazy frame construction bits.
UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
} else {
// If we're not in the doc, update our subtree pointer.
SetSubtreeRootPointer(aParent->SubtreeRoot());
}
nsNodeUtils::ParentChainChanged(this);
@ -570,6 +577,9 @@ nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
}
ClearInDocument();
// Begin keeping track of our subtree root.
SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
nsDataSlots *slots = GetExistingDataSlots();
if (slots) {
slots->mBindingParent = nsnull;

View File

@ -219,6 +219,7 @@ nsINode::nsSlots::Unlink()
nsINode::~nsINode()
{
NS_ASSERTION(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
NS_ASSERTION(mSubtreeRoot == this, "Didn't restore state properly?");
}
void*
@ -3164,6 +3165,10 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// aDocument->BindingManager()->ChangeDocumentFor(this, nsnull,
// aDocument);
// We no longer need to track the subtree pointer (and in fact we'll assert
// if we do this any later).
ClearSubtreeRootPointer();
// Being added to a document.
SetInDocument();
@ -3173,6 +3178,9 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
// And the restyle bits
ELEMENT_ALL_RESTYLE_FLAGS);
} else {
// If we're not in the doc, update our subtree pointer.
SetSubtreeRootPointer(aParent->SubtreeRoot());
}
// If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children
@ -3264,6 +3272,9 @@ nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
}
ClearInDocument();
// Begin keeping track of our subtree root.
SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
if (document) {
// Notify XBL- & nsIAnonymousContentCreator-generated
// anonymous content that the document is changing.

View File

@ -184,7 +184,7 @@ nsTextNode::CloneDataNode(nsINodeInfo *aNodeInfo, bool aCloneText) const
return it;
}
nsresult
void
nsTextNode::BindToAttribute(nsIAttribute* aAttr)
{
NS_ASSERTION(!IsInDoc(), "Unbind before binding!");
@ -194,18 +194,17 @@ nsTextNode::BindToAttribute(nsIAttribute* aAttr)
mParent = aAttr;
SetParentIsContent(false);
ClearInDocument();
return NS_OK;
SetSubtreeRootPointer(aAttr->SubtreeRoot());
}
nsresult
void
nsTextNode::UnbindFromAttribute()
{
NS_ASSERTION(GetNodeParent(), "Bind before unbinding!");
NS_ASSERTION(GetNodeParent() &&
GetNodeParent()->IsNodeOfType(nsINode::eATTRIBUTE),
NS_ASSERTION(GetNodeParent()->IsNodeOfType(nsINode::eATTRIBUTE),
"Use this method only to unbind from an attribute!");
mParent = nsnull;
return NS_OK;
SetSubtreeRootPointer(this);
}
nsresult

View File

@ -74,8 +74,8 @@ public:
virtual nsGenericDOMDataNode* CloneDataNode(nsINodeInfo *aNodeInfo,
bool aCloneText) const;
nsresult BindToAttribute(nsIAttribute* aAttr);
nsresult UnbindFromAttribute();
void BindToAttribute(nsIAttribute* aAttr);
void UnbindFromAttribute();
virtual nsXPCClassInfo* GetClassInfo();

View File

@ -727,9 +727,12 @@ nsImageMap::FreeAreas()
PRUint32 i, n = mAreas.Length();
for (i = 0; i < n; i++) {
Area* area = mAreas.ElementAt(i);
NS_ASSERTION(area->mArea->GetPrimaryFrame() == mImageFrame,
"Unexpected primary frame");
area->mArea->SetPrimaryFrame(nsnull);
if (area->mArea->IsInDoc()) {
NS_ASSERTION(area->mArea->GetPrimaryFrame() == mImageFrame,
"Unexpected primary frame");
area->mArea->SetPrimaryFrame(nsnull);
}
area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("focus"), this,
false);