fixes bugs 46782,50161,48643,49266,49265,46395;

This commit is contained in:
jfrancis%netscape.com 2000-08-26 04:03:50 +00:00
parent 5e37aa86a7
commit 0202119f78
26 changed files with 2084 additions and 1044 deletions

View File

@ -122,14 +122,14 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsStr
}
// if it's already set we are done
if (IsPropSet(aProp,aAttr,aValue)) return NS_OK;
if (IsPropSet(aProp,aAttr,nsnull)) return NS_OK;
// make a new propitem
PropItem *item = new PropItem(aProp,aAttr,aValue);
if (!item) return NS_ERROR_OUT_OF_MEMORY;
// remove it from the list of cleared properties, if we have a match
RemovePropFromClearedList(aProp,aAttr,aValue);
RemovePropFromClearedList(aProp,aAttr);
// add it to the list of set properties
mSetArray.AppendElement((void*)item);
@ -141,30 +141,25 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsStr
nsresult TypeInState::ClearAllProps()
{
// null prop means "all" props
return ClearProp(nsnull,nsAutoString(),nsAutoString());
return ClearProp(nsnull,nsAutoString());
}
nsresult TypeInState::ClearProp(nsIAtom *aProp)
{
return ClearProp(aProp,nsAutoString(),nsAutoString());
return ClearProp(aProp,nsAutoString());
}
nsresult TypeInState::ClearProp(nsIAtom *aProp, const nsString &aAttr)
{
return ClearProp(aProp,aAttr,nsAutoString());
}
nsresult TypeInState::ClearProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue)
{
// if it's already cleared we are done
if (IsPropCleared(aProp,aAttr,aValue)) return NS_OK;
if (IsPropCleared(aProp,aAttr)) return NS_OK;
// make a new propitem
PropItem *item = new PropItem(aProp,aAttr,aValue);
PropItem *item = new PropItem(aProp,aAttr,nsAutoString());
if (!item) return NS_ERROR_OUT_OF_MEMORY;
// remove it from the list of set properties, if we have a match
RemovePropFromSetList(aProp,aAttr,aValue);
RemovePropFromSetList(aProp,aAttr);
// add it to the list of cleared properties
mClearedArray.AppendElement((void*)item);
@ -222,7 +217,7 @@ nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize)
nsresult TypeInState::GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp)
{
return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsAutoString());
return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsnull);
}
nsresult TypeInState::GetTypingState(PRBool &isSet,
@ -230,7 +225,7 @@ nsresult TypeInState::GetTypingState(PRBool &isSet,
nsIAtom *aProp,
const nsString &aAttr)
{
return GetTypingState(isSet, theSetting, aProp, aAttr, nsAutoString());
return GetTypingState(isSet, theSetting, aProp, aAttr, nsnull);
}
@ -238,14 +233,14 @@ nsresult TypeInState::GetTypingState(PRBool &isSet,
PRBool &theSetting,
nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
nsString *aValue)
{
if (IsPropSet(aProp, aAttr, aValue))
{
isSet = PR_TRUE;
theSetting = PR_TRUE;
}
else if (IsPropCleared(aProp, aAttr, aValue))
else if (IsPropCleared(aProp, aAttr))
{
isSet = PR_TRUE;
theSetting = PR_FALSE;
@ -264,8 +259,7 @@ nsresult TypeInState::GetTypingState(PRBool &isSet,
*******************************************************************/
nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
const nsString &aAttr)
{
PRInt32 index;
PropItem *item;
@ -282,7 +276,7 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
if (item) delete item;
}
}
else if (FindPropInList(aProp, aAttr, aValue, mSetArray, index))
else if (FindPropInList(aProp, aAttr, nsnull, mSetArray, index))
{
item = (PropItem*)mSetArray.ElementAt(index);
mSetArray.RemoveElementAt(index);
@ -293,11 +287,10 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
nsresult TypeInState::RemovePropFromClearedList(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
const nsString &aAttr)
{
PRInt32 index;
if (FindPropInList(aProp, aAttr, aValue, mClearedArray, index))
if (FindPropInList(aProp, aAttr, nsnull, mClearedArray, index))
{
PropItem *item = (PropItem*)mClearedArray.ElementAt(index);
mClearedArray.RemoveElementAt(index);
@ -309,16 +302,16 @@ nsresult TypeInState::RemovePropFromClearedList(nsIAtom *aProp,
PRBool TypeInState::IsPropSet(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
nsString* outValue)
{
PRInt32 i;
return IsPropSet(aProp, aAttr, aValue, i);
return IsPropSet(aProp, aAttr, outValue, i);
}
PRBool TypeInState::IsPropSet(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue,
nsString *outValue,
PRInt32 &outIndex)
{
// linear search. list should be short.
@ -329,6 +322,7 @@ PRBool TypeInState::IsPropSet(nsIAtom *aProp,
if ( (item->tag == aProp) &&
(item->attr == aAttr) )
{
if (outValue) *outValue = item->value;
outIndex = i;
return PR_TRUE;
}
@ -338,22 +332,20 @@ PRBool TypeInState::IsPropSet(nsIAtom *aProp,
PRBool TypeInState::IsPropCleared(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
const nsString &aAttr)
{
PRInt32 i;
return IsPropCleared(aProp, aAttr, aValue, i);
return IsPropCleared(aProp, aAttr, i);
}
PRBool TypeInState::IsPropCleared(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue,
PRInt32 &outIndex)
{
if (FindPropInList(aProp, aAttr, aValue, mClearedArray, outIndex))
if (FindPropInList(aProp, aAttr, nsnull, mClearedArray, outIndex))
return PR_TRUE;
if (FindPropInList(0, nsAutoString(), nsAutoString(), mClearedArray, outIndex))
if (FindPropInList(0, nsAutoString(), nsnull, mClearedArray, outIndex))
{
// special case for all props cleared
outIndex = -1;
@ -364,7 +356,7 @@ PRBool TypeInState::IsPropCleared(nsIAtom *aProp,
PRBool TypeInState::FindPropInList(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue,
nsString *outValue,
nsVoidArray &aList,
PRInt32 &outIndex)
{
@ -376,6 +368,7 @@ PRBool TypeInState::FindPropInList(nsIAtom *aProp,
if ( (item->tag == aProp) &&
(item->attr == aAttr) )
{
if (outValue) *outValue = item->value;
outIndex = i;
return PR_TRUE;
}

View File

@ -57,7 +57,6 @@ public:
nsresult ClearAllProps();
nsresult ClearProp(nsIAtom *aProp);
nsresult ClearProp(nsIAtom *aProp, const nsString &aAttr);
nsresult ClearProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
//**************************************************************************
// TakeClearProperty: hands back next poroperty item on the clear list.
@ -78,17 +77,17 @@ public:
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp,
const nsString &aAttr);
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp,
const nsString &aAttr, const nsString &aValue);
const nsString &aAttr, nsString* outValue);
protected:
nsresult RemovePropFromSetList(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
nsresult RemovePropFromClearedList(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue, PRInt32 &outIndex);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue, PRInt32 &outIndex);
PRBool FindPropInList(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue, nsVoidArray &aList, PRInt32 &outIndex);
nsresult RemovePropFromSetList(nsIAtom *aProp, const nsString &aAttr);
nsresult RemovePropFromClearedList(nsIAtom *aProp, const nsString &aAttr);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, nsString* outValue);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, nsString* outValue, PRInt32 &outIndex);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr, PRInt32 &outIndex);
PRBool FindPropInList(nsIAtom *aProp, const nsString &aAttr, nsString *outValue, nsVoidArray &aList, PRInt32 &outIndex);
nsVoidArray mSetArray;
nsVoidArray mClearedArray;

View File

@ -152,17 +152,11 @@ PRInt32 nsEditor::gInstanceCount = 0;
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
* ranges since dom gravity will possibly change the ranges.
*/
nsSelectionState::nsSelectionState() : mArray(), mLock(PR_FALSE) {}
nsSelectionState::nsSelectionState() : mArray(){}
nsSelectionState::~nsSelectionState()
{
// free any items in the array
SelRangeStore *item;
while ((item = (SelRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
MakeEmpty();
}
nsresult
@ -171,7 +165,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i,rangeCount, arrayCount = mArray.Count();
SelRangeStore *item;
nsRangeStore *item;
aSel->GetRangeCount(&rangeCount);
// if we need more items in the array, new them
@ -180,7 +174,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
PRInt32 count = rangeCount-arrayCount;
for (i=0; i<count; i++)
{
item = new SelRangeStore;
item = new nsRangeStore;
mArray.AppendElement(item);
}
}
@ -188,7 +182,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
// else if we have too many, delete them
else if (rangeCount>arrayCount)
{
while ((item = (SelRangeStore*)mArray.ElementAt(rangeCount)))
while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
{
delete item;
mArray.RemoveElementAt(rangeCount);
@ -198,7 +192,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
// now store the selection ranges
for (i=0; i<rangeCount; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
res = aSel->GetRangeAt(i, getter_AddRefs(range));
@ -214,7 +208,7 @@ nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i, arrayCount = mArray.Count();
SelRangeStore *item;
nsRangeStore *item;
// clear out selection
aSel->ClearSelection();
@ -222,7 +216,7 @@ nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
// set the selection ranges anew
for (i=0; i<arrayCount; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(&range);
@ -239,8 +233,8 @@ PRBool
nsSelectionState::IsCollapsed()
{
if (1 != mArray.Count()) return PR_FALSE;
SelRangeStore *item;
item = (SelRangeStore*)mArray.ElementAt(0);
nsRangeStore *item;
item = (nsRangeStore*)mArray.ElementAt(0);
if (!item) return PR_FALSE;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(&range);
@ -258,12 +252,12 @@ nsSelectionState::IsEqual(nsSelectionState *aSelState)
if (myCount != itsCount) return PR_FALSE;
if (myCount < 1) return PR_FALSE;
SelRangeStore *myItem, *itsItem;
nsRangeStore *myItem, *itsItem;
for (i=0; i<myCount; i++)
{
myItem = (SelRangeStore*)mArray.ElementAt(0);
itsItem = (SelRangeStore*)(aSelState->mArray.ElementAt(0));
myItem = (nsRangeStore*)mArray.ElementAt(0);
itsItem = (nsRangeStore*)(aSelState->mArray.ElementAt(0));
if (!myItem || !itsItem) return PR_FALSE;
nsCOMPtr<nsIDOMRange> myRange, itsRange;
@ -281,22 +275,137 @@ nsSelectionState::IsEqual(nsSelectionState *aSelState)
return PR_TRUE;
}
// notification routines used to update the saved selection state in response to
// document editing.
void
nsSelectionState::MakeEmpty()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
PRBool
nsSelectionState::IsEmpty()
{
return (mArray.Count() == 0);
}
/***************************************************************************
* nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
*/
nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
nsRangeUpdater::~nsRangeUpdater()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
void*
nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
{
nsRangeStore *item = new nsRangeStore;
if (!item) return nsnull;
item->StoreRange(aRange);
mArray.AppendElement(item);
return item;
}
nsCOMPtr<nsIDOMRange>
nsRangeUpdater::ReclaimRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return nsnull;
nsCOMPtr<nsIDOMRange> outRange;
item->GetRange(&outRange);
mArray.RemoveElement(aCookie);
delete item;
return outRange;
}
void
nsRangeUpdater::DropRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return;
mArray.RemoveElement(aCookie);
delete item;
}
void
nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.AppendElement(aRangeItem);
return;
}
void
nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.RemoveElement(aRangeItem);
return;
}
nsresult
nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
RegisterRangeItem(item);
}
return NS_OK;;
}
nsresult
nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
DropRangeItem(item);
}
return NS_OK;;
}
// gravity methods:
nsresult
nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
@ -308,25 +417,25 @@ nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
}
nsresult
nsSelectionState::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
return SelAdjCreateNode(aParent, aPosition);
}
nsresult
nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
@ -341,7 +450,7 @@ nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt
nsresult
nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
@ -358,11 +467,11 @@ nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, ns
if (NS_FAILED(result)) return result;
// next step is to check for range enpoints inside aOldRightNode
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOldRightNode)
@ -393,7 +502,7 @@ nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, ns
nsresult
nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
@ -404,11 +513,11 @@ nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// adjust endpoints in aParent
@ -450,7 +559,7 @@ nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsresult
nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
@ -458,7 +567,7 @@ nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffs
nsresult
nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
@ -466,7 +575,7 @@ nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffs
nsresult
nsSelectionState::WillReplaceContainer()
nsRangeUpdater::WillReplaceContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -475,7 +584,7 @@ nsSelectionState::WillReplaceContainer()
nsresult
nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -484,11 +593,11 @@ nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNe
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOriginalNode)
@ -501,7 +610,7 @@ nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNe
nsresult
nsSelectionState::WillRemoveContainer()
nsRangeUpdater::WillRemoveContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -510,7 +619,7 @@ nsSelectionState::WillRemoveContainer()
nsresult
nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -519,11 +628,11 @@ nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRI
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aNode)
@ -546,7 +655,7 @@ nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRI
nsresult
nsSelectionState::WillInsertContainer()
nsRangeUpdater::WillInsertContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -555,7 +664,7 @@ nsSelectionState::WillInsertContainer()
nsresult
nsSelectionState::DidInsertContainer()
nsRangeUpdater::DidInsertContainer()
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -564,7 +673,7 @@ nsSelectionState::DidInsertContainer()
nsresult
nsSelectionState::WillMoveNode()
nsRangeUpdater::WillMoveNode()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -573,7 +682,7 @@ nsSelectionState::WillMoveNode()
nsresult
nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -582,11 +691,11 @@ nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOM
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// like a delete in aOldParent
@ -607,10 +716,21 @@ nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOM
/***************************************************************************
* helper class for nsSelectionState. SelRangeStore stores range endpoints.
* helper class for nsSelectionState. nsRangeStore stores range endpoints.
*/
nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
// DEBUG: PRInt32 nsRangeStore::n = 0;
nsRangeStore::nsRangeStore()
{
// DEBUG: n++; printf("range store alloc count=%d\n", n);
}
nsRangeStore::~nsRangeStore()
{
// DEBUG: n--; printf("range store alloc count=%d\n", n);
}
nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
aRange->GetStartContainer(getter_AddRefs(startNode));
@ -620,7 +740,7 @@ nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
return NS_OK;
}
nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
nsresult nsRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
{
if (!outRange) return NS_ERROR_NULL_POINTER;
nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
@ -644,22 +764,22 @@ nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
class nsAutoReplaceContainerSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
nsIDOMNode *mOriginalNode;
nsIDOMNode *mNewNode;
public:
nsAutoReplaceContainerSelNotify(nsSelectionState *aSelState, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mSel(aSelState)
nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mRU(aRangeUpdater)
,mOriginalNode(aOriginalNode)
,mNewNode(aNewNode)
{
if (mSel) mSel->WillReplaceContainer();
mRU.WillReplaceContainer();
}
~nsAutoReplaceContainerSelNotify()
{
if (mSel) mSel->DidReplaceContainer(mOriginalNode, mNewNode);
mRU.DidReplaceContainer(mOriginalNode, mNewNode);
}
};
@ -672,30 +792,30 @@ class nsAutoReplaceContainerSelNotify
class nsAutoRemoveContainerSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
nsIDOMNode *mNode;
nsIDOMNode *mParent;
PRInt32 mOffset;
PRUint32 mNodeOrigLen;
public:
nsAutoRemoveContainerSelNotify(nsSelectionState *aSelState,
nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRUint32 aNodeOrigLen) :
mSel(aSelState)
mRU(aRangeUpdater)
,mNode(aNode)
,mParent(aParent)
,mOffset(aOffset)
,mNodeOrigLen(aNodeOrigLen)
{
if (mSel) mSel->WillRemoveContainer();
mRU.WillRemoveContainer();
}
~nsAutoRemoveContainerSelNotify()
{
if (mSel) mSel->DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
}
};
@ -707,18 +827,18 @@ class nsAutoRemoveContainerSelNotify
class nsAutoInsertContainerSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
public:
nsAutoInsertContainerSelNotify(nsSelectionState *aSelState) :
mSel(aSelState)
nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
mRU(aRangeUpdater)
{
if (mSel) mSel->WillInsertContainer();
mRU.WillInsertContainer();
}
~nsAutoInsertContainerSelNotify()
{
if (mSel) mSel->DidInsertContainer();
mRU.DidInsertContainer();
}
};
@ -731,30 +851,30 @@ class nsAutoInsertContainerSelNotify
class nsAutoMoveNodeSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
nsIDOMNode *mOldParent;
nsIDOMNode *mNewParent;
PRInt32 mOldOffset;
PRInt32 mNewOffset;
public:
nsAutoMoveNodeSelNotify(nsSelectionState *aSelState,
nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aOldParent,
PRInt32 aOldOffset,
nsIDOMNode *aNewParent,
PRInt32 aNewOffset) :
mSel(aSelState)
mRU(aRangeUpdater)
,mOldParent(aOldParent)
,mNewParent(aNewParent)
,mOldOffset(aOldOffset)
,mNewOffset(aNewOffset)
{
if (mSel) mSel->WillMoveNode();
mRU.WillMoveNode();
}
~nsAutoMoveNodeSelNotify()
{
if (mSel) mSel->DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
}
};
@ -774,7 +894,8 @@ nsEditor::nsEditor()
, mPlaceHolderName(nsnull)
, mPlaceHolderBatch(0)
, mSelState(nsnull)
, mSavedSel(nsnull)
, mSavedSel()
, mRangeUpdater()
, mShouldTxnSetSelection(PR_TRUE)
, mBodyElement(nsnull)
, mInIMEMode(PR_FALSE)
@ -1595,7 +1716,7 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjCreateNode(aParent, aPosition);
mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
if (mActionListeners)
{
@ -1637,7 +1758,7 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjInsertNode(aParent, aPosition);
mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
if (mActionListeners)
{
@ -1686,7 +1807,7 @@ nsEditor::SplitNode(nsIDOMNode * aNode,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
if (mActionListeners)
{
@ -1743,7 +1864,7 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
if (mActionListeners)
{
@ -1789,7 +1910,7 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjDeleteNode(aElement, parent, offset);
mRangeUpdater.SelAdjDeleteNode(aElement, parent, offset);
if (mActionListeners)
{
@ -1856,8 +1977,8 @@ nsEditor::ReplaceContainer(nsIDOMNode *inNode,
// notify our internal selection state listener
// (Note: A nsAutoSelectionReset object must be created
// before calling this to initialize mSavedSel)
nsAutoReplaceContainerSelNotify selStateNotify(mSavedSel, inNode, *outNode);
// before calling this to initialize mRangeUpdater)
nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, *outNode);
nsCOMPtr<nsIDOMNode> child;
PRBool bHasMoreChildren;
@ -1908,7 +2029,7 @@ nsEditor::RemoveContainer(nsIDOMNode *inNode)
nodeList->GetLength(&nodeOrigLen);
// notify our internal selection state listener
nsAutoRemoveContainerSelNotify selNotify(mSavedSel, inNode, parent, offset, nodeOrigLen);
nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, inNode, parent, offset, nodeOrigLen);
nsCOMPtr<nsIDOMNode> child;
while (bHasMoreChildren)
@ -1970,7 +2091,7 @@ nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
}
// notify our internal selection state listener
nsAutoInsertContainerSelNotify selNotify(mSavedSel);
nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
// put inNode in new parent, outNode
res = DeleteNode(inNode);
@ -2009,7 +2130,7 @@ nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK;
// notify our internal selection state listener
nsAutoMoveNodeSelNotify selNotify(mSavedSel, oldParent, oldOffset, aParent, aOffset);
nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset, aParent, aOffset);
// need to adjust aOffset if we are moving aNode further along in it's current parent
if ((aParent == oldParent.get()) && (oldOffset < aOffset))
@ -2250,6 +2371,44 @@ nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
return NS_OK;
}
#ifdef XP_MAC
#pragma mark -
#pragma mark support for selection preservation
#pragma mark -
#endif
PRBool
nsEditor::ArePreservingSelection()
{
if (mSavedSel.IsEmpty()) return PR_FALSE;
return PR_TRUE;
}
nsresult
nsEditor::PreserveSelectionAcrossActions(nsIDOMSelection *aSel)
{
mSavedSel.SaveSelection(aSel);
mRangeUpdater.RegisterSelectionState(mSavedSel);
return NS_OK;
}
nsresult
nsEditor::RestorePreservedSelection(nsIDOMSelection *aSel)
{
if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE;
mSavedSel.RestoreSelection(aSel);
StopPreservingSelection();
return NS_OK;
}
void
nsEditor::StopPreservingSelection()
{
mRangeUpdater.DropSelectionState(mSavedSel);
mSavedSel.MakeEmpty();
}
#ifdef XP_MAC
#pragma mark -
#pragma mark nsIEditorIMESupport
@ -4985,6 +5144,7 @@ nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
nsIDOMNode *aSplitPointParent,
PRInt32 aSplitPointOffset,
PRInt32 *outOffset,
PRBool aNoEmptyContainers,
nsCOMPtr<nsIDOMNode> *outLeftNode,
nsCOMPtr<nsIDOMNode> *outRightNode)
{
@ -5006,12 +5166,12 @@ nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
// this nsEditor routine.
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
PRUint32 textLen=0;
if (nodeAsText)
nodeAsText->GetLength(&textLen);
PRUint32 len;
PRBool bDoSplit = PR_FALSE;
res = GetLengthOfDOMNode(nodeToSplit, len);
if (NS_FAILED(res)) return res;
if (!nodeAsText || (offset && (offset != (PRInt32)textLen)))
if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (PRInt32)len)))
{
bDoSplit = PR_TRUE;
res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));

View File

@ -80,8 +80,10 @@ class nsISelectionController;
*/
// first a helper struct for saving/setting ranges
struct SelRangeStore
struct nsRangeStore
{
nsRangeStore();
~nsRangeStore();
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
@ -89,6 +91,7 @@ struct SelRangeStore
PRInt32 startOffset;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
// DEBUG: static PRInt32 n;
};
class nsSelectionState
@ -104,6 +107,26 @@ class nsSelectionState
PRBool IsEqual(nsSelectionState *aSelState);
void MakeEmpty();
PRBool IsEmpty();
protected:
nsVoidArray mArray;
friend class nsRangeUpdater;
};
class nsRangeUpdater
{
public:
nsRangeUpdater();
~nsRangeUpdater();
void* RegisterRange(nsIDOMRange *aRange);
nsCOMPtr<nsIDOMRange> ReclaimRange(void *aCookie);
void DropRange(void *aCookie);
void RegisterRangeItem(nsRangeStore *aRangeItem);
void DropRangeItem(nsRangeStore *aRangeItem);
nsresult RegisterSelectionState(nsSelectionState &aSelState);
nsresult DropSelectionState(nsSelectionState &aSelState);
// editor selection gravity routines. Note that we can't always depend on
// DOM Range gravity to do what we want to the "real" selection. For instance,
@ -131,7 +154,7 @@ class nsSelectionState
nsresult DidInsertContainer();
nsresult WillMoveNode();
nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
protected:
nsVoidArray mArray;
PRBool mLock;
};
@ -483,6 +506,14 @@ public:
* with a call to EndOperation */
NS_IMETHOD EndOperation();
/** routines for managing the preservation of selection across
* various editor actions */
PRBool ArePreservingSelection();
nsresult PreserveSelectionAcrossActions(nsIDOMSelection *aSel);
nsresult RestorePreservedSelection(nsIDOMSelection *aSel);
void StopPreservingSelection();
/** return the string that represents text nodes in the content tree */
static nsresult GetTextNodeTag(nsString& aOutString);
@ -725,6 +756,7 @@ public:
nsIDOMNode *aSplitPointParent,
PRInt32 aSplitPointOffset,
PRInt32 *outOffset,
PRBool aNoEmptyContainers = PR_FALSE,
nsCOMPtr<nsIDOMNode> *outLeftNode = 0,
nsCOMPtr<nsIDOMNode> *outRightNode = 0);
nsresult JoinNodeDeep(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsCOMPtr<nsIDOMNode> *aOutJoinNode, PRInt32 *outOffset);
@ -748,15 +780,16 @@ protected:
nsCOMPtr<nsITransactionManager> mTxnMgr;
nsCOMPtr<nsIEditProperty> mEditProperty;
nsCOMPtr<nsICSSStyleSheet> mLastStyleSheet; // is owning this dangerous?
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsIAtom *mPlaceHolderName; // name of placeholder transaction
PRInt32 mPlaceHolderBatch; // nesting count for batching
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
nsSelectionState *mSavedSel; // cached selection for nsAutoSelectionReset
PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsIAtom *mPlaceHolderName; // name of placeholder transaction
PRInt32 mPlaceHolderBatch; // nesting count for batching
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
nsSelectionState mSavedSel; // cached selection for nsAutoSelectionReset
nsRangeUpdater mRangeUpdater; // utility class object for maintaining preserved ranges
PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
nsCOMPtr<nsIDOMElement> mBodyElement; // cached body node
PRInt32 mAction; // the current editor action
EDirection mDirection; // the current direction of editor action
PRInt32 mAction; // the current editor action
EDirection mDirection; // the current direction of editor action
// data necessary to build IME transactions
PRBool mInIMEMode; // are we inside an IME composition?

View File

@ -23,6 +23,9 @@
#include "nsEditorUtils.h"
#include "nsIDOMDocument.h"
#include "nsIDOMRange.h"
#include "nsIContent.h"
#include "nsLayoutCID.h"
/******************************************************************************
@ -34,24 +37,162 @@ mSel(nsnull)
,mEd(nsnull)
{
if (!aSel || !aEd) return; // not much we can do, bail.
if (aEd->mSavedSel) return; // we already have initted mSavedSel, so this must be nested call.
if (aEd->ArePreservingSelection()) return; // we already have initted mSavedSel, so this must be nested call.
mSel = do_QueryInterface(aSel);
mEd = aEd;
if (mSel)
{
mEd->mSavedSel = new nsSelectionState();
mEd->mSavedSel->SaveSelection(mSel);
mEd->PreserveSelectionAcrossActions(mSel);
}
}
nsAutoSelectionReset::~nsAutoSelectionReset()
{
if (mSel && mEd->mSavedSel) // mSel will be null if this was nested call
if (mSel && mEd->ArePreservingSelection()) // mSel will be null if this was nested call
{
mEd->mSavedSel->RestoreSelection(mSel);
delete mEd->mSavedSel;
mEd->mSavedSel = nsnull;
mEd->RestorePreservedSelection(mSel);
}
}
void
nsAutoSelectionReset::Abort()
{
mEd->StopPreservingSelection();
}
/******************************************************************************
* some helper classes for iterating the dom tree
*****************************************************************************/
static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
static NS_DEFINE_IID(kContentIteratorCID, NS_CONTENTITERATOR_CID);
nsDOMIterator::nsDOMIterator() :
mIter(nsnull)
{
}
nsDOMIterator::~nsDOMIterator()
{
}
nsresult
nsDOMIterator::Init(nsIDOMRange* aRange)
{
nsresult res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
return mIter->Init(aRange);
}
nsresult
nsDOMIterator::Init(nsIDOMNode* aNode)
{
nsresult res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
return mIter->Init(content);
}
void
nsDOMIterator::ForEach(nsDomIterFunctor& functor) const
{
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
nsresult res;
// iterate through dom
while (NS_ENUMERATOR_FALSE == mIter->IsDone())
{
res = mIter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return;
node = do_QueryInterface(content);
if (!node) return;
functor(node);
res = mIter->Next();
if (NS_FAILED(res)) return;
}
}
nsresult
nsDOMIterator::MakeList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes) const
{
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
nsresult res;
// make a array
res = NS_NewISupportsArray(getter_AddRefs(*outArrayOfNodes));
if (NS_FAILED(res)) return res;
return AppendList(functor, *outArrayOfNodes);
}
nsresult
nsDOMIterator::AppendList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> arrayOfNodes) const
{
if (!arrayOfNodes) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
nsresult res;
// iterate through dom and build list
while (NS_ENUMERATOR_FALSE == mIter->IsDone())
{
res = mIter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return res;
node = do_QueryInterface(content);
if (!node) return NS_ERROR_NULL_POINTER;
if (functor(node))
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
}
res = mIter->Next();
if (NS_FAILED(res)) return res;
}
return NS_OK;
}
nsDOMSubtreeIterator::nsDOMSubtreeIterator()
{
}
nsDOMSubtreeIterator::~nsDOMSubtreeIterator()
{
}
nsresult
nsDOMSubtreeIterator::Init(nsIDOMRange* aRange)
{
nsresult res = nsComponentManager::CreateInstance(kSubtreeIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
return mIter->Init(aRange);
}
nsresult
nsDOMSubtreeIterator::Init(nsIDOMNode* aNode)
{
nsresult res = nsComponentManager::CreateInstance(kSubtreeIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
return mIter->Init(content);
}

View File

@ -32,6 +32,7 @@
#include "nsIAtom.h"
#include "nsVoidArray.h"
#include "nsEditor.h"
#include "nsIContentIterator.h"
/***************************************************************************
* stack based helper class for batching a collection of txns inside a
@ -77,6 +78,9 @@ class nsAutoSelectionReset
/** destructor restores mSel to its former state */
~nsAutoSelectionReset();
/** Abort: cancel selection saver */
void Abort();
};
/***************************************************************************
@ -139,5 +143,47 @@ class nsAutoTxnsConserveSelection
PRBool mOldState;
};
/******************************************************************************
* some helper classes for iterating the dom tree
*****************************************************************************/
class nsDomIterFunctor
{
public:
virtual void* operator()(nsIDOMNode* aNode)=0;
};
class nsBoolDomIterFunctor
{
public:
virtual PRBool operator()(nsIDOMNode* aNode)=0;
};
class nsDOMIterator
{
public:
nsDOMIterator();
virtual ~nsDOMIterator();
nsresult Init(nsIDOMRange* aRange);
nsresult Init(nsIDOMNode* aNode);
void ForEach(nsDomIterFunctor& functor) const;
nsresult MakeList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes) const;
nsresult AppendList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> arrayOfNodes) const;
protected:
nsCOMPtr<nsIContentIterator> mIter;
};
class nsDOMSubtreeIterator : public nsDOMIterator
{
public:
nsDOMSubtreeIterator();
virtual ~nsDOMSubtreeIterator();
nsresult Init(nsIDOMRange* aRange);
nsresult Init(nsIDOMNode* aNode);
};
#endif // nsEditorUtils_h__

File diff suppressed because it is too large Load Diff

View File

@ -109,8 +109,8 @@ protected:
nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult DidMakeBasicBlock(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
nsresult AlignTableElement(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignTableCellContents(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType);
nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE);
nsresult InsertTab(nsIDOMSelection *aSelection, nsString *outString);
@ -146,12 +146,13 @@ protected:
nsresult GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBool &aDD);
nsresult GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult BustUpInlinesAtRangeEndpoints(nsRangeStore &inRange);
nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsCOMPtr<nsIDOMNode> GetHighestInlineParent(nsIDOMNode* aNode);
nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes,
nsVoidArray *inTransitionArray);
nsresult ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, const nsString *blockTag, PRBool *outMakeEmpty);
nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes);
nsresult SplitAsNeeded(const nsString *aTag, nsCOMPtr<nsIDOMNode> *inOutParent, PRInt32 *inOutOffset);

View File

@ -42,6 +42,7 @@ nsHTMLEditUtils::IsBody(nsIDOMNode *node)
{
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag.EqualsWithConversion("body"))
{
return PR_TRUE;
@ -573,4 +574,11 @@ nsHTMLEditUtils::IsDescendantOf(nsIDOMNode *aNode, nsIDOMNode *aParent)
}
PRBool
nsHTMLEditUtils::IsLeafNode(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
PRBool hasChildren = PR_FALSE;
aNode->HasChildNodes(&hasChildren);
return !hasChildren;
}

View File

@ -63,6 +63,9 @@ public:
static PRBool IsMozDiv(nsIDOMNode *aNode);
static PRBool IsMailCite(nsIDOMNode *aNode);
static PRBool IsDescendantOf(nsIDOMNode *aNode, nsIDOMNode *aParent);
static PRBool IsLeafNode(nsIDOMNode *aNode);
};
#endif /* nsHTMLEditUtils_h__ */

View File

@ -1777,6 +1777,16 @@ NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode);
// cache now current, use it! But override it with typeInState results if any...
PRBool isSet, theSetting;
if (aAttribute)
mTypeInState->GetTypingState(isSet, theSetting, aProperty, *aAttribute, outValue);
else
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
if (isSet)
{
aFirst = aAny = aAll = theSetting;
return NS_OK;
}
/*
if (aProperty == mBoldAtom.get())
{
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
@ -1815,7 +1825,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
aFirst = aAny = aAll = mCachedUnderlineStyle;
}
return NS_OK;
}
} */
}
// either non-collapsed selection or no cached value: do it the hard way
@ -7102,7 +7112,7 @@ nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<
///////////////////////////////////////////////////////////////////////////
// GetNextHTMLNode: returns the previous editable leaf node, if there is
// GetNextHTMLNode: returns the next editable leaf node, if there is
// one within the <body>
//
nsresult
@ -7239,6 +7249,75 @@ nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOu
return res;
}
nsresult
nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf)
{
// check parms
if (!aOutFirstLeaf || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutFirstLeaf = nsnull;
// find leftmost leaf
nsCOMPtr<nsIDOMNode> child;
nsresult res = GetLeftmostChild(aNode, getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && (!IsEditable(child) || !nsHTMLEditUtils::IsLeafNode(child)))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetNextHTMLNode(child, &tmp);
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
// only accept nodes that are descendants of aNode
if (nsHTMLEditUtils::IsDescendantOf(tmp, aNode))
child = tmp;
else
{
child = nsnull; // this will abort the loop
}
}
*aOutFirstLeaf = child;
return res;
}
nsresult
nsHTMLEditor::GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf)
{
// check parms
if (!aOutLastLeaf || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutLastLeaf = nsnull;
// find leftmost leaf
nsCOMPtr<nsIDOMNode> child;
nsresult res = GetRightmostChild(aNode, getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && (!IsEditable(child) || !nsHTMLEditUtils::IsLeafNode(child)))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetPriorHTMLNode(child, &tmp);
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
// only accept nodes that are descendants of aNode
if (nsHTMLEditUtils::IsDescendantOf(tmp, aNode))
child = tmp;
else
{
child = nsnull;
}
}
*aOutLastLeaf = child;
return res;
}
///////////////////////////////////////////////////////////////////////////
// IsEmptyNode: figure out if aNode is an empty node.
// A block can have children and still be considered empty,

View File

@ -332,6 +332,11 @@ public:
// aSelection is optional -- if null, we get current seletion
nsresult CollapseSelectionToDeepestNonTableFirstChild(nsIDOMSelection *aSelection, nsIDOMNode *aNode);
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListOrCellNotEmpty = PR_FALSE,
PRBool aSafeToAskFrames = PR_FALSE);
protected:
NS_IMETHOD InitRules();
@ -563,10 +568,8 @@ protected:
nsresult IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast);
nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild);
nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild);
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListOrCellNotEmpty = PR_FALSE,
PRBool aSafeToAskFrames = PR_FALSE);
nsresult GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf);
nsresult GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf);
nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver);

View File

@ -477,7 +477,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P
printf("It's a moz quote -- splitting\n");
nsCOMPtr<nsIDOMNode> outLeftNode;
nsCOMPtr<nsIDOMNode> outRightNode;
res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, &outLeftNode, &outRightNode);
res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, PR_TRUE, &outLeftNode, &outRightNode);
if (NS_FAILED(res)) return res;
PRBool bIsEmptyNode;

View File

@ -152,17 +152,11 @@ PRInt32 nsEditor::gInstanceCount = 0;
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
* ranges since dom gravity will possibly change the ranges.
*/
nsSelectionState::nsSelectionState() : mArray(), mLock(PR_FALSE) {}
nsSelectionState::nsSelectionState() : mArray(){}
nsSelectionState::~nsSelectionState()
{
// free any items in the array
SelRangeStore *item;
while ((item = (SelRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
MakeEmpty();
}
nsresult
@ -171,7 +165,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i,rangeCount, arrayCount = mArray.Count();
SelRangeStore *item;
nsRangeStore *item;
aSel->GetRangeCount(&rangeCount);
// if we need more items in the array, new them
@ -180,7 +174,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
PRInt32 count = rangeCount-arrayCount;
for (i=0; i<count; i++)
{
item = new SelRangeStore;
item = new nsRangeStore;
mArray.AppendElement(item);
}
}
@ -188,7 +182,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
// else if we have too many, delete them
else if (rangeCount>arrayCount)
{
while ((item = (SelRangeStore*)mArray.ElementAt(rangeCount)))
while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
{
delete item;
mArray.RemoveElementAt(rangeCount);
@ -198,7 +192,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
// now store the selection ranges
for (i=0; i<rangeCount; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
res = aSel->GetRangeAt(i, getter_AddRefs(range));
@ -214,7 +208,7 @@ nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i, arrayCount = mArray.Count();
SelRangeStore *item;
nsRangeStore *item;
// clear out selection
aSel->ClearSelection();
@ -222,7 +216,7 @@ nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
// set the selection ranges anew
for (i=0; i<arrayCount; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(&range);
@ -239,8 +233,8 @@ PRBool
nsSelectionState::IsCollapsed()
{
if (1 != mArray.Count()) return PR_FALSE;
SelRangeStore *item;
item = (SelRangeStore*)mArray.ElementAt(0);
nsRangeStore *item;
item = (nsRangeStore*)mArray.ElementAt(0);
if (!item) return PR_FALSE;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(&range);
@ -258,12 +252,12 @@ nsSelectionState::IsEqual(nsSelectionState *aSelState)
if (myCount != itsCount) return PR_FALSE;
if (myCount < 1) return PR_FALSE;
SelRangeStore *myItem, *itsItem;
nsRangeStore *myItem, *itsItem;
for (i=0; i<myCount; i++)
{
myItem = (SelRangeStore*)mArray.ElementAt(0);
itsItem = (SelRangeStore*)(aSelState->mArray.ElementAt(0));
myItem = (nsRangeStore*)mArray.ElementAt(0);
itsItem = (nsRangeStore*)(aSelState->mArray.ElementAt(0));
if (!myItem || !itsItem) return PR_FALSE;
nsCOMPtr<nsIDOMRange> myRange, itsRange;
@ -281,22 +275,137 @@ nsSelectionState::IsEqual(nsSelectionState *aSelState)
return PR_TRUE;
}
// notification routines used to update the saved selection state in response to
// document editing.
void
nsSelectionState::MakeEmpty()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
PRBool
nsSelectionState::IsEmpty()
{
return (mArray.Count() == 0);
}
/***************************************************************************
* nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
*/
nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
nsRangeUpdater::~nsRangeUpdater()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
void*
nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
{
nsRangeStore *item = new nsRangeStore;
if (!item) return nsnull;
item->StoreRange(aRange);
mArray.AppendElement(item);
return item;
}
nsCOMPtr<nsIDOMRange>
nsRangeUpdater::ReclaimRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return nsnull;
nsCOMPtr<nsIDOMRange> outRange;
item->GetRange(&outRange);
mArray.RemoveElement(aCookie);
delete item;
return outRange;
}
void
nsRangeUpdater::DropRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return;
mArray.RemoveElement(aCookie);
delete item;
}
void
nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.AppendElement(aRangeItem);
return;
}
void
nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.RemoveElement(aRangeItem);
return;
}
nsresult
nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
RegisterRangeItem(item);
}
return NS_OK;;
}
nsresult
nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
DropRangeItem(item);
}
return NS_OK;;
}
// gravity methods:
nsresult
nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
@ -308,25 +417,25 @@ nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
}
nsresult
nsSelectionState::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
return SelAdjCreateNode(aParent, aPosition);
}
nsresult
nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
@ -341,7 +450,7 @@ nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt
nsresult
nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
@ -358,11 +467,11 @@ nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, ns
if (NS_FAILED(result)) return result;
// next step is to check for range enpoints inside aOldRightNode
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOldRightNode)
@ -393,7 +502,7 @@ nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, ns
nsresult
nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
@ -404,11 +513,11 @@ nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// adjust endpoints in aParent
@ -450,7 +559,7 @@ nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsresult
nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
@ -458,7 +567,7 @@ nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffs
nsresult
nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
@ -466,7 +575,7 @@ nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffs
nsresult
nsSelectionState::WillReplaceContainer()
nsRangeUpdater::WillReplaceContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -475,7 +584,7 @@ nsSelectionState::WillReplaceContainer()
nsresult
nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -484,11 +593,11 @@ nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNe
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOriginalNode)
@ -501,7 +610,7 @@ nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNe
nsresult
nsSelectionState::WillRemoveContainer()
nsRangeUpdater::WillRemoveContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -510,7 +619,7 @@ nsSelectionState::WillRemoveContainer()
nsresult
nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -519,11 +628,11 @@ nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRI
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aNode)
@ -546,7 +655,7 @@ nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRI
nsresult
nsSelectionState::WillInsertContainer()
nsRangeUpdater::WillInsertContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -555,7 +664,7 @@ nsSelectionState::WillInsertContainer()
nsresult
nsSelectionState::DidInsertContainer()
nsRangeUpdater::DidInsertContainer()
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -564,7 +673,7 @@ nsSelectionState::DidInsertContainer()
nsresult
nsSelectionState::WillMoveNode()
nsRangeUpdater::WillMoveNode()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
@ -573,7 +682,7 @@ nsSelectionState::WillMoveNode()
nsresult
nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
@ -582,11 +691,11 @@ nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOM
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
SelRangeStore *item;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (SelRangeStore*)mArray.ElementAt(i);
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// like a delete in aOldParent
@ -607,10 +716,21 @@ nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOM
/***************************************************************************
* helper class for nsSelectionState. SelRangeStore stores range endpoints.
* helper class for nsSelectionState. nsRangeStore stores range endpoints.
*/
nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
// DEBUG: PRInt32 nsRangeStore::n = 0;
nsRangeStore::nsRangeStore()
{
// DEBUG: n++; printf("range store alloc count=%d\n", n);
}
nsRangeStore::~nsRangeStore()
{
// DEBUG: n--; printf("range store alloc count=%d\n", n);
}
nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
aRange->GetStartContainer(getter_AddRefs(startNode));
@ -620,7 +740,7 @@ nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
return NS_OK;
}
nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
nsresult nsRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
{
if (!outRange) return NS_ERROR_NULL_POINTER;
nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
@ -644,22 +764,22 @@ nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
class nsAutoReplaceContainerSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
nsIDOMNode *mOriginalNode;
nsIDOMNode *mNewNode;
public:
nsAutoReplaceContainerSelNotify(nsSelectionState *aSelState, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mSel(aSelState)
nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mRU(aRangeUpdater)
,mOriginalNode(aOriginalNode)
,mNewNode(aNewNode)
{
if (mSel) mSel->WillReplaceContainer();
mRU.WillReplaceContainer();
}
~nsAutoReplaceContainerSelNotify()
{
if (mSel) mSel->DidReplaceContainer(mOriginalNode, mNewNode);
mRU.DidReplaceContainer(mOriginalNode, mNewNode);
}
};
@ -672,30 +792,30 @@ class nsAutoReplaceContainerSelNotify
class nsAutoRemoveContainerSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
nsIDOMNode *mNode;
nsIDOMNode *mParent;
PRInt32 mOffset;
PRUint32 mNodeOrigLen;
public:
nsAutoRemoveContainerSelNotify(nsSelectionState *aSelState,
nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRUint32 aNodeOrigLen) :
mSel(aSelState)
mRU(aRangeUpdater)
,mNode(aNode)
,mParent(aParent)
,mOffset(aOffset)
,mNodeOrigLen(aNodeOrigLen)
{
if (mSel) mSel->WillRemoveContainer();
mRU.WillRemoveContainer();
}
~nsAutoRemoveContainerSelNotify()
{
if (mSel) mSel->DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
}
};
@ -707,18 +827,18 @@ class nsAutoRemoveContainerSelNotify
class nsAutoInsertContainerSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
public:
nsAutoInsertContainerSelNotify(nsSelectionState *aSelState) :
mSel(aSelState)
nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
mRU(aRangeUpdater)
{
if (mSel) mSel->WillInsertContainer();
mRU.WillInsertContainer();
}
~nsAutoInsertContainerSelNotify()
{
if (mSel) mSel->DidInsertContainer();
mRU.DidInsertContainer();
}
};
@ -731,30 +851,30 @@ class nsAutoInsertContainerSelNotify
class nsAutoMoveNodeSelNotify
{
private:
nsSelectionState *mSel;
nsRangeUpdater &mRU;
nsIDOMNode *mOldParent;
nsIDOMNode *mNewParent;
PRInt32 mOldOffset;
PRInt32 mNewOffset;
public:
nsAutoMoveNodeSelNotify(nsSelectionState *aSelState,
nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aOldParent,
PRInt32 aOldOffset,
nsIDOMNode *aNewParent,
PRInt32 aNewOffset) :
mSel(aSelState)
mRU(aRangeUpdater)
,mOldParent(aOldParent)
,mNewParent(aNewParent)
,mOldOffset(aOldOffset)
,mNewOffset(aNewOffset)
{
if (mSel) mSel->WillMoveNode();
mRU.WillMoveNode();
}
~nsAutoMoveNodeSelNotify()
{
if (mSel) mSel->DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
}
};
@ -774,7 +894,8 @@ nsEditor::nsEditor()
, mPlaceHolderName(nsnull)
, mPlaceHolderBatch(0)
, mSelState(nsnull)
, mSavedSel(nsnull)
, mSavedSel()
, mRangeUpdater()
, mShouldTxnSetSelection(PR_TRUE)
, mBodyElement(nsnull)
, mInIMEMode(PR_FALSE)
@ -1595,7 +1716,7 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjCreateNode(aParent, aPosition);
mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
if (mActionListeners)
{
@ -1637,7 +1758,7 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjInsertNode(aParent, aPosition);
mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
if (mActionListeners)
{
@ -1686,7 +1807,7 @@ nsEditor::SplitNode(nsIDOMNode * aNode,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
if (mActionListeners)
{
@ -1743,7 +1864,7 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
if (mActionListeners)
{
@ -1789,7 +1910,7 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mSavedSel) mSavedSel->SelAdjDeleteNode(aElement, parent, offset);
mRangeUpdater.SelAdjDeleteNode(aElement, parent, offset);
if (mActionListeners)
{
@ -1856,8 +1977,8 @@ nsEditor::ReplaceContainer(nsIDOMNode *inNode,
// notify our internal selection state listener
// (Note: A nsAutoSelectionReset object must be created
// before calling this to initialize mSavedSel)
nsAutoReplaceContainerSelNotify selStateNotify(mSavedSel, inNode, *outNode);
// before calling this to initialize mRangeUpdater)
nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, *outNode);
nsCOMPtr<nsIDOMNode> child;
PRBool bHasMoreChildren;
@ -1908,7 +2029,7 @@ nsEditor::RemoveContainer(nsIDOMNode *inNode)
nodeList->GetLength(&nodeOrigLen);
// notify our internal selection state listener
nsAutoRemoveContainerSelNotify selNotify(mSavedSel, inNode, parent, offset, nodeOrigLen);
nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, inNode, parent, offset, nodeOrigLen);
nsCOMPtr<nsIDOMNode> child;
while (bHasMoreChildren)
@ -1970,7 +2091,7 @@ nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
}
// notify our internal selection state listener
nsAutoInsertContainerSelNotify selNotify(mSavedSel);
nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
// put inNode in new parent, outNode
res = DeleteNode(inNode);
@ -2009,7 +2130,7 @@ nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK;
// notify our internal selection state listener
nsAutoMoveNodeSelNotify selNotify(mSavedSel, oldParent, oldOffset, aParent, aOffset);
nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset, aParent, aOffset);
// need to adjust aOffset if we are moving aNode further along in it's current parent
if ((aParent == oldParent.get()) && (oldOffset < aOffset))
@ -2250,6 +2371,44 @@ nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
return NS_OK;
}
#ifdef XP_MAC
#pragma mark -
#pragma mark support for selection preservation
#pragma mark -
#endif
PRBool
nsEditor::ArePreservingSelection()
{
if (mSavedSel.IsEmpty()) return PR_FALSE;
return PR_TRUE;
}
nsresult
nsEditor::PreserveSelectionAcrossActions(nsIDOMSelection *aSel)
{
mSavedSel.SaveSelection(aSel);
mRangeUpdater.RegisterSelectionState(mSavedSel);
return NS_OK;
}
nsresult
nsEditor::RestorePreservedSelection(nsIDOMSelection *aSel)
{
if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE;
mSavedSel.RestoreSelection(aSel);
StopPreservingSelection();
return NS_OK;
}
void
nsEditor::StopPreservingSelection()
{
mRangeUpdater.DropSelectionState(mSavedSel);
mSavedSel.MakeEmpty();
}
#ifdef XP_MAC
#pragma mark -
#pragma mark nsIEditorIMESupport
@ -4985,6 +5144,7 @@ nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
nsIDOMNode *aSplitPointParent,
PRInt32 aSplitPointOffset,
PRInt32 *outOffset,
PRBool aNoEmptyContainers,
nsCOMPtr<nsIDOMNode> *outLeftNode,
nsCOMPtr<nsIDOMNode> *outRightNode)
{
@ -5006,12 +5166,12 @@ nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
// this nsEditor routine.
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
PRUint32 textLen=0;
if (nodeAsText)
nodeAsText->GetLength(&textLen);
PRUint32 len;
PRBool bDoSplit = PR_FALSE;
res = GetLengthOfDOMNode(nodeToSplit, len);
if (NS_FAILED(res)) return res;
if (!nodeAsText || (offset && (offset != (PRInt32)textLen)))
if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (PRInt32)len)))
{
bDoSplit = PR_TRUE;
res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));

View File

@ -80,8 +80,10 @@ class nsISelectionController;
*/
// first a helper struct for saving/setting ranges
struct SelRangeStore
struct nsRangeStore
{
nsRangeStore();
~nsRangeStore();
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
@ -89,6 +91,7 @@ struct SelRangeStore
PRInt32 startOffset;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
// DEBUG: static PRInt32 n;
};
class nsSelectionState
@ -104,6 +107,26 @@ class nsSelectionState
PRBool IsEqual(nsSelectionState *aSelState);
void MakeEmpty();
PRBool IsEmpty();
protected:
nsVoidArray mArray;
friend class nsRangeUpdater;
};
class nsRangeUpdater
{
public:
nsRangeUpdater();
~nsRangeUpdater();
void* RegisterRange(nsIDOMRange *aRange);
nsCOMPtr<nsIDOMRange> ReclaimRange(void *aCookie);
void DropRange(void *aCookie);
void RegisterRangeItem(nsRangeStore *aRangeItem);
void DropRangeItem(nsRangeStore *aRangeItem);
nsresult RegisterSelectionState(nsSelectionState &aSelState);
nsresult DropSelectionState(nsSelectionState &aSelState);
// editor selection gravity routines. Note that we can't always depend on
// DOM Range gravity to do what we want to the "real" selection. For instance,
@ -131,7 +154,7 @@ class nsSelectionState
nsresult DidInsertContainer();
nsresult WillMoveNode();
nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
protected:
nsVoidArray mArray;
PRBool mLock;
};
@ -483,6 +506,14 @@ public:
* with a call to EndOperation */
NS_IMETHOD EndOperation();
/** routines for managing the preservation of selection across
* various editor actions */
PRBool ArePreservingSelection();
nsresult PreserveSelectionAcrossActions(nsIDOMSelection *aSel);
nsresult RestorePreservedSelection(nsIDOMSelection *aSel);
void StopPreservingSelection();
/** return the string that represents text nodes in the content tree */
static nsresult GetTextNodeTag(nsString& aOutString);
@ -725,6 +756,7 @@ public:
nsIDOMNode *aSplitPointParent,
PRInt32 aSplitPointOffset,
PRInt32 *outOffset,
PRBool aNoEmptyContainers = PR_FALSE,
nsCOMPtr<nsIDOMNode> *outLeftNode = 0,
nsCOMPtr<nsIDOMNode> *outRightNode = 0);
nsresult JoinNodeDeep(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsCOMPtr<nsIDOMNode> *aOutJoinNode, PRInt32 *outOffset);
@ -748,15 +780,16 @@ protected:
nsCOMPtr<nsITransactionManager> mTxnMgr;
nsCOMPtr<nsIEditProperty> mEditProperty;
nsCOMPtr<nsICSSStyleSheet> mLastStyleSheet; // is owning this dangerous?
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsIAtom *mPlaceHolderName; // name of placeholder transaction
PRInt32 mPlaceHolderBatch; // nesting count for batching
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
nsSelectionState *mSavedSel; // cached selection for nsAutoSelectionReset
PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsIAtom *mPlaceHolderName; // name of placeholder transaction
PRInt32 mPlaceHolderBatch; // nesting count for batching
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
nsSelectionState mSavedSel; // cached selection for nsAutoSelectionReset
nsRangeUpdater mRangeUpdater; // utility class object for maintaining preserved ranges
PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
nsCOMPtr<nsIDOMElement> mBodyElement; // cached body node
PRInt32 mAction; // the current editor action
EDirection mDirection; // the current direction of editor action
PRInt32 mAction; // the current editor action
EDirection mDirection; // the current direction of editor action
// data necessary to build IME transactions
PRBool mInIMEMode; // are we inside an IME composition?

View File

@ -23,6 +23,9 @@
#include "nsEditorUtils.h"
#include "nsIDOMDocument.h"
#include "nsIDOMRange.h"
#include "nsIContent.h"
#include "nsLayoutCID.h"
/******************************************************************************
@ -34,24 +37,162 @@ mSel(nsnull)
,mEd(nsnull)
{
if (!aSel || !aEd) return; // not much we can do, bail.
if (aEd->mSavedSel) return; // we already have initted mSavedSel, so this must be nested call.
if (aEd->ArePreservingSelection()) return; // we already have initted mSavedSel, so this must be nested call.
mSel = do_QueryInterface(aSel);
mEd = aEd;
if (mSel)
{
mEd->mSavedSel = new nsSelectionState();
mEd->mSavedSel->SaveSelection(mSel);
mEd->PreserveSelectionAcrossActions(mSel);
}
}
nsAutoSelectionReset::~nsAutoSelectionReset()
{
if (mSel && mEd->mSavedSel) // mSel will be null if this was nested call
if (mSel && mEd->ArePreservingSelection()) // mSel will be null if this was nested call
{
mEd->mSavedSel->RestoreSelection(mSel);
delete mEd->mSavedSel;
mEd->mSavedSel = nsnull;
mEd->RestorePreservedSelection(mSel);
}
}
void
nsAutoSelectionReset::Abort()
{
mEd->StopPreservingSelection();
}
/******************************************************************************
* some helper classes for iterating the dom tree
*****************************************************************************/
static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
static NS_DEFINE_IID(kContentIteratorCID, NS_CONTENTITERATOR_CID);
nsDOMIterator::nsDOMIterator() :
mIter(nsnull)
{
}
nsDOMIterator::~nsDOMIterator()
{
}
nsresult
nsDOMIterator::Init(nsIDOMRange* aRange)
{
nsresult res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
return mIter->Init(aRange);
}
nsresult
nsDOMIterator::Init(nsIDOMNode* aNode)
{
nsresult res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
return mIter->Init(content);
}
void
nsDOMIterator::ForEach(nsDomIterFunctor& functor) const
{
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
nsresult res;
// iterate through dom
while (NS_ENUMERATOR_FALSE == mIter->IsDone())
{
res = mIter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return;
node = do_QueryInterface(content);
if (!node) return;
functor(node);
res = mIter->Next();
if (NS_FAILED(res)) return;
}
}
nsresult
nsDOMIterator::MakeList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes) const
{
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
nsresult res;
// make a array
res = NS_NewISupportsArray(getter_AddRefs(*outArrayOfNodes));
if (NS_FAILED(res)) return res;
return AppendList(functor, *outArrayOfNodes);
}
nsresult
nsDOMIterator::AppendList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> arrayOfNodes) const
{
if (!arrayOfNodes) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
nsresult res;
// iterate through dom and build list
while (NS_ENUMERATOR_FALSE == mIter->IsDone())
{
res = mIter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return res;
node = do_QueryInterface(content);
if (!node) return NS_ERROR_NULL_POINTER;
if (functor(node))
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
}
res = mIter->Next();
if (NS_FAILED(res)) return res;
}
return NS_OK;
}
nsDOMSubtreeIterator::nsDOMSubtreeIterator()
{
}
nsDOMSubtreeIterator::~nsDOMSubtreeIterator()
{
}
nsresult
nsDOMSubtreeIterator::Init(nsIDOMRange* aRange)
{
nsresult res = nsComponentManager::CreateInstance(kSubtreeIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
return mIter->Init(aRange);
}
nsresult
nsDOMSubtreeIterator::Init(nsIDOMNode* aNode)
{
nsresult res = nsComponentManager::CreateInstance(kSubtreeIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIter));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
return mIter->Init(content);
}

View File

@ -32,6 +32,7 @@
#include "nsIAtom.h"
#include "nsVoidArray.h"
#include "nsEditor.h"
#include "nsIContentIterator.h"
/***************************************************************************
* stack based helper class for batching a collection of txns inside a
@ -77,6 +78,9 @@ class nsAutoSelectionReset
/** destructor restores mSel to its former state */
~nsAutoSelectionReset();
/** Abort: cancel selection saver */
void Abort();
};
/***************************************************************************
@ -139,5 +143,47 @@ class nsAutoTxnsConserveSelection
PRBool mOldState;
};
/******************************************************************************
* some helper classes for iterating the dom tree
*****************************************************************************/
class nsDomIterFunctor
{
public:
virtual void* operator()(nsIDOMNode* aNode)=0;
};
class nsBoolDomIterFunctor
{
public:
virtual PRBool operator()(nsIDOMNode* aNode)=0;
};
class nsDOMIterator
{
public:
nsDOMIterator();
virtual ~nsDOMIterator();
nsresult Init(nsIDOMRange* aRange);
nsresult Init(nsIDOMNode* aNode);
void ForEach(nsDomIterFunctor& functor) const;
nsresult MakeList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes) const;
nsresult AppendList(nsBoolDomIterFunctor& functor,
nsCOMPtr<nsISupportsArray> arrayOfNodes) const;
protected:
nsCOMPtr<nsIContentIterator> mIter;
};
class nsDOMSubtreeIterator : public nsDOMIterator
{
public:
nsDOMSubtreeIterator();
virtual ~nsDOMSubtreeIterator();
nsresult Init(nsIDOMRange* aRange);
nsresult Init(nsIDOMNode* aNode);
};
#endif // nsEditorUtils_h__

View File

@ -122,14 +122,14 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsStr
}
// if it's already set we are done
if (IsPropSet(aProp,aAttr,aValue)) return NS_OK;
if (IsPropSet(aProp,aAttr,nsnull)) return NS_OK;
// make a new propitem
PropItem *item = new PropItem(aProp,aAttr,aValue);
if (!item) return NS_ERROR_OUT_OF_MEMORY;
// remove it from the list of cleared properties, if we have a match
RemovePropFromClearedList(aProp,aAttr,aValue);
RemovePropFromClearedList(aProp,aAttr);
// add it to the list of set properties
mSetArray.AppendElement((void*)item);
@ -141,30 +141,25 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsStr
nsresult TypeInState::ClearAllProps()
{
// null prop means "all" props
return ClearProp(nsnull,nsAutoString(),nsAutoString());
return ClearProp(nsnull,nsAutoString());
}
nsresult TypeInState::ClearProp(nsIAtom *aProp)
{
return ClearProp(aProp,nsAutoString(),nsAutoString());
return ClearProp(aProp,nsAutoString());
}
nsresult TypeInState::ClearProp(nsIAtom *aProp, const nsString &aAttr)
{
return ClearProp(aProp,aAttr,nsAutoString());
}
nsresult TypeInState::ClearProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue)
{
// if it's already cleared we are done
if (IsPropCleared(aProp,aAttr,aValue)) return NS_OK;
if (IsPropCleared(aProp,aAttr)) return NS_OK;
// make a new propitem
PropItem *item = new PropItem(aProp,aAttr,aValue);
PropItem *item = new PropItem(aProp,aAttr,nsAutoString());
if (!item) return NS_ERROR_OUT_OF_MEMORY;
// remove it from the list of set properties, if we have a match
RemovePropFromSetList(aProp,aAttr,aValue);
RemovePropFromSetList(aProp,aAttr);
// add it to the list of cleared properties
mClearedArray.AppendElement((void*)item);
@ -222,7 +217,7 @@ nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize)
nsresult TypeInState::GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp)
{
return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsAutoString());
return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsnull);
}
nsresult TypeInState::GetTypingState(PRBool &isSet,
@ -230,7 +225,7 @@ nsresult TypeInState::GetTypingState(PRBool &isSet,
nsIAtom *aProp,
const nsString &aAttr)
{
return GetTypingState(isSet, theSetting, aProp, aAttr, nsAutoString());
return GetTypingState(isSet, theSetting, aProp, aAttr, nsnull);
}
@ -238,14 +233,14 @@ nsresult TypeInState::GetTypingState(PRBool &isSet,
PRBool &theSetting,
nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
nsString *aValue)
{
if (IsPropSet(aProp, aAttr, aValue))
{
isSet = PR_TRUE;
theSetting = PR_TRUE;
}
else if (IsPropCleared(aProp, aAttr, aValue))
else if (IsPropCleared(aProp, aAttr))
{
isSet = PR_TRUE;
theSetting = PR_FALSE;
@ -264,8 +259,7 @@ nsresult TypeInState::GetTypingState(PRBool &isSet,
*******************************************************************/
nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
const nsString &aAttr)
{
PRInt32 index;
PropItem *item;
@ -282,7 +276,7 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
if (item) delete item;
}
}
else if (FindPropInList(aProp, aAttr, aValue, mSetArray, index))
else if (FindPropInList(aProp, aAttr, nsnull, mSetArray, index))
{
item = (PropItem*)mSetArray.ElementAt(index);
mSetArray.RemoveElementAt(index);
@ -293,11 +287,10 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
nsresult TypeInState::RemovePropFromClearedList(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
const nsString &aAttr)
{
PRInt32 index;
if (FindPropInList(aProp, aAttr, aValue, mClearedArray, index))
if (FindPropInList(aProp, aAttr, nsnull, mClearedArray, index))
{
PropItem *item = (PropItem*)mClearedArray.ElementAt(index);
mClearedArray.RemoveElementAt(index);
@ -309,16 +302,16 @@ nsresult TypeInState::RemovePropFromClearedList(nsIAtom *aProp,
PRBool TypeInState::IsPropSet(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
nsString* outValue)
{
PRInt32 i;
return IsPropSet(aProp, aAttr, aValue, i);
return IsPropSet(aProp, aAttr, outValue, i);
}
PRBool TypeInState::IsPropSet(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue,
nsString *outValue,
PRInt32 &outIndex)
{
// linear search. list should be short.
@ -329,6 +322,7 @@ PRBool TypeInState::IsPropSet(nsIAtom *aProp,
if ( (item->tag == aProp) &&
(item->attr == aAttr) )
{
if (outValue) *outValue = item->value;
outIndex = i;
return PR_TRUE;
}
@ -338,22 +332,20 @@ PRBool TypeInState::IsPropSet(nsIAtom *aProp,
PRBool TypeInState::IsPropCleared(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue)
const nsString &aAttr)
{
PRInt32 i;
return IsPropCleared(aProp, aAttr, aValue, i);
return IsPropCleared(aProp, aAttr, i);
}
PRBool TypeInState::IsPropCleared(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue,
PRInt32 &outIndex)
{
if (FindPropInList(aProp, aAttr, aValue, mClearedArray, outIndex))
if (FindPropInList(aProp, aAttr, nsnull, mClearedArray, outIndex))
return PR_TRUE;
if (FindPropInList(0, nsAutoString(), nsAutoString(), mClearedArray, outIndex))
if (FindPropInList(0, nsAutoString(), nsnull, mClearedArray, outIndex))
{
// special case for all props cleared
outIndex = -1;
@ -364,7 +356,7 @@ PRBool TypeInState::IsPropCleared(nsIAtom *aProp,
PRBool TypeInState::FindPropInList(nsIAtom *aProp,
const nsString &aAttr,
const nsString &aValue,
nsString *outValue,
nsVoidArray &aList,
PRInt32 &outIndex)
{
@ -376,6 +368,7 @@ PRBool TypeInState::FindPropInList(nsIAtom *aProp,
if ( (item->tag == aProp) &&
(item->attr == aAttr) )
{
if (outValue) *outValue = item->value;
outIndex = i;
return PR_TRUE;
}

View File

@ -57,7 +57,6 @@ public:
nsresult ClearAllProps();
nsresult ClearProp(nsIAtom *aProp);
nsresult ClearProp(nsIAtom *aProp, const nsString &aAttr);
nsresult ClearProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
//**************************************************************************
// TakeClearProperty: hands back next poroperty item on the clear list.
@ -78,17 +77,17 @@ public:
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp,
const nsString &aAttr);
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp,
const nsString &aAttr, const nsString &aValue);
const nsString &aAttr, nsString* outValue);
protected:
nsresult RemovePropFromSetList(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
nsresult RemovePropFromClearedList(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue, PRInt32 &outIndex);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue, PRInt32 &outIndex);
PRBool FindPropInList(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue, nsVoidArray &aList, PRInt32 &outIndex);
nsresult RemovePropFromSetList(nsIAtom *aProp, const nsString &aAttr);
nsresult RemovePropFromClearedList(nsIAtom *aProp, const nsString &aAttr);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, nsString* outValue);
PRBool IsPropSet(nsIAtom *aProp, const nsString &aAttr, nsString* outValue, PRInt32 &outIndex);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr);
PRBool IsPropCleared(nsIAtom *aProp, const nsString &aAttr, PRInt32 &outIndex);
PRBool FindPropInList(nsIAtom *aProp, const nsString &aAttr, nsString *outValue, nsVoidArray &aList, PRInt32 &outIndex);
nsVoidArray mSetArray;
nsVoidArray mClearedArray;

File diff suppressed because it is too large Load Diff

View File

@ -109,8 +109,8 @@ protected:
nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult DidMakeBasicBlock(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
nsresult AlignTableElement(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignTableCellContents(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType);
nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE);
nsresult InsertTab(nsIDOMSelection *aSelection, nsString *outString);
@ -146,12 +146,13 @@ protected:
nsresult GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBool &aDD);
nsresult GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult BustUpInlinesAtRangeEndpoints(nsRangeStore &inRange);
nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsCOMPtr<nsIDOMNode> GetHighestInlineParent(nsIDOMNode* aNode);
nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes,
nsVoidArray *inTransitionArray);
nsresult ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, const nsString *blockTag, PRBool *outMakeEmpty);
nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes);
nsresult SplitAsNeeded(const nsString *aTag, nsCOMPtr<nsIDOMNode> *inOutParent, PRInt32 *inOutOffset);

View File

@ -42,6 +42,7 @@ nsHTMLEditUtils::IsBody(nsIDOMNode *node)
{
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag.EqualsWithConversion("body"))
{
return PR_TRUE;
@ -573,4 +574,11 @@ nsHTMLEditUtils::IsDescendantOf(nsIDOMNode *aNode, nsIDOMNode *aParent)
}
PRBool
nsHTMLEditUtils::IsLeafNode(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
PRBool hasChildren = PR_FALSE;
aNode->HasChildNodes(&hasChildren);
return !hasChildren;
}

View File

@ -63,6 +63,9 @@ public:
static PRBool IsMozDiv(nsIDOMNode *aNode);
static PRBool IsMailCite(nsIDOMNode *aNode);
static PRBool IsDescendantOf(nsIDOMNode *aNode, nsIDOMNode *aParent);
static PRBool IsLeafNode(nsIDOMNode *aNode);
};
#endif /* nsHTMLEditUtils_h__ */

View File

@ -1777,6 +1777,16 @@ NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode);
// cache now current, use it! But override it with typeInState results if any...
PRBool isSet, theSetting;
if (aAttribute)
mTypeInState->GetTypingState(isSet, theSetting, aProperty, *aAttribute, outValue);
else
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
if (isSet)
{
aFirst = aAny = aAll = theSetting;
return NS_OK;
}
/*
if (aProperty == mBoldAtom.get())
{
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
@ -1815,7 +1825,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
aFirst = aAny = aAll = mCachedUnderlineStyle;
}
return NS_OK;
}
} */
}
// either non-collapsed selection or no cached value: do it the hard way
@ -7102,7 +7112,7 @@ nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<
///////////////////////////////////////////////////////////////////////////
// GetNextHTMLNode: returns the previous editable leaf node, if there is
// GetNextHTMLNode: returns the next editable leaf node, if there is
// one within the <body>
//
nsresult
@ -7239,6 +7249,75 @@ nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOu
return res;
}
nsresult
nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf)
{
// check parms
if (!aOutFirstLeaf || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutFirstLeaf = nsnull;
// find leftmost leaf
nsCOMPtr<nsIDOMNode> child;
nsresult res = GetLeftmostChild(aNode, getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && (!IsEditable(child) || !nsHTMLEditUtils::IsLeafNode(child)))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetNextHTMLNode(child, &tmp);
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
// only accept nodes that are descendants of aNode
if (nsHTMLEditUtils::IsDescendantOf(tmp, aNode))
child = tmp;
else
{
child = nsnull; // this will abort the loop
}
}
*aOutFirstLeaf = child;
return res;
}
nsresult
nsHTMLEditor::GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf)
{
// check parms
if (!aOutLastLeaf || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutLastLeaf = nsnull;
// find leftmost leaf
nsCOMPtr<nsIDOMNode> child;
nsresult res = GetRightmostChild(aNode, getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && (!IsEditable(child) || !nsHTMLEditUtils::IsLeafNode(child)))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetPriorHTMLNode(child, &tmp);
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
// only accept nodes that are descendants of aNode
if (nsHTMLEditUtils::IsDescendantOf(tmp, aNode))
child = tmp;
else
{
child = nsnull;
}
}
*aOutLastLeaf = child;
return res;
}
///////////////////////////////////////////////////////////////////////////
// IsEmptyNode: figure out if aNode is an empty node.
// A block can have children and still be considered empty,

View File

@ -332,6 +332,11 @@ public:
// aSelection is optional -- if null, we get current seletion
nsresult CollapseSelectionToDeepestNonTableFirstChild(nsIDOMSelection *aSelection, nsIDOMNode *aNode);
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListOrCellNotEmpty = PR_FALSE,
PRBool aSafeToAskFrames = PR_FALSE);
protected:
NS_IMETHOD InitRules();
@ -563,10 +568,8 @@ protected:
nsresult IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast);
nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild);
nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild);
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListOrCellNotEmpty = PR_FALSE,
PRBool aSafeToAskFrames = PR_FALSE);
nsresult GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf);
nsresult GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf);
nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver);

View File

@ -477,7 +477,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P
printf("It's a moz quote -- splitting\n");
nsCOMPtr<nsIDOMNode> outLeftNode;
nsCOMPtr<nsIDOMNode> outRightNode;
res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, &outLeftNode, &outRightNode);
res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, PR_TRUE, &outLeftNode, &outRightNode);
if (NS_FAILED(res)) return res;
PRBool bIsEmptyNode;