diff --git a/mozilla/content/base/public/nsIContent.h b/mozilla/content/base/public/nsIContent.h index 8b6407d0659..759ed5a9486 100644 --- a/mozilla/content/base/public/nsIContent.h +++ b/mozilla/content/base/public/nsIContent.h @@ -189,9 +189,12 @@ public: * RangeAdd -- informs content that it owns one or both range endpoints * RangeRemove -- informs content that it no longer owns a range endpoint + * GetRangeList -- returns the list of ranges that have one or both endpoints + * within this content item */ NS_IMETHOD RangeAdd(nsIDOMRange& aRange) = 0; NS_IMETHOD RangeRemove(nsIDOMRange& aRange) = 0; + NS_IMETHOD GetRangeList(nsVoidArray*& aResult) const = 0; /** * Add this object's size information to the sizeof handler and diff --git a/mozilla/content/base/src/nsGenericElement.cpp b/mozilla/content/base/src/nsGenericElement.cpp index b4bd9582f68..b4c104dac2c 100644 --- a/mozilla/content/base/src/nsGenericElement.cpp +++ b/mozilla/content/base/src/nsGenericElement.cpp @@ -28,6 +28,7 @@ #include "nsIDOMDocument.h" #include "nsIDOMDocumentFragment.h" #include "nsIDOMRange.h" +#include "nsRange.h" #include "nsIEventListenerManager.h" #include "nsILinkHandler.h" #include "nsIScriptContextOwner.h" @@ -592,6 +593,8 @@ nsGenericElement::nsGenericElement() nsGenericElement::~nsGenericElement() { + // pop any enclosed ranges out + // nsRange::OwnerGone(mContent); not used for now NS_IF_RELEASE(mTag); NS_IF_RELEASE(mListenerManager); if (nsnull != mDOMSlots) { @@ -2088,6 +2091,7 @@ nsGenericContainerElement::InsertChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildInserted(mContent, aIndex); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2110,6 +2114,7 @@ nsGenericContainerElement::ReplaceChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildReplaced(mContent, aIndex, oldKid); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2132,6 +2137,7 @@ nsGenericContainerElement::AppendChildTo(nsIContent* aKid, PRBool aNotify) if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + // ranges don't need adjustment since new child is at end of list nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2150,6 +2156,7 @@ nsGenericContainerElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) if (nsnull != oldKid ) { nsIDocument* doc = mDocument; PRBool rv = mChildren.RemoveElementAt(aIndex); + nsRange::OwnerChildRemoved(mContent, aIndex, oldKid); if (aNotify) { if (nsnull != doc) { doc->ContentRemoved(mContent, oldKid, aIndex); diff --git a/mozilla/content/base/src/nsRange.cpp b/mozilla/content/base/src/nsRange.cpp index 873124b458e..f38eb35cebf 100644 --- a/mozilla/content/base/src/nsRange.cpp +++ b/mozilla/content/base/src/nsRange.cpp @@ -555,24 +555,80 @@ errorLabel: return nsnull; } - -// sanity check routine for content helpers. confirms that given -// is an nsIContent and that it owns one or both range endpoints. -// returns the associated domNode for convenience -nsresult nsRange::ContentOwnsUs(nsIContent* aParentNode, nsIDOMNode** domNode) +nsresult nsRange::GetDOMNodeFromContent(nsIContent* aParentNode, nsIDOMNode** domNode) { *domNode = nsnull; nsresult res = aParentNode->QueryInterface(kIDOMNodeIID, (void**)domNode); if (!NS_SUCCEEDED(res)) { - NS_NOTREACHED("nsRange::ContentOwnsUs"); + NS_NOTREACHED("nsRange::GetDOMNodeFromContent"); NS_IF_RELEASE(*domNode); return res; } - if ((mStartParent != *domNode) && (mEndParent != *domNode)) + return NS_OK; +} + +nsresult nsRange::PopRanges(nsIDOMNode* aDestNode, PRInt32 aOffset, nsIContent* aSourceNode) +{ + // utility routine to pop all the range endpoints inside the content subtree defined by + // aSourceNode, into the node/offset represented by aDestNode/aOffset. + + nsContentIterator iter(aSourceNode); + nsIContent* cN; + nsVoidArray* theRangeList; + nsresult res; + + res = iter.CurrentNode(&cN); + while (NS_COMFALSE == iter.IsDone()) + { + cN->GetRangeList(theRangeList); + if (theRangeList) + { + nsRange* theRange; + PRInt32 loop = 0; + while (theRange = NS_STATIC_CAST(nsRange*, (theRangeList->ElementAt(loop)))) + { + nsIDOMNode *domNode; + res = GetDOMNodeFromContent(cN, &domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "error updating range list"); + NS_PRECONDITION(domNode, "error updating range list"); + // sanity check - do range and content agree over ownership? + res = theRange->ContentOwnsUs(domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "range and content disagree over range ownership"); + + if (theRange->mStartParent == domNode) + { + // promote start point up to replacement point + theRange->SetStart(aDestNode, aOffset); + } + if (theRange->mEndParent == domNode) + { + // promote end point up to replacement point + theRange->SetEnd(aDestNode, aOffset); + } + + NS_IF_RELEASE(domNode); + loop++; + } + + } + iter.Next(); + NS_IF_RELEASE(cN); // balances addref inside CurrentNode() + res = iter.CurrentNode(&cN); + } + + return NS_OK; +} + +// sanity check routine for content helpers. confirms that given +// node owns one or both range endpoints. +nsresult nsRange::ContentOwnsUs(nsIDOMNode* domNode) +{ + NS_PRECONDITION(domNode, "null pointer"); + if ((mStartParent != domNode) && (mEndParent != domNode)) { NS_NOTREACHED("nsRange::ContentOwnsUs"); - NS_IF_RELEASE(*domNode); + NS_IF_RELEASE(domNode); return NS_ERROR_UNEXPECTED; } return NS_OK; @@ -1244,109 +1300,123 @@ toStringComplexLabel: } -nsresult nsRange::OwnerDestroyed(nsIContent* aParentNode) +nsresult nsRange::OwnerGone(nsIContent* aDyingNode) { - nsIDOMNode *domNode; - nsIDOMNode *grandma; - nsIDOMNode *newStartNode = mStartParent; - nsIDOMNode *newEndNode = mEndParent; - PRInt32 newStartOffset = mStartOffset; - PRInt32 newEndOffset = mEndOffset; - - // sanity check - are we ourselves? - nsresult res = ContentOwnsUs(aParentNode,&domNode); - if (!NS_SUCCEEDED(res)) return res; - - if (mStartParent == domNode) - { - // promote (mStartParent,mStartOffset) to aParentNode's parent and offset - res = domNode->GetParentNode(&grandma); - if (!NS_SUCCEEDED(res)) - { - // we just shot out the top of the document (or fragment). We'd better unposition this range - DoSetRange(nsnull,0,nsnull,0); - NS_IF_RELEASE(grandma); - NS_RELEASE(domNode); - return NS_OK; - } - newStartOffset = IndexOf(domNode); - newStartNode = grandma; - } - if (mEndParent == domNode) - { - // promote (mStartParent,mStartOffset) to aParentNode's parent and offset - res = domNode->GetParentNode(&grandma); - if (!NS_SUCCEEDED(res)) - { - // we just shot out the top of the document (or fragment). We'd better unposition this range - DoSetRange(nsnull,0,nsnull,0); - NS_IF_RELEASE(grandma); - NS_RELEASE(domNode); - return NS_OK; - } - newEndOffset = IndexOf(domNode); - newEndNode = grandma; - } - res = DoSetRange(newStartNode,newStartOffset,newEndNode,newEndOffset); - NS_IF_RELEASE(grandma); - NS_RELEASE(domNode); - return res; + // nothing for now - ideally we would pop out all the enclosed + // ranges, but that it an expensive bunch of tests to do when + // destroying content nodes. + return NS_OK; } nsresult nsRange::OwnerChildInserted(nsIContent* aParentNode, PRInt32 aOffset) { + // quick return if no range list + nsVoidArray *theRangeList; + aParentNode->GetRangeList(theRangeList); + if (theRangeList == nsnull) return NS_OK; + + PRInt32 loop = 0; + nsRange* theRange; nsIDOMNode *domNode; - // sanity check - are we ourselves? - nsresult res = ContentOwnsUs(aParentNode,&domNode); - if (!NS_SUCCEEDED(res)) return res; + nsresult res; + + res = GetDOMNodeFromContent(aParentNode, &domNode); + if (NS_SUCCEEDED(res)) return res; + if (!domNode) return NS_ERROR_UNEXPECTED; - if (mStartParent == domNode) + while (theRange = NS_STATIC_CAST(nsRange*, (theRangeList->ElementAt(loop)))) { - // if child inserted before start, move start offset right one - if (aOffset <= mStartOffset) mStartOffset++; - } - if (mEndParent == domNode) - { - // if child inserted before end, move end offset right one - if (aOffset <= mEndOffset) mEndOffset++; + // sanity check - do range and content agree over ownership? + res = theRange->ContentOwnsUs(domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "range and content disagree over range ownership"); + if (NS_SUCCEEDED(res)) + { + if (theRange->mStartParent == domNode) + { + // if child inserted before start, move start offset right one + if (aOffset <= theRange->mStartOffset) theRange->mStartOffset++; + } + if (theRange->mEndParent == domNode) + { + // if child inserted before end, move end offset right one + if (aOffset <= theRange->mEndOffset) theRange->mEndOffset++; + } + NS_PRECONDITION(NS_SUCCEEDED(res), "error updating range list"); + } + loop++; } NS_RELEASE(domNode); + return NS_OK; +} + + +nsresult nsRange::OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aRemovedNode) +{ + // quick return if no range list + nsVoidArray *theRangeList; + aParentNode->GetRangeList(theRangeList); + if (theRangeList == nsnull) return NS_OK; + + PRInt32 loop = 0; + nsRange *theRange; + nsIDOMNode *domNode; + nsresult res; + + res = GetDOMNodeFromContent(aParentNode, &domNode); + if (NS_SUCCEEDED(res)) return res; + if (!domNode) return NS_ERROR_UNEXPECTED; + + // any ranges that are in the parentNode may need to have offsets updated + while (theRange = NS_STATIC_CAST(nsRange*, (theRangeList->ElementAt(loop)))) + { + // sanity check - do range and content agree over ownership? + res = theRange->ContentOwnsUs(domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "range and content disagree over range ownership"); + if (NS_SUCCEEDED(res)) + { + if (theRange->mStartParent == domNode) + { + // if child deleted before start, move start offset left one + if (aOffset <= theRange->mStartOffset) theRange->mStartOffset--; + } + if (theRange->mEndParent == domNode) + { + // if child deleted before end, move end offset left one + if (aOffset <= theRange->mEndOffset) + { + if (theRange->mEndOffset>0) theRange->mEndOffset--; + } + } + } + loop++; + } + + // any ranges in the content subtree rooted by aRemovedNode need to + // have the enclosed endpoints promoted up to the parentNode/offset + res = PopRanges(domNode, aOffset, aRemovedNode); + + NS_RELEASE(domNode); + return NS_OK; +} + + +nsresult nsRange::OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aReplacedNode) +{ + // don't need to adjust ranges whose endpoints are in this parent, + // but we do need to pop out any range endpoints inside the subtree + // rooted by aReplacedNode. + + nsIDOMNode* parentDomNode; + nsresult res; + + res = GetDOMNodeFromContent(aParentNode, &parentDomNode); + if (NS_SUCCEEDED(res)) return res; + if (!parentDomNode) return NS_ERROR_UNEXPECTED; + + res = PopRanges(parentDomNode, aOffset, aReplacedNode); + + NS_RELEASE(parentDomNode); return res; } - -nsresult nsRange::OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset) -{ - nsIDOMNode *domNode; - // sanity check - are we ourselves? - nsresult res = ContentOwnsUs(aParentNode,&domNode); - if (!NS_SUCCEEDED(res)) return res; - - if (mStartParent == domNode) - { - // if child deleted before start, move start offset left one - if (aOffset <= mStartOffset) - { - if (mStartOffset>0) mStartOffset--; - } - } - if (mEndParent == domNode) - { - // if child deleted before end, move end offset left one - if (aOffset <= mEndOffset) - { - if (mEndOffset>0) mEndOffset--; - } - } - NS_RELEASE(domNode); - return NS_OK; -} - - -nsresult nsRange::OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset) -{ - // for now, do nothing - return NS_OK; -} - diff --git a/mozilla/content/base/src/nsRange.h b/mozilla/content/base/src/nsRange.h index 7642ddc9d98..5e59cfa21b6 100644 --- a/mozilla/content/base/src/nsRange.h +++ b/mozilla/content/base/src/nsRange.h @@ -80,13 +80,13 @@ public: // nsRange interface extensions - NS_IMETHOD OwnerDestroyed(nsIContent* aParentNode); + static NS_METHOD OwnerGone(nsIContent* aParentNode); - NS_IMETHOD OwnerChildInserted(nsIContent* aParentNode, PRInt32 aOffset); + static NS_METHOD OwnerChildInserted(nsIContent* aParentNode, PRInt32 aOffset); - NS_IMETHOD OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset); + static NS_METHOD OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aRemovedNode); - NS_IMETHOD OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset); + static NS_METHOD OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aReplacedNode); private: @@ -110,6 +110,8 @@ private: static PRInt32 IndexOf(nsIDOMNode* aNode); static PRInt32 FillArrayWithAncestors(nsVoidArray* aArray,nsIDOMNode* aNode); static nsIDOMNode* CommonParent(nsIDOMNode* aNode1, nsIDOMNode* aNode2); + static nsresult GetDOMNodeFromContent(nsIContent* aParentNode, nsIDOMNode** domNode); + static nsresult PopRanges(nsIDOMNode* aDestNode, PRInt32 aOffset, nsIContent* aSourceNode); static nsresult CloneSibsAndParents(nsIDOMNode* parentNode, PRInt32 nodeOffset, @@ -135,8 +137,8 @@ private: nsresult AddToListOf(nsIDOMNode* aNode); nsresult RemoveFromListOf(nsIDOMNode* aNode); - - nsresult ContentOwnsUs(nsIContent* aParentNode, nsIDOMNode** domNode); + + nsresult ContentOwnsUs(nsIDOMNode* domNode); }; // Make a new nsIDOMRange object diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp index a5b04b4c86e..e7c655f7b4e 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp @@ -45,6 +45,7 @@ #include "nsStyleConsts.h" #include "nsXIFConverter.h" #include "nsFrame.h" +#include "nsRange.h" #include "nsIPresShell.h" #include "nsIView.h" #include "nsIViewManager.h" @@ -2300,6 +2301,7 @@ nsGenericHTMLContainerElement::InsertChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildInserted(mContent, aIndex); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2322,6 +2324,7 @@ nsGenericHTMLContainerElement::ReplaceChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildReplaced(mContent, aIndex, oldKid); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2344,6 +2347,7 @@ nsGenericHTMLContainerElement::AppendChildTo(nsIContent* aKid, PRBool aNotify) if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + // ranges don't need adjustment since new child is at end of list nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2362,6 +2366,7 @@ nsGenericHTMLContainerElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) if (nsnull != oldKid ) { nsIDocument* doc = mDocument; PRBool rv = mChildren.RemoveElementAt(aIndex); + nsRange::OwnerChildRemoved(mContent, aIndex, oldKid); if (aNotify) { if (nsnull != doc) { doc->ContentRemoved(mContent, oldKid, aIndex); diff --git a/mozilla/content/xml/content/src/nsXMLElement.h b/mozilla/content/xml/content/src/nsXMLElement.h index 376bc4e4e49..d2a4279759b 100644 --- a/mozilla/content/xml/content/src/nsXMLElement.h +++ b/mozilla/content/xml/content/src/nsXMLElement.h @@ -144,6 +144,9 @@ public: NS_IMETHOD RangeRemove(nsIDOMRange& aRange) { return mInner.RangeRemove(aRange); } + NS_IMETHOD GetRangeList(nsVoidArray*& aResult) const { + return mInner.GetRangeList(aResult); + } // nsIXMLContent NS_IMETHOD SetNameSpacePrefix(nsIAtom* aNameSpace); diff --git a/mozilla/layout/base/public/nsIContent.h b/mozilla/layout/base/public/nsIContent.h index 8b6407d0659..759ed5a9486 100644 --- a/mozilla/layout/base/public/nsIContent.h +++ b/mozilla/layout/base/public/nsIContent.h @@ -189,9 +189,12 @@ public: * RangeAdd -- informs content that it owns one or both range endpoints * RangeRemove -- informs content that it no longer owns a range endpoint + * GetRangeList -- returns the list of ranges that have one or both endpoints + * within this content item */ NS_IMETHOD RangeAdd(nsIDOMRange& aRange) = 0; NS_IMETHOD RangeRemove(nsIDOMRange& aRange) = 0; + NS_IMETHOD GetRangeList(nsVoidArray*& aResult) const = 0; /** * Add this object's size information to the sizeof handler and diff --git a/mozilla/layout/base/src/nsDocumentFragment.h b/mozilla/layout/base/src/nsDocumentFragment.h index abf19c81857..f34c4e6af78 100644 --- a/mozilla/layout/base/src/nsDocumentFragment.h +++ b/mozilla/layout/base/src/nsDocumentFragment.h @@ -177,6 +177,8 @@ public: { return mInner.RangeAdd(aRange); } NS_IMETHOD RangeRemove(nsIDOMRange& aRange) { return mInner.RangeRemove(aRange); } + NS_IMETHOD GetRangeList(nsVoidArray*& aResult) const + { return mInner.GetRangeList(aResult); } protected: nsGenericContainerElement mInner; diff --git a/mozilla/layout/base/src/nsGenericElement.cpp b/mozilla/layout/base/src/nsGenericElement.cpp index b4bd9582f68..b4c104dac2c 100644 --- a/mozilla/layout/base/src/nsGenericElement.cpp +++ b/mozilla/layout/base/src/nsGenericElement.cpp @@ -28,6 +28,7 @@ #include "nsIDOMDocument.h" #include "nsIDOMDocumentFragment.h" #include "nsIDOMRange.h" +#include "nsRange.h" #include "nsIEventListenerManager.h" #include "nsILinkHandler.h" #include "nsIScriptContextOwner.h" @@ -592,6 +593,8 @@ nsGenericElement::nsGenericElement() nsGenericElement::~nsGenericElement() { + // pop any enclosed ranges out + // nsRange::OwnerGone(mContent); not used for now NS_IF_RELEASE(mTag); NS_IF_RELEASE(mListenerManager); if (nsnull != mDOMSlots) { @@ -2088,6 +2091,7 @@ nsGenericContainerElement::InsertChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildInserted(mContent, aIndex); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2110,6 +2114,7 @@ nsGenericContainerElement::ReplaceChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildReplaced(mContent, aIndex, oldKid); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2132,6 +2137,7 @@ nsGenericContainerElement::AppendChildTo(nsIContent* aKid, PRBool aNotify) if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + // ranges don't need adjustment since new child is at end of list nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2150,6 +2156,7 @@ nsGenericContainerElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) if (nsnull != oldKid ) { nsIDocument* doc = mDocument; PRBool rv = mChildren.RemoveElementAt(aIndex); + nsRange::OwnerChildRemoved(mContent, aIndex, oldKid); if (aNotify) { if (nsnull != doc) { doc->ContentRemoved(mContent, oldKid, aIndex); diff --git a/mozilla/layout/base/src/nsRange.cpp b/mozilla/layout/base/src/nsRange.cpp index 873124b458e..f38eb35cebf 100644 --- a/mozilla/layout/base/src/nsRange.cpp +++ b/mozilla/layout/base/src/nsRange.cpp @@ -555,24 +555,80 @@ errorLabel: return nsnull; } - -// sanity check routine for content helpers. confirms that given -// is an nsIContent and that it owns one or both range endpoints. -// returns the associated domNode for convenience -nsresult nsRange::ContentOwnsUs(nsIContent* aParentNode, nsIDOMNode** domNode) +nsresult nsRange::GetDOMNodeFromContent(nsIContent* aParentNode, nsIDOMNode** domNode) { *domNode = nsnull; nsresult res = aParentNode->QueryInterface(kIDOMNodeIID, (void**)domNode); if (!NS_SUCCEEDED(res)) { - NS_NOTREACHED("nsRange::ContentOwnsUs"); + NS_NOTREACHED("nsRange::GetDOMNodeFromContent"); NS_IF_RELEASE(*domNode); return res; } - if ((mStartParent != *domNode) && (mEndParent != *domNode)) + return NS_OK; +} + +nsresult nsRange::PopRanges(nsIDOMNode* aDestNode, PRInt32 aOffset, nsIContent* aSourceNode) +{ + // utility routine to pop all the range endpoints inside the content subtree defined by + // aSourceNode, into the node/offset represented by aDestNode/aOffset. + + nsContentIterator iter(aSourceNode); + nsIContent* cN; + nsVoidArray* theRangeList; + nsresult res; + + res = iter.CurrentNode(&cN); + while (NS_COMFALSE == iter.IsDone()) + { + cN->GetRangeList(theRangeList); + if (theRangeList) + { + nsRange* theRange; + PRInt32 loop = 0; + while (theRange = NS_STATIC_CAST(nsRange*, (theRangeList->ElementAt(loop)))) + { + nsIDOMNode *domNode; + res = GetDOMNodeFromContent(cN, &domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "error updating range list"); + NS_PRECONDITION(domNode, "error updating range list"); + // sanity check - do range and content agree over ownership? + res = theRange->ContentOwnsUs(domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "range and content disagree over range ownership"); + + if (theRange->mStartParent == domNode) + { + // promote start point up to replacement point + theRange->SetStart(aDestNode, aOffset); + } + if (theRange->mEndParent == domNode) + { + // promote end point up to replacement point + theRange->SetEnd(aDestNode, aOffset); + } + + NS_IF_RELEASE(domNode); + loop++; + } + + } + iter.Next(); + NS_IF_RELEASE(cN); // balances addref inside CurrentNode() + res = iter.CurrentNode(&cN); + } + + return NS_OK; +} + +// sanity check routine for content helpers. confirms that given +// node owns one or both range endpoints. +nsresult nsRange::ContentOwnsUs(nsIDOMNode* domNode) +{ + NS_PRECONDITION(domNode, "null pointer"); + if ((mStartParent != domNode) && (mEndParent != domNode)) { NS_NOTREACHED("nsRange::ContentOwnsUs"); - NS_IF_RELEASE(*domNode); + NS_IF_RELEASE(domNode); return NS_ERROR_UNEXPECTED; } return NS_OK; @@ -1244,109 +1300,123 @@ toStringComplexLabel: } -nsresult nsRange::OwnerDestroyed(nsIContent* aParentNode) +nsresult nsRange::OwnerGone(nsIContent* aDyingNode) { - nsIDOMNode *domNode; - nsIDOMNode *grandma; - nsIDOMNode *newStartNode = mStartParent; - nsIDOMNode *newEndNode = mEndParent; - PRInt32 newStartOffset = mStartOffset; - PRInt32 newEndOffset = mEndOffset; - - // sanity check - are we ourselves? - nsresult res = ContentOwnsUs(aParentNode,&domNode); - if (!NS_SUCCEEDED(res)) return res; - - if (mStartParent == domNode) - { - // promote (mStartParent,mStartOffset) to aParentNode's parent and offset - res = domNode->GetParentNode(&grandma); - if (!NS_SUCCEEDED(res)) - { - // we just shot out the top of the document (or fragment). We'd better unposition this range - DoSetRange(nsnull,0,nsnull,0); - NS_IF_RELEASE(grandma); - NS_RELEASE(domNode); - return NS_OK; - } - newStartOffset = IndexOf(domNode); - newStartNode = grandma; - } - if (mEndParent == domNode) - { - // promote (mStartParent,mStartOffset) to aParentNode's parent and offset - res = domNode->GetParentNode(&grandma); - if (!NS_SUCCEEDED(res)) - { - // we just shot out the top of the document (or fragment). We'd better unposition this range - DoSetRange(nsnull,0,nsnull,0); - NS_IF_RELEASE(grandma); - NS_RELEASE(domNode); - return NS_OK; - } - newEndOffset = IndexOf(domNode); - newEndNode = grandma; - } - res = DoSetRange(newStartNode,newStartOffset,newEndNode,newEndOffset); - NS_IF_RELEASE(grandma); - NS_RELEASE(domNode); - return res; + // nothing for now - ideally we would pop out all the enclosed + // ranges, but that it an expensive bunch of tests to do when + // destroying content nodes. + return NS_OK; } nsresult nsRange::OwnerChildInserted(nsIContent* aParentNode, PRInt32 aOffset) { + // quick return if no range list + nsVoidArray *theRangeList; + aParentNode->GetRangeList(theRangeList); + if (theRangeList == nsnull) return NS_OK; + + PRInt32 loop = 0; + nsRange* theRange; nsIDOMNode *domNode; - // sanity check - are we ourselves? - nsresult res = ContentOwnsUs(aParentNode,&domNode); - if (!NS_SUCCEEDED(res)) return res; + nsresult res; + + res = GetDOMNodeFromContent(aParentNode, &domNode); + if (NS_SUCCEEDED(res)) return res; + if (!domNode) return NS_ERROR_UNEXPECTED; - if (mStartParent == domNode) + while (theRange = NS_STATIC_CAST(nsRange*, (theRangeList->ElementAt(loop)))) { - // if child inserted before start, move start offset right one - if (aOffset <= mStartOffset) mStartOffset++; - } - if (mEndParent == domNode) - { - // if child inserted before end, move end offset right one - if (aOffset <= mEndOffset) mEndOffset++; + // sanity check - do range and content agree over ownership? + res = theRange->ContentOwnsUs(domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "range and content disagree over range ownership"); + if (NS_SUCCEEDED(res)) + { + if (theRange->mStartParent == domNode) + { + // if child inserted before start, move start offset right one + if (aOffset <= theRange->mStartOffset) theRange->mStartOffset++; + } + if (theRange->mEndParent == domNode) + { + // if child inserted before end, move end offset right one + if (aOffset <= theRange->mEndOffset) theRange->mEndOffset++; + } + NS_PRECONDITION(NS_SUCCEEDED(res), "error updating range list"); + } + loop++; } NS_RELEASE(domNode); + return NS_OK; +} + + +nsresult nsRange::OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aRemovedNode) +{ + // quick return if no range list + nsVoidArray *theRangeList; + aParentNode->GetRangeList(theRangeList); + if (theRangeList == nsnull) return NS_OK; + + PRInt32 loop = 0; + nsRange *theRange; + nsIDOMNode *domNode; + nsresult res; + + res = GetDOMNodeFromContent(aParentNode, &domNode); + if (NS_SUCCEEDED(res)) return res; + if (!domNode) return NS_ERROR_UNEXPECTED; + + // any ranges that are in the parentNode may need to have offsets updated + while (theRange = NS_STATIC_CAST(nsRange*, (theRangeList->ElementAt(loop)))) + { + // sanity check - do range and content agree over ownership? + res = theRange->ContentOwnsUs(domNode); + NS_PRECONDITION(NS_SUCCEEDED(res), "range and content disagree over range ownership"); + if (NS_SUCCEEDED(res)) + { + if (theRange->mStartParent == domNode) + { + // if child deleted before start, move start offset left one + if (aOffset <= theRange->mStartOffset) theRange->mStartOffset--; + } + if (theRange->mEndParent == domNode) + { + // if child deleted before end, move end offset left one + if (aOffset <= theRange->mEndOffset) + { + if (theRange->mEndOffset>0) theRange->mEndOffset--; + } + } + } + loop++; + } + + // any ranges in the content subtree rooted by aRemovedNode need to + // have the enclosed endpoints promoted up to the parentNode/offset + res = PopRanges(domNode, aOffset, aRemovedNode); + + NS_RELEASE(domNode); + return NS_OK; +} + + +nsresult nsRange::OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aReplacedNode) +{ + // don't need to adjust ranges whose endpoints are in this parent, + // but we do need to pop out any range endpoints inside the subtree + // rooted by aReplacedNode. + + nsIDOMNode* parentDomNode; + nsresult res; + + res = GetDOMNodeFromContent(aParentNode, &parentDomNode); + if (NS_SUCCEEDED(res)) return res; + if (!parentDomNode) return NS_ERROR_UNEXPECTED; + + res = PopRanges(parentDomNode, aOffset, aReplacedNode); + + NS_RELEASE(parentDomNode); return res; } - -nsresult nsRange::OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset) -{ - nsIDOMNode *domNode; - // sanity check - are we ourselves? - nsresult res = ContentOwnsUs(aParentNode,&domNode); - if (!NS_SUCCEEDED(res)) return res; - - if (mStartParent == domNode) - { - // if child deleted before start, move start offset left one - if (aOffset <= mStartOffset) - { - if (mStartOffset>0) mStartOffset--; - } - } - if (mEndParent == domNode) - { - // if child deleted before end, move end offset left one - if (aOffset <= mEndOffset) - { - if (mEndOffset>0) mEndOffset--; - } - } - NS_RELEASE(domNode); - return NS_OK; -} - - -nsresult nsRange::OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset) -{ - // for now, do nothing - return NS_OK; -} - diff --git a/mozilla/layout/base/src/nsRange.h b/mozilla/layout/base/src/nsRange.h index 7642ddc9d98..5e59cfa21b6 100644 --- a/mozilla/layout/base/src/nsRange.h +++ b/mozilla/layout/base/src/nsRange.h @@ -80,13 +80,13 @@ public: // nsRange interface extensions - NS_IMETHOD OwnerDestroyed(nsIContent* aParentNode); + static NS_METHOD OwnerGone(nsIContent* aParentNode); - NS_IMETHOD OwnerChildInserted(nsIContent* aParentNode, PRInt32 aOffset); + static NS_METHOD OwnerChildInserted(nsIContent* aParentNode, PRInt32 aOffset); - NS_IMETHOD OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset); + static NS_METHOD OwnerChildRemoved(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aRemovedNode); - NS_IMETHOD OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset); + static NS_METHOD OwnerChildReplaced(nsIContent* aParentNode, PRInt32 aOffset, nsIContent* aReplacedNode); private: @@ -110,6 +110,8 @@ private: static PRInt32 IndexOf(nsIDOMNode* aNode); static PRInt32 FillArrayWithAncestors(nsVoidArray* aArray,nsIDOMNode* aNode); static nsIDOMNode* CommonParent(nsIDOMNode* aNode1, nsIDOMNode* aNode2); + static nsresult GetDOMNodeFromContent(nsIContent* aParentNode, nsIDOMNode** domNode); + static nsresult PopRanges(nsIDOMNode* aDestNode, PRInt32 aOffset, nsIContent* aSourceNode); static nsresult CloneSibsAndParents(nsIDOMNode* parentNode, PRInt32 nodeOffset, @@ -135,8 +137,8 @@ private: nsresult AddToListOf(nsIDOMNode* aNode); nsresult RemoveFromListOf(nsIDOMNode* aNode); - - nsresult ContentOwnsUs(nsIContent* aParentNode, nsIDOMNode** domNode); + + nsresult ContentOwnsUs(nsIDOMNode* domNode); }; // Make a new nsIDOMRange object diff --git a/mozilla/layout/html/content/src/nsGenericHTMLElement.cpp b/mozilla/layout/html/content/src/nsGenericHTMLElement.cpp index a5b04b4c86e..e7c655f7b4e 100644 --- a/mozilla/layout/html/content/src/nsGenericHTMLElement.cpp +++ b/mozilla/layout/html/content/src/nsGenericHTMLElement.cpp @@ -45,6 +45,7 @@ #include "nsStyleConsts.h" #include "nsXIFConverter.h" #include "nsFrame.h" +#include "nsRange.h" #include "nsIPresShell.h" #include "nsIView.h" #include "nsIViewManager.h" @@ -2300,6 +2301,7 @@ nsGenericHTMLContainerElement::InsertChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildInserted(mContent, aIndex); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2322,6 +2324,7 @@ nsGenericHTMLContainerElement::ReplaceChildAt(nsIContent* aKid, if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + nsRange::OwnerChildReplaced(mContent, aIndex, oldKid); nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2344,6 +2347,7 @@ nsGenericHTMLContainerElement::AppendChildTo(nsIContent* aKid, PRBool aNotify) if (rv) { NS_ADDREF(aKid); aKid->SetParent(mContent); + // ranges don't need adjustment since new child is at end of list nsIDocument* doc = mDocument; if (nsnull != doc) { aKid->SetDocument(doc, PR_FALSE); @@ -2362,6 +2366,7 @@ nsGenericHTMLContainerElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) if (nsnull != oldKid ) { nsIDocument* doc = mDocument; PRBool rv = mChildren.RemoveElementAt(aIndex); + nsRange::OwnerChildRemoved(mContent, aIndex, oldKid); if (aNotify) { if (nsnull != doc) { doc->ContentRemoved(mContent, oldKid, aIndex); diff --git a/mozilla/layout/xml/content/src/nsXMLElement.h b/mozilla/layout/xml/content/src/nsXMLElement.h index 376bc4e4e49..d2a4279759b 100644 --- a/mozilla/layout/xml/content/src/nsXMLElement.h +++ b/mozilla/layout/xml/content/src/nsXMLElement.h @@ -144,6 +144,9 @@ public: NS_IMETHOD RangeRemove(nsIDOMRange& aRange) { return mInner.RangeRemove(aRange); } + NS_IMETHOD GetRangeList(nsVoidArray*& aResult) const { + return mInner.GetRangeList(aResult); + } // nsIXMLContent NS_IMETHOD SetNameSpacePrefix(nsIAtom* aNameSpace);