From f0b161c7c60a90adf2ea8e7aa18ff913ded87acd Mon Sep 17 00:00:00 2001 From: "Olli.Pettay%helsinki.fi" Date: Wed, 17 May 2006 16:14:33 +0000 Subject: [PATCH] Bug 329122, Event dispatching code in nsGenericDOMDataNode doesn't handle event retargeting, r=sicking, sr=bz git-svn-id: svn://10.0.0.236/trunk@197861 18797224-902f-48f8-a5cc-f745e15eee43 --- .../content/base/src/nsGenericDOMDataNode.cpp | 98 +++++++++++++------ .../content/base/src/nsGenericDOMDataNode.h | 46 +++++++++ mozilla/content/base/src/nsGenericElement.cpp | 17 +++- mozilla/content/base/src/nsGenericElement.h | 6 ++ 4 files changed, 130 insertions(+), 37 deletions(-) diff --git a/mozilla/content/base/src/nsGenericDOMDataNode.cpp b/mozilla/content/base/src/nsGenericDOMDataNode.cpp index 2a30848823f..adad3d4d330 100644 --- a/mozilla/content/base/src/nsGenericDOMDataNode.cpp +++ b/mozilla/content/base/src/nsGenericDOMDataNode.cpp @@ -68,6 +68,19 @@ #include "pldhash.h" #include "prprf.h" +nsGenericDOMDataNode::nsDataSlots::nsDataSlots(PtrBits aFlags) + : nsINode::nsSlots(aFlags), + mBindingParent(nsnull) +{ +} + +nsGenericDOMDataNode::nsDataSlots::~nsDataSlots() +{ + if (mChildNodes) { + mChildNodes->DropReference(); + } +} + nsGenericDOMDataNode::nsGenericDOMDataNode(nsINodeInfo *aNodeInfo) : nsITextContent(aNodeInfo) { @@ -75,7 +88,12 @@ nsGenericDOMDataNode::nsGenericDOMDataNode(nsINodeInfo *aNodeInfo) nsGenericDOMDataNode::~nsGenericDOMDataNode() { - NS_ASSERTION(!HasSlots(), "Datanodes should not have slots"); + if (HasSlots()) { + nsDataSlots* slots = GetDataSlots(); + PtrBits flags = slots->mFlags | NODE_DOESNT_HAVE_SLOTS; + delete slots; + mFlagsOrSlots = flags; + } } @@ -202,15 +220,17 @@ nsGenericDOMDataNode::GetNextSibling(nsIDOMNode** aNextSibling) nsresult nsGenericDOMDataNode::GetChildNodes(nsIDOMNodeList** aChildNodes) { - // XXX Since we believe this won't be done very often, we won't - // burn another slot in the data node and just create a new - // (empty) childNodes list every time we're asked. - nsChildContentList* list = new nsChildContentList(nsnull); - if (!list) { - return NS_ERROR_OUT_OF_MEMORY; + *aChildNodes = nsnull; + nsDataSlots *slots = GetDataSlots(); + NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY); + + if (!slots->mChildNodes) { + slots->mChildNodes = new nsChildContentList(this); + NS_ENSURE_TRUE(slots->mChildNodes, NS_ERROR_OUT_OF_MEMORY); } - return CallQueryInterface(list, aChildNodes); + NS_ADDREF(*aChildNodes = slots->mChildNodes); + return NS_OK; } nsresult @@ -647,26 +667,34 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // only assert if our parent is _changing_ while we have a parent. NS_PRECONDITION(!GetParent() || aParent == GetParent(), "Already have a parent. Unbind first!"); - // XXXbz GetBindingParent() is broken for us, so can't assert - // anything about it yet. - // NS_PRECONDITION(!GetBindingParent() || - // aBindingParent == GetBindingParent() || - // (aParent && - // aParent->GetBindingParent() == GetBindingParent()), - // "Already have a binding parent. Unbind first!"); + NS_PRECONDITION(!GetBindingParent() || + aBindingParent == GetBindingParent() || + (!aBindingParent && aParent && + aParent->GetBindingParent() == GetBindingParent()), + "Already have a binding parent. Unbind first!"); - // XXXbz we don't keep track of the binding parent yet. We should. - - nsresult rv; + if (!aBindingParent && aParent) { + aBindingParent = aParent->GetBindingParent(); + } + + // First set the binding parent + if (aBindingParent) { + nsDataSlots *slots = GetDataSlots(); + NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY); + + slots->mBindingParent = aBindingParent; // Weak, so no addref happens. + } // Set parent if (aParent) { - mParentPtrBits = NS_REINTERPRET_CAST(PtrBits, aParent) | PARENT_BIT_PARENT_IS_CONTENT; + mParentPtrBits = + NS_REINTERPRET_CAST(PtrBits, aParent) | PARENT_BIT_PARENT_IS_CONTENT; } else { mParentPtrBits = NS_REINTERPRET_CAST(PtrBits, aDocument); } + nsresult rv = NS_OK; nsIDocument *oldOwnerDocument = GetOwnerDoc(); nsIDocument *newOwnerDocument; nsNodeInfoManager* nodeInfoManager; @@ -675,6 +703,7 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // Set document if (aDocument) { + // XXX See the comment in nsGenericElement::BindToTree mParentPtrBits |= PARENT_BIT_INDOCUMENT; if (mText.IsBidi()) { aDocument->SetBidiEnabled(PR_TRUE); @@ -721,10 +750,8 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document"); NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); - // XXXbz GetBindingParent() is broken for us, so can't assert - // anything about it yet. - // NS_POSTCONDITION(aBindingParent = GetBindingParent(), - // "Bound to wrong binding parent"); + NS_POSTCONDITION(aBindingParent == GetBindingParent(), + "Bound to wrong binding parent"); return NS_OK; } @@ -732,7 +759,20 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, void nsGenericDOMDataNode::UnbindFromTree(PRBool aDeep, PRBool aNullParent) { + nsIDocument *document = GetCurrentDoc(); + if (document) { + // Notify XBL- & nsIAnonymousContentCreator-generated + // anonymous content that the document is changing. + // This is needed to update the insertion point. + document->BindingManager()->ChangeDocumentFor(this, document, nsnull); + } + mParentPtrBits = aNullParent ? 0 : mParentPtrBits & ~PARENT_BIT_INDOCUMENT; + + nsDataSlots *slots = GetExistingDataSlots(); + if (slots) { + slots->mBindingParent = nsnull; + } } nsIAtom * @@ -792,13 +832,7 @@ nsGenericDOMDataNode::GetAttrCount() const nsresult nsGenericDOMDataNode::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { - //FIXME! Handle event retargeting, bug 329122. - aVisitor.mCanHandle = PR_TRUE; - aVisitor.mParentTarget = GetParent(); - if (!aVisitor.mParentTarget) { - aVisitor.mParentTarget = GetCurrentDoc(); - } - return NS_OK; + return nsGenericElement::doPreHandleEvent(this, aVisitor); } nsresult @@ -866,8 +900,8 @@ nsGenericDOMDataNode::MayHaveFrame() const nsIContent * nsGenericDOMDataNode::GetBindingParent() const { - nsIContent* parent = GetParent(); - return parent ? parent->GetBindingParent() : nsnull; + nsDataSlots *slots = GetExistingDataSlots(); + return slots ? slots->mBindingParent : nsnull; } PRBool diff --git a/mozilla/content/base/src/nsGenericDOMDataNode.h b/mozilla/content/base/src/nsGenericDOMDataNode.h index 32fcf146e48..48cbcf8dbf9 100644 --- a/mozilla/content/base/src/nsGenericDOMDataNode.h +++ b/mozilla/content/base/src/nsGenericDOMDataNode.h @@ -271,6 +271,52 @@ public: void ToCString(nsAString& aBuf, PRInt32 aOffset, PRInt32 aLen) const; #endif + /** + * There are a set of DOM- and scripting-specific instance variables + * that may only be instantiated when a content object is accessed + * through the DOM. Rather than burn actual slots in the content + * objects for each of these instance variables, we put them off + * in a side structure that's only allocated when the content is + * accessed through the DOM. + */ + class nsDataSlots : public nsINode::nsSlots + { + public: + nsDataSlots(PtrBits aFlags); + ~nsDataSlots(); + + /** + * An object implementing nsIDOMNodeList for this content (childNodes) + * @see nsIDOMNodeList + */ + nsRefPtr mChildNodes; + + /** + * The nearest enclosing content node with a binding that created us. + * @see nsIContent::GetBindingParent + */ + nsIContent* mBindingParent; // [Weak] + }; + + nsDataSlots *GetDataSlots() + { + if (!HasSlots()) { + nsDataSlots *slots = new nsDataSlots(mFlagsOrSlots); + + if (!slots) { + return nsnull; + } + + SetSlots(slots); + } + + return NS_STATIC_CAST(nsDataSlots*, FlagsAsSlots()); + } + + nsDataSlots *GetExistingDataSlots() const + { + return NS_STATIC_CAST(nsDataSlots*, GetExistingSlots()); + } protected: nsresult SplitText(PRUint32 aOffset, nsIDOMText** aReturn); diff --git a/mozilla/content/base/src/nsGenericElement.cpp b/mozilla/content/base/src/nsGenericElement.cpp index 28b021a2790..2207fcea2d6 100644 --- a/mozilla/content/base/src/nsGenericElement.cpp +++ b/mozilla/content/base/src/nsGenericElement.cpp @@ -1904,11 +1904,18 @@ nsGenericElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) nsresult nsGenericElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) +{ + return nsGenericElement::doPreHandleEvent(this, aVisitor); +} + +nsresult +nsGenericElement::doPreHandleEvent(nsIContent* aContent, + nsEventChainPreVisitor& aVisitor) { //FIXME! Document how this event retargeting works, Bug 329124. aVisitor.mCanHandle = PR_TRUE; - nsCOMPtr parent = GetParent(); - if (IsNativeAnonymous()) { + nsCOMPtr parent = aContent->GetParent(); + if (aContent->IsNativeAnonymous()) { // Don't propagate mutation events which are dispatched somewhere inside // native anonymous content. if (aVisitor.mEvent->eventStructType == NS_MUTATION_EVENT) { @@ -1926,11 +1933,11 @@ nsGenericElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) // check for an anonymous parent // XXX XBL2/sXBL issue - nsIDocument* ownerDoc = GetOwnerDoc(); + nsIDocument* ownerDoc = aContent->GetOwnerDoc(); if (ownerDoc) { nsCOMPtr insertionParent; ownerDoc->BindingManager()-> - GetInsertionParent(this, getter_AddRefs(insertionParent)); + GetInsertionParent(aContent, getter_AddRefs(insertionParent)); NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent && aVisitor.mEventTargetAtParent != insertionParent), "Retargeting and having insertion parent!"); @@ -1942,7 +1949,7 @@ nsGenericElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) if (parent) { aVisitor.mParentTarget = parent; } else { - aVisitor.mParentTarget = GetCurrentDoc(); + aVisitor.mParentTarget = aContent->GetCurrentDoc(); } return NS_OK; } diff --git a/mozilla/content/base/src/nsGenericElement.h b/mozilla/content/base/src/nsGenericElement.h index f5ae9b02b0d..99ff834f2c0 100644 --- a/mozilla/content/base/src/nsGenericElement.h +++ b/mozilla/content/base/src/nsGenericElement.h @@ -618,6 +618,12 @@ public: nsIDocument* aDocument, nsAttrAndChildArray& aChildArray); + /** + * Default event prehandling for content objects. Handles event retargeting. + */ + static nsresult doPreHandleEvent(nsIContent* aContent, + nsEventChainPreVisitor& aVisitor); + /** * Method to create and dispatch a left-click event loosely based on * aSourceEvent. If aFullDispatch is true, the event will be dispatched