/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* * nsRange.cpp: Implementation of the nsIDOMRange object. */ #include "nsIDOMRange.h" #include "nsIDOMNode.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentFragment.h" #include "nsIContent.h" #include "nsVoidArray.h" #include "nsIDOMText.h" static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kIRangeIID, NS_IDOMRANGE_IID); static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID); class nsRange : public nsIDOMRange { public: NS_DECL_ISUPPORTS nsRange(); virtual ~nsRange(); // IsPositioned attribute disappeared from the dom spec NS_IMETHOD GetIsPositioned(PRBool* aIsPositioned); NS_IMETHOD GetStartParent(nsIDOMNode** aStartParent); NS_IMETHOD GetStartOffset(PRInt32* aStartOffset); NS_IMETHOD GetEndParent(nsIDOMNode** aEndParent); NS_IMETHOD GetEndOffset(PRInt32* aEndOffset); NS_IMETHOD GetIsCollapsed(PRBool* aIsCollapsed); NS_IMETHOD GetCommonParent(nsIDOMNode** aCommonParent); NS_IMETHOD SetStart(nsIDOMNode* aParent, PRInt32 aOffset); NS_IMETHOD SetStartBefore(nsIDOMNode* aSibling); NS_IMETHOD SetStartAfter(nsIDOMNode* aSibling); NS_IMETHOD SetEnd(nsIDOMNode* aParent, PRInt32 aOffset); NS_IMETHOD SetEndBefore(nsIDOMNode* aSibling); NS_IMETHOD SetEndAfter(nsIDOMNode* aSibling); NS_IMETHOD Collapse(PRBool aToStart); NS_IMETHOD Unposition(); NS_IMETHOD SelectNode(nsIDOMNode* aN); NS_IMETHOD SelectNodeContents(nsIDOMNode* aN); NS_IMETHOD CompareEndPoints(PRUint16 how, nsIDOMRange* srcRange, PRInt32* ret); NS_IMETHOD DeleteContents(); NS_IMETHOD ExtractContents(nsIDOMDocumentFragment** aReturn); NS_IMETHOD CloneContents(nsIDOMDocumentFragment** aReturn); NS_IMETHOD InsertNode(nsIDOMNode* aN); NS_IMETHOD SurroundContents(nsIDOMNode* aN); NS_IMETHOD Clone(nsIDOMRange** aReturn); NS_IMETHOD ToString(nsString& aReturn); private: PRBool mIsPositioned; nsIDOMNode *mStartParent; nsIDOMNode *mEndParent; PRInt32 mStartOffset; PRInt32 mEndOffset; nsVoidArray *mStartAncestors; // just keeping these around to avoid reallocing the arrays. nsVoidArray *mEndAncestors; // the contents of these arrays are discarded across calls. nsVoidArray *mStartAncestorOffsets; // nsVoidArray *mEndAncestorOffsets; // // no copy's or assigns nsRange(const nsRange&); nsRange& operator=(const nsRange&); // helper routines PRBool InSameDoc(nsIDOMNode* aNode1, nsIDOMNode* aNode2); nsresult DoSetRange(nsIDOMNode* aStartN, PRInt32 aStartOffset, nsIDOMNode* aEndN, PRInt32 aEndOffset); PRBool IsIncreasing(nsIDOMNode* aStartN, PRInt32 aStartOff, nsIDOMNode* aEndN, PRInt32 aEndOff); nsresult IsPointInRange(nsIDOMNode* aParent, PRInt32 aOffset, PRBool* aResult); nsresult ComparePointToRange(nsIDOMNode* aParent, PRInt32 aOffset, PRInt32* aResult); PRInt32 IndexOf(nsIDOMNode* aNode); PRInt32 FillArrayWithAncestors(nsVoidArray* aArray,nsIDOMNode* aNode); PRInt32 GetAncestorsAndOffsets(nsIDOMNode* aNode, PRInt32 aOffset, nsVoidArray* aAncestorNodes, nsVoidArray* aAncestorOffsets); nsIDOMNode* CommonParent(nsIDOMNode* aNode1, nsIDOMNode* aNode2); }; nsresult NS_NewRange(nsIDOMRange** aInstancePtrResult) { nsRange * range = new nsRange(); return range->QueryInterface(kIRangeIID, (void**) aInstancePtrResult); } /****************************************************** * constructor/destructor ******************************************************/ nsRange::nsRange() { NS_INIT_REFCNT(); mIsPositioned = PR_FALSE; mStartParent = nsnull; mStartOffset = 0; mEndParent = nsnull; mEndOffset = 0; mStartAncestors = nsnull; mEndAncestors = nsnull; mStartAncestorOffsets = nsnull; mEndAncestorOffsets = nsnull; } nsRange::~nsRange() { delete mStartAncestors; delete mEndAncestors; delete mStartAncestorOffsets; delete mEndAncestorOffsets; } /****************************************************** * XPCOM cruft ******************************************************/ NS_IMPL_ADDREF(nsRange) NS_IMPL_RELEASE(nsRange) nsresult nsRange::QueryInterface(const nsIID& aIID, void** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } if (aIID.Equals(kISupportsIID)) { *aInstancePtrResult = (void*)(nsISupports*)this; NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(kIRangeIID)) { *aInstancePtrResult = (void*)(nsIDOMRange*)this; NS_ADDREF_THIS(); return NS_OK; } return !NS_OK; } /****************************************************** * Private helper routines ******************************************************/ PRBool nsRange::InSameDoc(nsIDOMNode* aNode1, nsIDOMNode* aNode2) { nsresult res; nsIDOMDocument* document1; res = aNode1->GetOwnerDocument(&document1); if (!NS_SUCCEEDED(res)) return PR_FALSE; nsIDOMDocument* document2; res = aNode2->GetOwnerDocument(&document2); if (!NS_SUCCEEDED(res)) { NS_IF_RELEASE(document1); return PR_FALSE; } PRBool retval = PR_FALSE; // Now compare the two documents: is direct comparison safe? if (document1 == document2) retval = PR_TRUE; NS_IF_RELEASE(document1); NS_IF_RELEASE(document2); return retval; } nsresult nsRange::DoSetRange(nsIDOMNode* aStartN, PRInt32 aStartOffset, nsIDOMNode* aEndN, PRInt32 aEndOffset) { if (mStartParent != aStartN) { NS_IF_RELEASE(mStartParent); mStartParent = aStartN; NS_ADDREF(mStartParent); } mStartOffset = aStartOffset; if (mEndParent != aEndN) { NS_IF_RELEASE(mEndParent); mEndParent = aEndN; NS_ADDREF(mEndParent); } mEndOffset = aEndOffset; return NS_OK; } PRBool nsRange::IsIncreasing(nsIDOMNode* aStartN, PRInt32 aStartOffset, nsIDOMNode* aEndN, PRInt32 aEndOffset) { PRInt32 numStartAncestors = 0; PRInt32 numEndAncestors = 0; PRInt32 commonNodeStartOffset = 0; PRInt32 commonNodeEndOffset = 0; // no trivial cases please if (!aStartN || !aEndN) return PR_FALSE; // check common case first if (aStartN==aEndN) { if (aStartOffset>aEndOffset) return PR_FALSE; else return PR_TRUE; } // lazy allocation of ancestor data if (!mStartAncestors) { mStartAncestors = new nsVoidArray(); mStartAncestorOffsets = new nsVoidArray(); mEndAncestors = new nsVoidArray(); mEndAncestorOffsets = new nsVoidArray(); if (!mStartAncestors || mStartAncestorOffsets || mEndAncestors || mEndAncestorOffsets) { NS_NOTREACHED("nsRange::IsIncreasing"); return PR_FALSE; } } else { mStartAncestors->Clear(); mStartAncestorOffsets->Clear(); mEndAncestors->Clear(); mEndAncestorOffsets->Clear(); } numStartAncestors = GetAncestorsAndOffsets(aStartN,aStartOffset,mStartAncestors,mStartAncestorOffsets); numEndAncestors = GetAncestorsAndOffsets(aEndN,aEndOffset,mEndAncestors,mEndAncestorOffsets); --numStartAncestors; // adjusting for 0-based counting --numEndAncestors; // back through the ancestors, starting from the root, until first non-matching ancestor found while (mStartAncestors->ElementAt(numStartAncestors) == mEndAncestors->ElementAt(numEndAncestors)) { --numStartAncestors; --numEndAncestors; if (numStartAncestors<0) break; // this will only happen if one endpoint's node is the common ancestor of the other if (numEndAncestors<0) break; } // now back up one and that's the last common ancestor from the root, // or the first common ancestor from the leaf perspective numStartAncestors++; numEndAncestors++; commonNodeStartOffset = (PRInt32)mStartAncestorOffsets->ElementAt(numStartAncestors); commonNodeEndOffset = (PRInt32)mEndAncestorOffsets->ElementAt(numEndAncestors); if (commonNodeStartOffset>commonNodeEndOffset) return PR_FALSE; else return PR_TRUE; } nsresult nsRange::IsPointInRange(nsIDOMNode* aParent, PRInt32 aOffset, PRBool* aResult) { PRInt32 compareResult = 0; nsresult res; res = ComparePointToRange(aParent, aOffset, &compareResult); if (compareResult) *aResult = PR_FALSE; else *aResult = PR_TRUE; return res; } // returns -1 if point is before range, 0 if point is in range, 1 if point is after range nsresult nsRange::ComparePointToRange(nsIDOMNode* aParent, PRInt32 aOffset, PRInt32* aResult) { // check arguments if (!aResult) return NS_ERROR_NULL_POINTER; // no trivial cases please if (!aParent) return NS_ERROR_NULL_POINTER; // our rnage is in a good state? if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; // check common case first if ((aParent == mStartParent) && (aParent == mEndParent)) { if (aOffsetmEndOffset) { *aResult = 1; return NS_OK; } *aResult = 0; return NS_OK; } // ok, do it the hard way nsVoidArray *pointAncestors = new nsVoidArray(); nsVoidArray *pointAncestorOffsets = new nsVoidArray(); PRInt32 numPointAncestors = 0; PRInt32 numStartAncestors = 0; PRInt32 numEndAncestors = 0; PRInt32 commonNodeStartOffset = 0; PRInt32 commonNodeEndOffset = 0; if (!pointAncestors || !pointAncestorOffsets) { NS_NOTREACHED("nsRange::ComparePointToRange"); delete pointAncestors; delete pointAncestorOffsets; return NS_ERROR_OUT_OF_MEMORY; } // lazy allocation of ancestor data if (!mStartAncestors) { mStartAncestors = new nsVoidArray(); mStartAncestorOffsets = new nsVoidArray(); mEndAncestors = new nsVoidArray(); mEndAncestorOffsets = new nsVoidArray(); if (!mStartAncestors || mStartAncestorOffsets || mEndAncestors || mEndAncestorOffsets) { // my kingdom for exceptions NS_NOTREACHED("nsRange::ComparePointToRange"); delete pointAncestors; delete pointAncestorOffsets; delete mStartAncestors; delete mStartAncestorOffsets; delete mEndAncestors; delete mEndAncestorOffsets; return NS_ERROR_OUT_OF_MEMORY; } } else { mStartAncestors->Clear(); mStartAncestorOffsets->Clear(); mEndAncestors->Clear(); mEndAncestorOffsets->Clear(); } numStartAncestors = GetAncestorsAndOffsets(mStartParent,mStartOffset,mStartAncestors,mStartAncestorOffsets); numEndAncestors = GetAncestorsAndOffsets(mEndParent,mEndOffset,mEndAncestors,mEndAncestorOffsets); numPointAncestors = GetAncestorsAndOffsets(aParent,aOffset,pointAncestors,pointAncestorOffsets); // walk down the arrays until we either find that the point is outside, or we hit the // end of one of the range endpoint arrays, in which case the point is inside. If we // hit the end of the point array and NOT of the range endopint arrays, then the point // is before the range (a bit of a special case, that) --numStartAncestors; // adjusting for 0-based counting --numEndAncestors; --numPointAncestors; // back through the ancestors, starting from the root while (mStartAncestors->ElementAt(numStartAncestors) == pointAncestors->ElementAt(numEndAncestors)) { if ((PRInt32)pointAncestorOffsets->ElementAt(numPointAncestors) < (PRInt32)mStartAncestorOffsets->ElementAt(numStartAncestors)) { *aResult = -1; break; } if ((PRInt32)pointAncestorOffsets->ElementAt(numPointAncestors) > (PRInt32)mEndAncestorOffsets->ElementAt(numEndAncestors)) { *aResult = 1; break; } --numStartAncestors; --numEndAncestors; --numPointAncestors; if ((numStartAncestors < 0) || (numEndAncestors < 0)) { *aResult = 0; break; } if (numPointAncestors < 0) { *aResult = -1; break; } } // clean up delete pointAncestors; delete pointAncestorOffsets; return NS_OK; } // At the moment nobody is using this - maybe we can get rid of it. PRInt32 nsRange::IndexOf(nsIDOMNode* aChildNode) { nsIDOMNode *parentNode; nsIContent *contentChild; nsIContent *contentParent; PRInt32 theIndex = nsnull; // lots of error checking for now if (!aChildNode) { NS_NOTREACHED("nsRange::IndexOf"); return 0; } // get the parent node nsresult res = aChildNode->GetParentNode(&parentNode); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::IndexOf"); return 0; } // convert node and parent to nsIContent, so that we can find the child index res = parentNode->QueryInterface(kIContentIID, (void**)&contentParent); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::IndexOf"); NS_IF_RELEASE(contentParent); return 0; } res = aChildNode->QueryInterface(kIContentIID, (void**)&contentChild); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::IndexOf"); NS_IF_RELEASE(contentParent); NS_IF_RELEASE(contentChild); return 0; } // finally we get the index res = contentParent->IndexOf(contentChild,theIndex); NS_IF_RELEASE(contentParent); NS_IF_RELEASE(contentChild); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::IndexOf"); return 0; } return theIndex; } PRInt32 nsRange::FillArrayWithAncestors(nsVoidArray* aArray,nsIDOMNode* aNode) { PRInt32 i=0; nsIDOMNode *node = aNode; // callers responsibility to make sure args are non-null and proper type // insert node itself aArray->InsertElementAt((void*)node,i); // insert all the ancestors // not doing all the inserts at location 0, that would make for lots of memcopys in the voidArray::InsertElementAt implementation node->GetParentNode(&node); while(node) { ++i; aArray->InsertElementAt((void*)node,i); node->GetParentNode(&node); } return i; } PRInt32 nsRange::GetAncestorsAndOffsets(nsIDOMNode* aNode, PRInt32 aOffset, nsVoidArray* aAncestorNodes, nsVoidArray* aAncestorOffsets) { PRInt32 i=0; PRInt32 nodeOffset; nsresult res; nsIContent *contentNode; nsIContent *contentParent; // callers responsibility to make sure args are non-null and proper type res = aNode->QueryInterface(kIContentIID, (void**)&contentNode); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::GetAncestorsAndOffsets"); NS_IF_RELEASE(contentNode); return -1; // poor man's error code } // insert node itself aAncestorNodes->InsertElementAt((void*)contentNode,i); aAncestorOffsets->InsertElementAt((void*)aOffset,i); // insert all the ancestors // not doing all the inserts at location 0, that would make for lots of memcopys in the voidArray::InsertElementAt implementation contentNode->GetParent(contentParent); while(contentParent) { if (!contentParent) break; contentParent->IndexOf(contentNode, nodeOffset); ++i; aAncestorNodes->InsertElementAt((void*)contentParent,i); aAncestorOffsets->InsertElementAt((void*)nodeOffset,i); contentNode->GetParent(contentParent); NS_IF_RELEASE(contentNode); contentNode = contentParent; } return i; } nsIDOMNode* nsRange::CommonParent(nsIDOMNode* aNode1, nsIDOMNode* aNode2) { nsIDOMNode *theParent = nsnull; // no null nodes please if (!aNode1 || !aNode2) { NS_NOTREACHED("nsRange::CommonParent"); return nsnull; } // shortcut for common case - both nodes are the same if (aNode1 == aNode2) { // should be able to remove this error check later nsresult res = aNode1->GetParentNode(&theParent); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::CommonParent"); return nsnull; } return theParent; } // otherwise traverse the tree for the common ancestor // For now, a pretty dumb hack on computing this nsVoidArray *array1 = new nsVoidArray(); nsVoidArray *array2 = new nsVoidArray(); PRInt32 i=0, j=0; // out of mem? out of luck! if (!array1 || !array2) { NS_NOTREACHED("nsRange::CommonParent"); goto errorLabel; } // get ancestors of each node i = FillArrayWithAncestors(array1,aNode1); j = FillArrayWithAncestors(array2,aNode2); // sanity test (for now) - FillArrayWithAncestors succeeded if ((i==-1) || (j==-1)) { NS_NOTREACHED("nsRange::CommonParent"); goto errorLabel; } // sanity test (for now) - the end of each array // should match and be the root if (array1->ElementAt(i) != array2->ElementAt(j)) { NS_NOTREACHED("nsRange::CommonParent"); goto errorLabel; } // back through the ancestors, starting from the root, until // first different ancestor found. while (array1->ElementAt(i) == array2->ElementAt(j)) { --i; --j; if (i<0) break; // this will only happen if one endpoint's node is the common ancestor of the other if (j<0) break; } // now back up one and that's the last common ancestor from the root, // or the first common ancestor from the leaf perspective i++; theParent = (nsIDOMNode*)array1->ElementAt(i); return theParent; errorLabel: delete array1; delete array2; return nsnull; } /****************************************************** * public functionality ******************************************************/ nsresult nsRange::GetIsPositioned(PRBool* aIsPositioned) { *aIsPositioned = mIsPositioned; return NS_OK; } nsresult nsRange::GetStartParent(nsIDOMNode** aStartParent) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; if (!aStartParent) return NS_ERROR_NULL_POINTER; NS_IF_RELEASE(*aStartParent); NS_IF_ADDREF(mStartParent); *aStartParent = mStartParent; return NS_OK; } nsresult nsRange::GetStartOffset(PRInt32* aStartOffset) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; if (!aStartOffset) return NS_ERROR_NULL_POINTER; *aStartOffset = mStartOffset; return NS_OK; } nsresult nsRange::GetEndParent(nsIDOMNode** aEndParent) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; if (!aEndParent) return NS_ERROR_NULL_POINTER; NS_IF_RELEASE(*aEndParent); NS_IF_ADDREF(mEndParent); *aEndParent = mEndParent; return NS_OK; } nsresult nsRange::GetEndOffset(PRInt32* aEndOffset) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; if (!aEndOffset) return NS_ERROR_NULL_POINTER; *aEndOffset = mEndOffset; return NS_OK; } nsresult nsRange::GetIsCollapsed(PRBool* aIsCollapsed) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; if (mEndParent == 0 || (mStartParent == mEndParent && mStartOffset == mEndOffset)) *aIsCollapsed = PR_TRUE; else *aIsCollapsed = PR_FALSE; return NS_OK; } nsresult nsRange::GetCommonParent(nsIDOMNode** aCommonParent) { *aCommonParent = CommonParent(mStartParent,mEndParent); return NS_OK; } nsresult nsRange::SetStart(nsIDOMNode* aParent, PRInt32 aOffset) { nsresult res; if (!aParent) return NS_ERROR_NULL_POINTER; // must be in same document as endpoint, else // endpoint is collapsed to new start. if (mIsPositioned && !InSameDoc(aParent,mEndParent)) { res = DoSetRange(aParent,aOffset,aParent,aOffset); return res; } // start must be before end if (mIsPositioned && !IsIncreasing(aParent,aOffset,mEndParent,mEndOffset)) return NS_ERROR_ILLEGAL_VALUE; // if it's in an attribute node, end must be in or descended from same node // (haven't done this one yet) res = DoSetRange(aParent,aOffset,mEndParent,mEndOffset); return res; } nsresult nsRange::SetStartBefore(nsIDOMNode* aSibling) { if (!aSibling) return NS_ERROR_NULL_POINTER; return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsRange::SetStartAfter(nsIDOMNode* aSibling) { if (!aSibling) return NS_ERROR_NULL_POINTER; return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsRange::SetEnd(nsIDOMNode* aParent, PRInt32 aOffset) { nsresult res; if (!aParent) return NS_ERROR_NULL_POINTER; // must be in same document as startpoint, else // endpoint is collapsed to new end. if (mIsPositioned && !InSameDoc(aParent,mEndParent)) { res = DoSetRange(aParent,aOffset,aParent,aOffset); return res; } // start must be before end if (mIsPositioned && !IsIncreasing(mStartParent,mStartOffset,aParent,aOffset)) return NS_ERROR_ILLEGAL_VALUE; // if it's in an attribute node, start must be in or descended from same node // (haven't done this one yet) res = DoSetRange(mStartParent,mStartOffset,aParent,aOffset); return res; } nsresult nsRange::SetEndBefore(nsIDOMNode* aSibling) { if (!aSibling) return NS_ERROR_NULL_POINTER; return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsRange::SetEndAfter(nsIDOMNode* aSibling) { if (!aSibling) return NS_ERROR_NULL_POINTER; return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsRange::Collapse(PRBool aToStart) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; if (aToStart) { NS_IF_RELEASE(mEndParent); NS_IF_ADDREF(mStartParent); mEndParent = mStartParent; mEndOffset = mStartOffset; return NS_OK; } else { NS_IF_RELEASE(mStartParent); NS_IF_ADDREF(mEndParent); mStartParent = mEndParent; mStartOffset = mEndOffset; return NS_OK; } } nsresult nsRange::Unposition() { NS_IF_RELEASE(mStartParent); mStartParent = nsnull; mStartOffset = 0; NS_IF_RELEASE(mEndParent); mEndParent = nsnull; mEndOffset = 0; mIsPositioned = PR_FALSE; return NS_OK; } nsresult nsRange::SelectNode(nsIDOMNode* aN) { nsIDOMNode * parent; nsresult res = aN->GetParentNode(&parent); if (!NS_SUCCEEDED(res)) return res; if (mIsPositioned) Unposition(); NS_IF_ADDREF(parent); mStartParent = parent; mStartOffset = 0; // XXX NO DIRECT WAY TO GET CHILD # OF THIS NODE! NS_IF_ADDREF(parent); mEndParent = parent; mEndOffset = mStartOffset; return NS_OK; } nsresult nsRange::SelectNodeContents(nsIDOMNode* aN) { if (mIsPositioned) Unposition(); NS_IF_ADDREF(aN); mStartParent = aN; mStartOffset = 0; NS_IF_ADDREF(aN); mEndParent = aN; mEndOffset = 0; // WRONG! SHOULD BE # OF LAST CHILD! return NS_OK; } nsresult nsRange::DeleteContents() { nsIContent *cStart; nsIContent *cEnd; // get the content versions of our endpoints nsresult res = mStartParent->QueryInterface(kIContentIID, (void**)&cStart); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::DeleteContents"); return NS_ERROR_UNEXPECTED; } res = mEndParent->QueryInterface(kIContentIID, (void**)&cEnd); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::DeleteContents"); NS_IF_RELEASE(cStart); return NS_ERROR_UNEXPECTED; } // effeciency hack for simple case if (cStart == cEnd) { PRBool hasChildren; res = cStart->CanContainChildren(hasChildren); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::DeleteContents"); NS_IF_RELEASE(cStart); NS_IF_RELEASE(cEnd); return res; } if (hasChildren) { PRInt32 i; for (i=mEndOffset; --i; i>=mStartOffset) { res = cStart->RemoveChildAt(i, PR_TRUE); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::DeleteContents"); NS_IF_RELEASE(cStart); NS_IF_RELEASE(cEnd); return res; } } NS_IF_RELEASE(cStart); NS_IF_RELEASE(cEnd); return NS_OK; } else // textnode, or somesuch. offsets refer to data in node { nsIDOMText *textNode; res = mStartParent->QueryInterface(kIDOMTextIID, (void**)&textNode); if (!NS_SUCCEEDED(res)) // if it's not a text node, punt { NS_NOTREACHED("nsRange::DeleteContents"); NS_IF_RELEASE(cStart); NS_IF_RELEASE(cEnd); return NS_ERROR_UNEXPECTED; } // delete the text res = textNode->DeleteData(mStartOffset, mEndOffset); if (!NS_SUCCEEDED(res)) { NS_NOTREACHED("nsRange::DeleteContents"); NS_IF_RELEASE(cStart); NS_IF_RELEASE(cEnd); NS_IF_RELEASE(textNode); return res; } NS_IF_RELEASE(textNode); return NS_OK; } } // complex case // not done yet return NS_OK; } nsresult nsRange::CompareEndPoints(PRUint16 how, nsIDOMRange* srcRange, PRInt32* aCmpRet) { nsresult res; if (aCmpRet == 0) return NS_ERROR_NULL_POINTER; if (srcRange == 0) return NS_ERROR_INVALID_ARG; nsIDOMNode* node1 = 0; nsIDOMNode* node2 = 0; PRInt32 offset1, offset2; switch (how) { case nsIDOMRange::START_TO_START: node1 = mStartParent; offset1 = mStartOffset; res = srcRange->GetStartParent(&node2); if (NS_SUCCEEDED(res)) res = srcRange->GetStartOffset(&offset2); break; case nsIDOMRange::START_TO_END: node1 = mStartParent; offset1 = mStartOffset; res = srcRange->GetEndParent(&node2); if (NS_SUCCEEDED(res)) res = srcRange->GetEndOffset(&offset2); break; case nsIDOMRange::END_TO_START: node1 = mEndParent; offset1 = mEndOffset; res = srcRange->GetStartParent(&node2); if (NS_SUCCEEDED(res)) res = srcRange->GetStartOffset(&offset2); break; case nsIDOMRange::END_TO_END: node1 = mEndParent; offset1 = mEndOffset; res = srcRange->GetEndParent(&node2); if (NS_SUCCEEDED(res)) res = srcRange->GetEndOffset(&offset2); break; default: // shouldn't get here return NS_ERROR_ILLEGAL_VALUE; } if (!NS_SUCCEEDED(res)) { NS_IF_RELEASE(node2); return res; } if ((node1 == node2) && (offset1 == offset2)) *aCmpRet = 0; else if (IsIncreasing(node1, offset2, node2, offset2)) *aCmpRet = 1; else *aCmpRet = -1; NS_IF_RELEASE(node2); return NS_OK; } nsresult nsRange::ExtractContents(nsIDOMDocumentFragment** aReturn) { nsresult res = CloneContents(aReturn); if (!NS_SUCCEEDED(res)) return res; res = DeleteContents(); return res; } nsresult nsRange::CloneContents(nsIDOMDocumentFragment** aReturn) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; nsresult res; nsIDOMDocument* document; res = mStartParent->GetOwnerDocument(&document); if (!NS_SUCCEEDED(res)) return res; // Create a new document fragment in the context of this document nsIDOMDocumentFragment* docfrag; res = document->CreateDocumentFragment(&docfrag); NS_IF_RELEASE(document); // XXX but who owns this doc frag -- when will it be released? if (NS_SUCCEEDED(res)) { // Loop over the nodes contained in this Range: // XXX Getting the nodes still needs to be implemented! return NS_ERROR_NOT_IMPLEMENTED; #if 0 while (1) { // This is how to append a node to a doc frag: nsIDOMNode* clonedNode; res = thisNode->cloneNode(PR_TRUE, &clonedNode); if (!NS_SUCCEEDED(res)) break; // punt nsIDOMNode* retNode; res = docfrag->insertBefore(clonedNode, (nsIDOMNode*)0, &retNode); if (!NS_SUCCEEDED(res)) break; } // haven't implemented it yet, so just punt: if (!NS_SUCCEEDED(res)) { NS_IF_RELEASE(docfrag); return res; } return NS_OK; #endif } NS_IF_RELEASE(docfrag); return res; } nsresult nsRange::Clone(nsIDOMRange** aReturn) { if (aReturn == 0) return NS_ERROR_NULL_POINTER; return NS_ERROR_NOT_IMPLEMENTED; nsresult res = NS_NewRange(aReturn); if (!NS_SUCCEEDED(res)) return res; res = (*aReturn)->SetStart(mStartParent, mStartOffset); if (NS_SUCCEEDED(res)) res = (*aReturn)->SetEnd(mEndParent, mEndOffset); if (!NS_SUCCEEDED(res)) { NS_IF_RELEASE(*aReturn); return res; } return NS_OK; } nsresult nsRange::InsertNode(nsIDOMNode* aN) { return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsRange::SurroundContents(nsIDOMNode* aN) { return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsRange::ToString(nsString& aReturn) { return NS_ERROR_NOT_IMPLEMENTED; }