зеркало из https://github.com/mozilla/gecko-dev.git
fix for 178264: nsRangeUpdater bugs and enhancements. precursor to 143338 landing. r=brade; sr=kin
This commit is contained in:
Родитель
be10466ef4
Коммит
12635e7ebd
|
@ -40,6 +40,7 @@
|
|||
#include "nsCRT.h"
|
||||
|
||||
#include "DeleteElementTxn.h"
|
||||
#include "nsSelectionState.h"
|
||||
#ifdef NS_DEBUG
|
||||
#include "nsIDOMElement.h"
|
||||
#endif
|
||||
|
@ -52,17 +53,20 @@ static const PRBool gNoisy = PR_FALSE;
|
|||
|
||||
|
||||
DeleteElementTxn::DeleteElementTxn()
|
||||
: EditTxn()
|
||||
: EditTxn()
|
||||
,mElement()
|
||||
,mParent()
|
||||
,mRefNode()
|
||||
,mRangeUpdater(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DeleteElementTxn::Init(nsIDOMNode *aElement)
|
||||
NS_IMETHODIMP DeleteElementTxn::Init(nsIDOMNode *aElement,
|
||||
nsRangeUpdater *aRangeUpdater)
|
||||
{
|
||||
if (nsnull!=aElement) {
|
||||
mElement = do_QueryInterface(aElement);
|
||||
}
|
||||
else
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
if (!aElement) return NS_ERROR_NULL_POINTER;
|
||||
mElement = do_QueryInterface(aElement);
|
||||
mRangeUpdater = aRangeUpdater;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -108,8 +112,14 @@ NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
|
|||
// remember which child mElement was (by remembering which child was next)
|
||||
result = mElement->GetNextSibling(getter_AddRefs(mRefNode)); // can return null mRefNode
|
||||
|
||||
// give range updater a chance. SelAdjDeleteNode() needs to be called *before*
|
||||
// we do the action, unlike some of the other nsRangeStore update methods.
|
||||
if (mRangeUpdater)
|
||||
mRangeUpdater->SelAdjDeleteNode(mElement);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> resultNode;
|
||||
result = mParent->RemoveChild(mElement, getter_AddRefs(resultNode));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -155,6 +165,9 @@ NS_IMETHODIMP DeleteElementTxn::RedoTransaction(void)
|
|||
if (!mParent) { return NS_OK; } // this is a legal state, the txn is a no-op
|
||||
if (!mElement) { return NS_ERROR_NULL_POINTER; }
|
||||
|
||||
if (mRangeUpdater)
|
||||
mRangeUpdater->SelAdjDeleteNode(mElement);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> resultNode;
|
||||
nsresult result = mParent->RemoveChild(mElement, getter_AddRefs(resultNode));
|
||||
return result;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#define DeleteElementTxn_h__
|
||||
|
||||
#include "EditTxn.h"
|
||||
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
|
@ -48,6 +49,8 @@
|
|||
0x6fd77770, 0xac49, 0x11d2, \
|
||||
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
|
||||
|
||||
class nsRangeUpdater;
|
||||
|
||||
/**
|
||||
* A transaction that deletes a single element
|
||||
*/
|
||||
|
@ -60,7 +63,7 @@ public:
|
|||
/** initialize the transaction.
|
||||
* @param aElement the node to delete
|
||||
*/
|
||||
NS_IMETHOD Init(nsIDOMNode *aElement);
|
||||
NS_IMETHOD Init(nsIDOMNode *aElement, nsRangeUpdater *aRangeUpdater);
|
||||
|
||||
private:
|
||||
DeleteElementTxn();
|
||||
|
@ -84,15 +87,15 @@ protected:
|
|||
/** the element to delete */
|
||||
nsCOMPtr<nsIDOMNode> mElement;
|
||||
|
||||
/** the node into which the new node will be inserted */
|
||||
/** parent of node to delete */
|
||||
nsCOMPtr<nsIDOMNode> mParent;
|
||||
|
||||
/** the index in mParent for the new node */
|
||||
PRUint32 mOffsetInParent;
|
||||
|
||||
/** the node we will insert mNewNode before. We compute this ourselves. */
|
||||
/** next sibling to remember for undo/redo purposes */
|
||||
nsCOMPtr<nsIDOMNode> mRefNode;
|
||||
|
||||
/** range updater object */
|
||||
nsRangeUpdater *mRangeUpdater;
|
||||
|
||||
friend class TransactionFactory;
|
||||
|
||||
};
|
||||
|
|
|
@ -61,17 +61,28 @@ static const PRBool gNoisy = PR_FALSE;
|
|||
|
||||
// note that aEditor is not refcounted
|
||||
DeleteRangeTxn::DeleteRangeTxn()
|
||||
: EditAggregateTxn()
|
||||
: EditAggregateTxn()
|
||||
,mRange()
|
||||
,mStartParent()
|
||||
,mStartOffset(0)
|
||||
,mEndParent()
|
||||
,mCommonParent()
|
||||
,mEndOffset(0)
|
||||
,mEditor(nsnull)
|
||||
,mRangeUpdater(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DeleteRangeTxn::Init(nsIEditor *aEditor, nsIDOMRange *aRange)
|
||||
NS_IMETHODIMP DeleteRangeTxn::Init(nsIEditor *aEditor,
|
||||
nsIDOMRange *aRange,
|
||||
nsRangeUpdater *aRangeUpdater)
|
||||
{
|
||||
NS_ASSERTION(aEditor && aRange, "bad state");
|
||||
if (!aEditor || !aRange) { return NS_ERROR_NOT_INITIALIZED; }
|
||||
|
||||
mEditor = aEditor;
|
||||
mRange = do_QueryInterface(aRange);
|
||||
mRangeUpdater = aRangeUpdater;
|
||||
|
||||
nsresult result = aRange->GetStartContainer(getter_AddRefs(mStartParent));
|
||||
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed.");
|
||||
|
@ -235,7 +246,7 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
|
|||
numToDel = 1;
|
||||
else
|
||||
numToDel = aEndOffset-aStartOffset;
|
||||
txn->Init(mEditor, textNode, aStartOffset, numToDel);
|
||||
txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater);
|
||||
AppendChild(txn);
|
||||
NS_RELEASE(txn);
|
||||
}
|
||||
|
@ -262,7 +273,7 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
|
|||
if (NS_FAILED(result)) return result;
|
||||
if (!txn) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
txn->Init(child);
|
||||
txn->Init(child, mRangeUpdater);
|
||||
AppendChild(txn);
|
||||
NS_RELEASE(txn);
|
||||
}
|
||||
|
@ -300,7 +311,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent,
|
|||
if (NS_FAILED(result)) return result;
|
||||
if (!txn) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
txn->Init(mEditor, textNode, start, numToDelete);
|
||||
txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater);
|
||||
AppendChild(txn);
|
||||
NS_RELEASE(txn);
|
||||
}
|
||||
|
@ -341,7 +352,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween()
|
|||
if (NS_FAILED(result)) return result;
|
||||
if (!txn) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
txn->Init(node);
|
||||
txn->Init(node, mRangeUpdater);
|
||||
AppendChild(txn);
|
||||
NS_RELEASE(txn);
|
||||
iter->Next();
|
||||
|
|
|
@ -54,6 +54,7 @@ class nsIDOMDocument;
|
|||
|
||||
class nsIDOMRange;
|
||||
class nsIEditor;
|
||||
class nsRangeUpdater;
|
||||
|
||||
/**
|
||||
* A transaction that deletes an entire range in the content tree
|
||||
|
@ -68,7 +69,9 @@ public:
|
|||
* @param aEditor the object providing basic editing operations
|
||||
* @param aRange the range to delete
|
||||
*/
|
||||
NS_IMETHOD Init(nsIEditor *aEditor, nsIDOMRange *aRange);
|
||||
NS_IMETHOD Init(nsIEditor *aEditor,
|
||||
nsIDOMRange *aRange,
|
||||
nsRangeUpdater *aRangeUpdater);
|
||||
|
||||
private:
|
||||
DeleteRangeTxn();
|
||||
|
@ -122,6 +125,9 @@ protected:
|
|||
/** the editor for this transaction */
|
||||
nsIEditor* mEditor;
|
||||
|
||||
/** range updater object */
|
||||
nsRangeUpdater *mRangeUpdater;
|
||||
|
||||
friend class TransactionFactory;
|
||||
|
||||
};
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "DeleteTextTxn.h"
|
||||
#include "nsIDOMCharacterData.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsSelectionState.h"
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
static PRBool gNoisy = PR_FALSE;
|
||||
|
@ -47,7 +48,13 @@ static const PRBool gNoisy = PR_FALSE;
|
|||
#endif
|
||||
|
||||
DeleteTextTxn::DeleteTextTxn()
|
||||
: EditTxn()
|
||||
: EditTxn()
|
||||
,mEditor(nsnull)
|
||||
,mElement()
|
||||
,mOffset(0)
|
||||
,mNumCharsToDelete(0)
|
||||
,mDeletedText(0)
|
||||
,mRangeUpdater(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -58,7 +65,8 @@ DeleteTextTxn::~DeleteTextTxn()
|
|||
NS_IMETHODIMP DeleteTextTxn::Init(nsIEditor *aEditor,
|
||||
nsIDOMCharacterData *aElement,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aNumCharsToDelete)
|
||||
PRUint32 aNumCharsToDelete,
|
||||
nsRangeUpdater *aRangeUpdater)
|
||||
{
|
||||
NS_ASSERTION(aEditor&&aElement, "bad arg");
|
||||
if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; }
|
||||
|
@ -73,6 +81,7 @@ NS_IMETHODIMP DeleteTextTxn::Init(nsIEditor *aEditor,
|
|||
NS_ASSERTION(count>=aNumCharsToDelete, "bad arg, numCharsToDelete. Not enough characters in node");
|
||||
NS_ASSERTION(count>=aOffset+aNumCharsToDelete, "bad arg, numCharsToDelete. Not enough characters in node");
|
||||
mDeletedText.SetLength(0);
|
||||
mRangeUpdater = aRangeUpdater;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -87,6 +96,9 @@ NS_IMETHODIMP DeleteTextTxn::DoTransaction(void)
|
|||
result = mElement->DeleteData(mOffset, mNumCharsToDelete);
|
||||
if (NS_FAILED(result)) return result;
|
||||
|
||||
if (mRangeUpdater)
|
||||
mRangeUpdater->SelAdjDeleteText(mElement, mOffset, mNumCharsToDelete);
|
||||
|
||||
// only set selection to deletion point if editor gives permission
|
||||
PRBool bAdjustSelection;
|
||||
mEditor->ShouldTxnSetSelection(&bAdjustSelection);
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
0x4d3a2720, 0xac49, 0x11d2, \
|
||||
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
|
||||
|
||||
class nsRangeUpdater;
|
||||
|
||||
/**
|
||||
* A transaction that removes text from a content node.
|
||||
*/
|
||||
|
@ -67,7 +69,8 @@ public:
|
|||
NS_IMETHOD Init(nsIEditor *aEditor,
|
||||
nsIDOMCharacterData *aElement,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aNumCharsToDelete);
|
||||
PRUint32 aNumCharsToDelete,
|
||||
nsRangeUpdater *aRangeUpdater);
|
||||
|
||||
private:
|
||||
DeleteTextTxn();
|
||||
|
@ -100,6 +103,9 @@ protected:
|
|||
/** the text that was deleted */
|
||||
nsString mDeletedText;
|
||||
|
||||
/** range updater object */
|
||||
nsRangeUpdater *mRangeUpdater;
|
||||
|
||||
friend class TransactionFactory;
|
||||
|
||||
};
|
||||
|
|
|
@ -1326,8 +1326,6 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
|
|||
// The transaction system (if any) has taken ownwership of txn
|
||||
NS_IF_RELEASE(txn);
|
||||
|
||||
mRangeUpdater.SelAdjDeleteNode(aElement, parent, offset);
|
||||
|
||||
if (mActionListeners)
|
||||
{
|
||||
for (i = 0; i < mActionListeners->Count(); i++)
|
||||
|
@ -2650,8 +2648,6 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
|
|||
|
||||
result = Do(txn);
|
||||
|
||||
mRangeUpdater.SelAdjDeleteText(aElement, aOffset, aLength);
|
||||
|
||||
// let listeners know what happened
|
||||
if (mActionListeners)
|
||||
{
|
||||
|
@ -2679,7 +2675,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement,
|
|||
{
|
||||
result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)aTxn);
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = (*aTxn)->Init(this, aElement, aOffset, aLength);
|
||||
result = (*aTxn)->Init(this, aElement, aOffset, aLength, &mRangeUpdater);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -4641,7 +4637,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement,
|
|||
{
|
||||
result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn);
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = (*aTxn)->Init(aElement);
|
||||
result = (*aTxn)->Init(aElement, &mRangeUpdater);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -4786,7 +4782,7 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction,
|
|||
result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn);
|
||||
if ((NS_SUCCEEDED(result)) && (nsnull!=txn))
|
||||
{
|
||||
txn->Init(this, range);
|
||||
txn->Init(this, range, &mRangeUpdater);
|
||||
(*aTxn)->AppendChild(txn);
|
||||
NS_RELEASE(txn);
|
||||
}
|
||||
|
@ -4814,10 +4810,9 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction,
|
|||
|
||||
//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented
|
||||
NS_IMETHODIMP
|
||||
nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange,
|
||||
nsIEditor::EDirection
|
||||
aAction,
|
||||
EditAggregateTxn *aTxn)
|
||||
nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange,
|
||||
nsIEditor::EDirection aAction,
|
||||
EditAggregateTxn *aTxn)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
PRBool isFirst;
|
||||
|
@ -4852,8 +4847,8 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange,
|
|||
isFirst = (0==offset);
|
||||
isLast = (count==(PRUint32)offset);
|
||||
|
||||
// XXX: if isFirst && isLast, then we'll need to delete the node
|
||||
// as well as the 1 child
|
||||
// XXX: if isFirst && isLast, then we'll need to delete the node
|
||||
// as well as the 1 child
|
||||
|
||||
// build a transaction for deleting the appropriate data
|
||||
// XXX: this has to come from rule section
|
||||
|
|
|
@ -576,6 +576,7 @@ protected:
|
|||
friend class nsAutoTxnsConserveSelection;
|
||||
friend class nsAutoSelectionReset;
|
||||
friend class nsAutoRules;
|
||||
friend class nsRangeUpdater;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -203,51 +203,18 @@ nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
|
|||
|
||||
nsRangeUpdater::~nsRangeUpdater()
|
||||
{
|
||||
// free any items in the array
|
||||
nsRangeStore *item;
|
||||
for (PRInt32 i = mArray.Count()-1; i >= 0; --i)
|
||||
{
|
||||
item = (nsRangeStore*)mArray.ElementAt(i);
|
||||
delete item;
|
||||
}
|
||||
//mArray.Clear(); not really needed
|
||||
// nothing to do, we don't own the items in our array.
|
||||
}
|
||||
|
||||
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(address_of(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;
|
||||
if (mArray.IndexOf(aRangeItem) != -1)
|
||||
{
|
||||
NS_ERROR("tried to register an already registered range");
|
||||
return; // don't register it again. It would get doubly adjusted.
|
||||
}
|
||||
mArray.AppendElement(aRangeItem);
|
||||
return;
|
||||
}
|
||||
|
@ -327,23 +294,28 @@ nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
|
|||
|
||||
|
||||
nsresult
|
||||
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
|
||||
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode)
|
||||
{
|
||||
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;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> parent;
|
||||
PRInt32 offset = 0;
|
||||
nsRangeStore *item;
|
||||
|
||||
nsresult res = nsEditor::GetNodeLocation(aNode, address_of(parent), &offset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
item = (nsRangeStore*)mArray.ElementAt(i);
|
||||
if (!item) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
|
||||
if ((item->startNode.get() == parent) && (item->startOffset > offset))
|
||||
item->startOffset--;
|
||||
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
|
||||
if ((item->endNode.get() == parent) && (item->endOffset > offset))
|
||||
item->endOffset--;
|
||||
}
|
||||
// MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't
|
||||
|
|
|
@ -94,9 +94,6 @@ class nsRangeUpdater
|
|||
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);
|
||||
|
@ -109,7 +106,7 @@ class nsRangeUpdater
|
|||
// which is not what you want if you know you are reinserting it.
|
||||
nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
|
||||
nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
|
||||
nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
|
||||
nsresult SelAdjDeleteNode(nsIDOMNode *aNode);
|
||||
nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
|
||||
nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode,
|
||||
nsIDOMNode *aRightNode,
|
||||
|
|
Загрузка…
Ссылка в новой задаче