fixes for bugs: 16361,12253,15696,15734; r=sfraser

This commit is contained in:
jfrancis%netscape.com 1999-11-01 15:15:35 +00:00
Родитель 69f850d965
Коммит f5c0eec556
12 изменённых файлов: 480 добавлений и 260 удалений

Просмотреть файл

@ -25,36 +25,29 @@ EditAggregateTxn::EditAggregateTxn()
: EditTxn()
{
// base class does this: NS_INIT_REFCNT();
mChildren = new nsVoidArray();
nsresult res = NS_NewISupportsArray(getter_AddRefs(mChildren));
NS_POSTCONDITION(NS_SUCCEEDED(res), "EditAggregateTxn failed in constructor");
SetTransactionDescriptionID( kTransactionID );
/* log description initialized in parent constructor */
}
EditAggregateTxn::~EditAggregateTxn()
{
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
NS_IF_RELEASE(txn);
}
delete mChildren;
}
// nsISupportsArray cleans up array for us at destruct time
}
NS_IMETHODIMP EditAggregateTxn::Do(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
PRUint32 count;
mChildren->Count(&count);
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Do();
if (NS_FAILED(result))
@ -67,14 +60,17 @@ NS_IMETHODIMP EditAggregateTxn::Do(void)
NS_IMETHODIMP EditAggregateTxn::Undo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
PRUint32 count;
mChildren->Count(&count);
// undo goes through children backwards
for (i=count-1; i>=0; i--)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Undo();
if (NS_FAILED(result))
break;
@ -86,13 +82,16 @@ NS_IMETHODIMP EditAggregateTxn::Undo(void)
NS_IMETHODIMP EditAggregateTxn::Redo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
PRUint32 count;
mChildren->Count(&count);
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Redo();
if (NS_FAILED(result))
break;
@ -113,13 +112,17 @@ NS_IMETHODIMP EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransa
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 count = mChildren->Count();
PRInt32 i;
PRUint32 count;
mChildren->Count(&count);
NS_ASSERTION(count>0, "bad count");
if (0<count)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(count-1));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Merge(aDidMerge, aTransaction);
}
}
@ -148,22 +151,25 @@ NS_IMETHODIMP EditAggregateTxn::GetRedoString(nsString *aString)
NS_IMETHODIMP EditAggregateTxn::AppendChild(EditTxn *aTxn)
{
if ((nsnull!=mChildren) && (nsnull!=aTxn))
if (mChildren && aTxn)
{
mChildren->AppendElement(aTxn);
// aaahhhh! broken interfaces drive me crazy!!!
nsCOMPtr<nsISupports> isupports;
aTxn->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(isupports));
mChildren->AppendElement(isupports);
return NS_OK;
}
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP EditAggregateTxn::GetCount(PRInt32 *aCount)
NS_IMETHODIMP EditAggregateTxn::GetCount(PRUint32 *aCount)
{
if (!aCount) {
return NS_ERROR_NULL_POINTER;
}
*aCount=0;
if (mChildren) {
*aCount = mChildren->Count();
mChildren->Count(aCount);
}
return NS_OK;
}
@ -183,14 +189,16 @@ NS_IMETHODIMP EditAggregateTxn::GetTxnAt(PRInt32 aIndex, EditTxn **aTxn)
}
// get the transaction at aIndex
const PRInt32 txnCount = mChildren->Count();
PRUint32 txnCount;
mChildren->Count(&txnCount);
if (0>aIndex || txnCount<=aIndex) {
return NS_ERROR_UNEXPECTED;
}
*aTxn = (EditTxn *)(mChildren->ElementAt(aIndex));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(aIndex));
// ugh, this is all wrong - what a mess we have with editor transaction interfaces
isupports->QueryInterface(EditTxn::GetCID(), (void**)aTxn);
if (!*aTxn)
return NS_ERROR_UNEXPECTED;
NS_ADDREF(*aTxn);
return NS_OK;
}

Просмотреть файл

@ -22,13 +22,13 @@
#include "EditTxn.h"
#include "nsIAtom.h"
#include "nsCOMPtr.h"
#include "nsISupportsArray.h"
#define EDIT_AGGREGATE_TXN_CID \
{/* 345921a0-ac49-11d2-86d8-000064657374 */ \
0x345921a0, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsVoidArray;
/**
* base class for all document editing transactions that require aggregation.
@ -68,7 +68,7 @@ public:
/** get the number of nested txns.
* This is the number of top-level txns, it does not do recursive decent.
*/
NS_IMETHOD GetCount(PRInt32 *aCount);
NS_IMETHOD GetCount(PRUint32 *aCount);
/** get the txn at index aIndex.
* returns NS_ERROR_UNEXPECTED if there is no txn at aIndex.
@ -85,8 +85,7 @@ public:
protected:
//XXX: if this was an nsISupportsArray, it would handle refcounting for us
nsVoidArray * mChildren;
nsCOMPtr<nsISupportsArray> mChildren;
nsCOMPtr<nsIAtom> mName;
};

Просмотреть файл

@ -356,25 +356,31 @@ nsEditor::Do(nsITransaction *aTxn)
if (mPlaceHolderBatch && !mPlaceHolderTxn)
{
// it's pretty darn amazing how many different types of pointers
// this transcation goes through here. I bet this is a record.
// this transaction goes through here. I bet this is a record.
// We start off with an EditTxn since that's what the factory returns.
EditTxn *editTxn;
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
result = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), &editTxn);
if (NS_FAILED(result)) { return result; }
if (!editTxn) { return NS_ERROR_NULL_POINTER; }
// Then we QI to an nsIAbsorbingTransaction to get at placeholder functionality
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
editTxn->QueryInterface(nsIAbsorbingTransaction::GetIID(), getter_AddRefs(plcTxn));
// have to use line above instead of line below due to our broken
// interface model for transactions.
// plcTxn = do_QueryInterface(editTxn);
// have to use line above instead of "plcTxn = do_QueryInterface(editTxn);"
// due to our broken interface model for transactions.
// save off weak reference to placeholder txn
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mTxnStartNode, mTxnStartOffset);
// we will recurse, but will not hit this case in the nested call
// finally we QI to an nsITransaction since that's what Do() expects
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
nsITransaction* txn = theTxn;
// we want to escape from this routine with a positive refcount
txn->AddRef();
Do(txn);
Do(txn); // we will recurse, but will not hit this case in the nested call
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
}
if (aTxn)
@ -398,7 +404,7 @@ nsEditor::Do(nsITransaction *aTxn)
selection->EndBatchChanges(); // no need to check result here, don't lose result of operation
}
NS_POSTCONDITION((NS_SUCCEEDED(result)), "transaction did not execute properly\n");
return result;
@ -853,6 +859,8 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons
if (NS_SUCCEEDED(result)) {
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -886,6 +894,8 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute)
if (NS_SUCCEEDED(result)) {
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -906,6 +916,8 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded.");
}
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -932,6 +944,8 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
if (NS_SUCCEEDED(result)) {
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
@ -976,6 +990,8 @@ nsEditor::SplitNode(nsIDOMNode * aNode,
NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
}
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
@ -1020,6 +1036,10 @@ nsEditor::InsertNoneditableTextNode(nsIDOMNode* parent, PRInt32 offset,
// Now get the pointer to the node we just created ...
nsCOMPtr<nsIDOMNode> newNode;
res = txn->GetNewNode(getter_AddRefs(newNode));
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (NS_FAILED(res))
return res;
nsCOMPtr<nsIDOMCharacterData> newTextNode;
@ -1112,6 +1132,9 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
for (i = 0; i < mActionListeners->Count(); i++)
@ -1147,6 +1170,9 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
for (i = 0; i < mActionListeners->Count(); i++)
@ -1360,8 +1386,9 @@ nsEditor::EndComposition(void)
// Note that this means IME won't work without an undo stack!
if (mTxnMgr)
{
nsCOMPtr<nsITransaction> txn;
result = mTxnMgr->PeekUndoStack(getter_AddRefs(txn));
nsITransaction *txn;
result = mTxnMgr->PeekUndoStack(&txn);
// PeekUndoStack does not addref
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
if (plcTxn)
{
@ -1589,6 +1616,9 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
BeginUpdateViewBatch();
result = Do(aggTxn);
result = Do(txn);
// The transaction system (if any) has taken ownwership of txns.
// aggTxn released at end of routine.
NS_IF_RELEASE(txn);
EndUpdateViewBatch();
}
else if (NS_ERROR_EDITOR_NO_SELECTION==result)
@ -1599,9 +1629,9 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
{
// only execute the aggTxn if we actually populated it with at least one sub-txn
PRInt32 count=0;
PRUint32 count=0;
aggTxn->GetCount(&count);
if (0!=count)
if (count)
{
result = Do(aggTxn);
}
@ -1609,6 +1639,8 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
{
result = NS_OK;
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(aggTxn);
// create the text node
if (NS_SUCCEEDED(result))
@ -1645,6 +1677,8 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
}
}
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(aggTxn);
return result;
}
@ -1981,6 +2015,8 @@ NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert)
if (NS_SUCCEEDED(result)) {
result = Do(insertTxn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(insertTxn);
}
else {
result = NS_ERROR_UNEXPECTED;
@ -1988,6 +2024,8 @@ NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert)
}
}
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
}
}
return result;
@ -2003,6 +2041,8 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
result = Do(txn);
// HACKForceRedraw();
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -3961,6 +4001,9 @@ nsEditor::DeleteSelectionImpl(ESelectionCollapseDirection aAction)
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}

Просмотреть файл

@ -91,7 +91,9 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
switch (info->action)
{
case kInsertText:
return WillInsertText(aSelection,
case kInsertTextIME:
return WillInsertText(info->action,
aSelection,
aCancel,
aHandled,
info->inString,
@ -148,13 +150,14 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
********************************************************/
nsresult
nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
nsHTMLEditRules::WillInsertText(PRInt32 aAction,
nsIDOMSelection *aSelection,
PRBool *aCancel,
PRBool *aHandled,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength)
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength)
{ if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
// initialize out param
@ -249,9 +252,11 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
if (priorNode && IsBreak(priorNode) && HasMozAttr(priorNode)
&& (blockParent == mEditor->GetBlockNodeParent(priorNode)))
{
needMozDiv = PR_TRUE;
res = mEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
// but we only need to make a moz div if we weren't in a listitem
if (!IsListItem(blockParent))
needMozDiv = PR_TRUE;
}
// if we are directly in a body or (non-moz) div, create a moz-div.
@ -271,59 +276,62 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
}
char nbspStr[2] = {nbsp, 0};
PRBool bCancel;
nsString theString(*inString); // copy instring for now
PRInt32 pos = theString.FindCharInSet(specialChars);
if(0 == theString.Length()) {
// special case for IME. We need this to remove the last
// unconverted text.
PRBool bCancel;
nsString partialString;
res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState);
if(aAction == kInsertTextIME)
{
// special case for IME. We need this to :
// a) handle null strings, which are meaningful for IME
// b) prevent the string from being broken into substrings,
// which can happen in non-IME processing below.
// I should probably convert runs of spaces and tabs here as well
res = DoTextInsertion(aSelection, &bCancel, &theString, typeInState);
}
while (theString.Length())
else // aAction == kInsertText
{
PRBool bCancel;
nsString partialString;
// if first char is special, then use just it
if (pos == 0) pos = 1;
if (pos == -1) pos = theString.Length();
theString.Left(partialString, pos);
theString.Cut(0, pos);
// is it a solo tab?
if (partialString == "\t" )
while (theString.Length())
{
res = InsertTab(aSelection,outString);
nsString partialString;
// if first char is special, then use just it
if (pos == 0) pos = 1;
if (pos == -1) pos = theString.Length();
theString.Left(partialString, pos);
theString.Cut(0, pos);
// is it a solo tab?
if (partialString == "\t" )
{
res = InsertTab(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo space?
else if (partialString == " ")
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo nbsp?
else if (partialString == nbspStr)
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo return?
else if (partialString == "\n")
{
res = mEditor->InsertBreak();
}
else
{
res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState);
}
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
pos = theString.FindCharInSet(specialChars);
}
// is it a solo space?
else if (partialString == " ")
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo nbsp?
else if (partialString == nbspStr)
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo return?
else if (partialString == "\n")
{
res = mEditor->InsertBreak();
}
else
{
res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState);
}
if (NS_FAILED(res)) return res;
pos = theString.FindCharInSet(specialChars);
}
return res;
}
@ -423,15 +431,8 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P
if (NS_FAILED(res)) return res;
}
nsCOMPtr<nsIDOMNode> brNode;
res = mEditor->InsertBR(&brNode); // only inserts a br node
res = InsertMozBR(); // inserts a br node with moz attr
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
if (brElem)
{
res = mEditor->SetAttribute(brElem, "type", "_moz");
if (NS_FAILED(res)) return res;
}
*aHandled = PR_TRUE;
}
else if (bIsMozDiv && AtStartOfBlock(node, offset, blockParent))
@ -1671,6 +1672,17 @@ nsHTMLEditRules::IsMozDiv(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsMozBR: true if node an html br node with type = _moz
//
PRBool
nsHTMLEditRules::IsMozBR(nsIDOMNode *node)
{
if (IsBreak(node) && HasMozAttr(node)) return PR_TRUE;
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// HasMozAttr: true if node has type attribute = _moz
// (used to indicate the div's and br's we use in
@ -1744,7 +1756,10 @@ nsHTMLEditRules::InBody(nsIDOMNode *node)
// if the children are empty or non-editable.
//
nsresult
nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount,
PRBool aListItemsNotEmpty)
{
if (!aNode || !outIsEmptyBlock) return NS_ERROR_NULL_POINTER;
*outIsEmptyBlock = PR_TRUE;
@ -1752,10 +1767,11 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
// nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> nodeToTest;
if (nsEditor::IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
else nsCOMPtr<nsIDOMElement> block;
// else nsCOMPtr<nsIDOMElement> block;
// looks like I forgot to finish this. Wonder what I was going to do?
if (!nodeToTest) return NS_ERROR_NULL_POINTER;
return IsEmptyNode(nodeToTest, outIsEmptyBlock);
return IsEmptyNode(nodeToTest, outIsEmptyBlock, aMozBRDoesntCount, aListItemsNotEmpty);
}
@ -1765,7 +1781,10 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
// if the children are empty or non-editable.
//
nsresult
nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
nsHTMLEditRules::IsEmptyNode( nsIDOMNode *aNode,
PRBool *outIsEmptyNode,
PRBool aMozBRDoesntCount,
PRBool aListItemsNotEmpty)
{
if (!aNode || !outIsEmptyNode) return NS_ERROR_NULL_POINTER;
*outIsEmptyNode = PR_TRUE;
@ -1785,9 +1804,9 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
// then we dont call it empty (it's an <hr>, or <br>, etc).
// Also, if it's an anchor then dont treat it as empty - even though
// anchors are containers, named anchors are "empty" but we don't
// want to treat them as such. Also, don't call ListItems empty:
// empty list items still render and might be wanted.
if (!mEditor->IsContainer(aNode) || IsAnchor(aNode) || IsListItem(aNode))
// want to treat them as such. Also, don't call ListItems empty
// if caller desires.
if (!mEditor->IsContainer(aNode) || IsAnchor(aNode) || (aListItemsNotEmpty &&IsListItem(aNode)))
{
*outIsEmptyNode = PR_FALSE;
return NS_OK;
@ -1830,8 +1849,13 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
{
// is it the node we are iterating over?
if (node.get() == aNode) break;
// otherwise it ain't empty
*outIsEmptyNode = PR_FALSE;
// is it a moz-BR and did the caller ask us not to consider those relevant?
if (!(aMozBRDoesntCount && IsMozBR(node)))
{
// otherwise it ain't empty
*outIsEmptyNode = PR_FALSE;
break;
}
}
}
res = iter->Next();
@ -2634,6 +2658,25 @@ nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection,
}
///////////////////////////////////////////////////////////////////////////
// InsertMozBR: put a BR node with moz attribute at current insertion point
//
nsresult
nsHTMLEditRules::InsertMozBR()
{
nsCOMPtr<nsIDOMNode> brNode;
nsresult res = mEditor->InsertBR(&brNode); // only inserts a br node
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
if (brElem)
{
res = mEditor->SetAttribute(brElem, "type", "_moz");
if (NS_FAILED(res)) return res;
}
return res;
}
///////////////////////////////////////////////////////////////////////////
// ReturnInHeader: do the right thing for returns pressed in headers
//
@ -2658,7 +2701,7 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
// if the new (righthand) header node is empty, delete it
PRBool isEmpty;
res = IsEmptyBlock(aHeader, &isEmpty);
res = IsEmptyBlock(aHeader, &isEmpty, PR_TRUE);
if (NS_FAILED(res)) return res;
if (isEmpty)
{
@ -2797,7 +2840,7 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
// if we are in an empty listitem, then we want to pop up out of the list
PRBool isEmpty;
res = IsEmptyBlock(aListItem, &isEmpty);
res = IsEmptyBlock(aListItem, &isEmpty, PR_TRUE, PR_FALSE);
if (NS_FAILED(res)) return res;
if (isEmpty)
{
@ -2832,6 +2875,8 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(aListItem,0);
// insert a moz-br
InsertMozBR();
return res;
}
@ -3342,7 +3387,7 @@ nsHTMLEditRules::CleanUpSelection(nsIDOMSelection *aSelection)
if (!node) return NS_ERROR_FAILURE;
PRBool bIsEmptyNode;
res = IsEmptyNode(node, &bIsEmptyNode);
res = IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode && !IsBody(node))
{

Просмотреть файл

@ -47,13 +47,14 @@ protected:
// nsHTMLEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PRBool *aHandled,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength);
nsresult WillInsertText( PRInt32 aAction,
nsIDOMSelection *aSelection,
PRBool *aCancel,
PRBool *aHandled,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength);
nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction,
PRBool *aCancel, PRBool *aHandled);
@ -66,6 +67,7 @@ protected:
nsresult InsertTab(nsIDOMSelection *aSelection, nsString *outString);
nsresult InsertSpace(nsIDOMSelection *aSelection, nsString *outString);
nsresult InsertMozBR();
nsresult ReturnInHeader(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
@ -88,13 +90,20 @@ protected:
static PRBool IsDiv(nsIDOMNode *aNode);
static PRBool IsNormalDiv(nsIDOMNode *aNode);
static PRBool IsMozDiv(nsIDOMNode *aNode);
static PRBool IsMozBR(nsIDOMNode *aNode);
static PRBool IsMailCite(nsIDOMNode *aNode);
static PRBool HasMozAttr(nsIDOMNode *aNode);
static PRBool InBody(nsIDOMNode *aNode);
nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock);
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode);
nsresult IsEmptyBlock(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
nsresult IsEmptyNode(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);

Просмотреть файл

@ -1260,6 +1260,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert)
if (!selection) return NS_ERROR_NULL_POINTER;
nsAutoString resultString;
nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertText);
// set a different action flag if we are an IME event
if (mInIMEMode) ruleInfo.action = nsTextEditRules::kInsertTextIME;
ruleInfo.inString = &aStringToInsert;
ruleInfo.outString = &resultString;
ruleInfo.typeInState = *mTypeInState;
@ -2881,6 +2883,8 @@ nsHTMLEditor::AddStyleSheet(nsICSSStyleSheet* aSheet)
mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one
}
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(txn);
return rv;
}
@ -2900,6 +2904,8 @@ nsHTMLEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet)
mLastStyleSheet = nsnull; // forget it
}
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(txn);
return rv;
}

Просмотреть файл

@ -25,36 +25,29 @@ EditAggregateTxn::EditAggregateTxn()
: EditTxn()
{
// base class does this: NS_INIT_REFCNT();
mChildren = new nsVoidArray();
nsresult res = NS_NewISupportsArray(getter_AddRefs(mChildren));
NS_POSTCONDITION(NS_SUCCEEDED(res), "EditAggregateTxn failed in constructor");
SetTransactionDescriptionID( kTransactionID );
/* log description initialized in parent constructor */
}
EditAggregateTxn::~EditAggregateTxn()
{
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
NS_IF_RELEASE(txn);
}
delete mChildren;
}
// nsISupportsArray cleans up array for us at destruct time
}
NS_IMETHODIMP EditAggregateTxn::Do(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
PRUint32 count;
mChildren->Count(&count);
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Do();
if (NS_FAILED(result))
@ -67,14 +60,17 @@ NS_IMETHODIMP EditAggregateTxn::Do(void)
NS_IMETHODIMP EditAggregateTxn::Undo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
PRUint32 count;
mChildren->Count(&count);
// undo goes through children backwards
for (i=count-1; i>=0; i--)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Undo();
if (NS_FAILED(result))
break;
@ -86,13 +82,16 @@ NS_IMETHODIMP EditAggregateTxn::Undo(void)
NS_IMETHODIMP EditAggregateTxn::Redo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
PRUint32 count;
mChildren->Count(&count);
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Redo();
if (NS_FAILED(result))
break;
@ -113,13 +112,17 @@ NS_IMETHODIMP EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransa
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
if (nsnull!=mChildren)
if (mChildren)
{
PRInt32 count = mChildren->Count();
PRInt32 i;
PRUint32 count;
mChildren->Count(&count);
NS_ASSERTION(count>0, "bad count");
if (0<count)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(count-1));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(i));
nsCOMPtr<nsITransaction> txn ( do_QueryInterface(isupports) );
if (!txn) { return NS_ERROR_NULL_POINTER; }
result = txn->Merge(aDidMerge, aTransaction);
}
}
@ -148,22 +151,25 @@ NS_IMETHODIMP EditAggregateTxn::GetRedoString(nsString *aString)
NS_IMETHODIMP EditAggregateTxn::AppendChild(EditTxn *aTxn)
{
if ((nsnull!=mChildren) && (nsnull!=aTxn))
if (mChildren && aTxn)
{
mChildren->AppendElement(aTxn);
// aaahhhh! broken interfaces drive me crazy!!!
nsCOMPtr<nsISupports> isupports;
aTxn->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(isupports));
mChildren->AppendElement(isupports);
return NS_OK;
}
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP EditAggregateTxn::GetCount(PRInt32 *aCount)
NS_IMETHODIMP EditAggregateTxn::GetCount(PRUint32 *aCount)
{
if (!aCount) {
return NS_ERROR_NULL_POINTER;
}
*aCount=0;
if (mChildren) {
*aCount = mChildren->Count();
mChildren->Count(aCount);
}
return NS_OK;
}
@ -183,14 +189,16 @@ NS_IMETHODIMP EditAggregateTxn::GetTxnAt(PRInt32 aIndex, EditTxn **aTxn)
}
// get the transaction at aIndex
const PRInt32 txnCount = mChildren->Count();
PRUint32 txnCount;
mChildren->Count(&txnCount);
if (0>aIndex || txnCount<=aIndex) {
return NS_ERROR_UNEXPECTED;
}
*aTxn = (EditTxn *)(mChildren->ElementAt(aIndex));
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(mChildren->ElementAt(aIndex));
// ugh, this is all wrong - what a mess we have with editor transaction interfaces
isupports->QueryInterface(EditTxn::GetCID(), (void**)aTxn);
if (!*aTxn)
return NS_ERROR_UNEXPECTED;
NS_ADDREF(*aTxn);
return NS_OK;
}

Просмотреть файл

@ -22,13 +22,13 @@
#include "EditTxn.h"
#include "nsIAtom.h"
#include "nsCOMPtr.h"
#include "nsISupportsArray.h"
#define EDIT_AGGREGATE_TXN_CID \
{/* 345921a0-ac49-11d2-86d8-000064657374 */ \
0x345921a0, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsVoidArray;
/**
* base class for all document editing transactions that require aggregation.
@ -68,7 +68,7 @@ public:
/** get the number of nested txns.
* This is the number of top-level txns, it does not do recursive decent.
*/
NS_IMETHOD GetCount(PRInt32 *aCount);
NS_IMETHOD GetCount(PRUint32 *aCount);
/** get the txn at index aIndex.
* returns NS_ERROR_UNEXPECTED if there is no txn at aIndex.
@ -85,8 +85,7 @@ public:
protected:
//XXX: if this was an nsISupportsArray, it would handle refcounting for us
nsVoidArray * mChildren;
nsCOMPtr<nsISupportsArray> mChildren;
nsCOMPtr<nsIAtom> mName;
};

Просмотреть файл

@ -356,25 +356,31 @@ nsEditor::Do(nsITransaction *aTxn)
if (mPlaceHolderBatch && !mPlaceHolderTxn)
{
// it's pretty darn amazing how many different types of pointers
// this transcation goes through here. I bet this is a record.
// this transaction goes through here. I bet this is a record.
// We start off with an EditTxn since that's what the factory returns.
EditTxn *editTxn;
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
result = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), &editTxn);
if (NS_FAILED(result)) { return result; }
if (!editTxn) { return NS_ERROR_NULL_POINTER; }
// Then we QI to an nsIAbsorbingTransaction to get at placeholder functionality
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
editTxn->QueryInterface(nsIAbsorbingTransaction::GetIID(), getter_AddRefs(plcTxn));
// have to use line above instead of line below due to our broken
// interface model for transactions.
// plcTxn = do_QueryInterface(editTxn);
// have to use line above instead of "plcTxn = do_QueryInterface(editTxn);"
// due to our broken interface model for transactions.
// save off weak reference to placeholder txn
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mTxnStartNode, mTxnStartOffset);
// we will recurse, but will not hit this case in the nested call
// finally we QI to an nsITransaction since that's what Do() expects
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
nsITransaction* txn = theTxn;
// we want to escape from this routine with a positive refcount
txn->AddRef();
Do(txn);
Do(txn); // we will recurse, but will not hit this case in the nested call
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
}
if (aTxn)
@ -398,7 +404,7 @@ nsEditor::Do(nsITransaction *aTxn)
selection->EndBatchChanges(); // no need to check result here, don't lose result of operation
}
NS_POSTCONDITION((NS_SUCCEEDED(result)), "transaction did not execute properly\n");
return result;
@ -853,6 +859,8 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons
if (NS_SUCCEEDED(result)) {
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -886,6 +894,8 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute)
if (NS_SUCCEEDED(result)) {
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -906,6 +916,8 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded.");
}
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -932,6 +944,8 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
if (NS_SUCCEEDED(result)) {
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
@ -976,6 +990,8 @@ nsEditor::SplitNode(nsIDOMNode * aNode,
NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
}
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
@ -1020,6 +1036,10 @@ nsEditor::InsertNoneditableTextNode(nsIDOMNode* parent, PRInt32 offset,
// Now get the pointer to the node we just created ...
nsCOMPtr<nsIDOMNode> newNode;
res = txn->GetNewNode(getter_AddRefs(newNode));
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (NS_FAILED(res))
return res;
nsCOMPtr<nsIDOMCharacterData> newTextNode;
@ -1112,6 +1132,9 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
for (i = 0; i < mActionListeners->Count(); i++)
@ -1147,6 +1170,9 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
if (mActionListeners)
{
for (i = 0; i < mActionListeners->Count(); i++)
@ -1360,8 +1386,9 @@ nsEditor::EndComposition(void)
// Note that this means IME won't work without an undo stack!
if (mTxnMgr)
{
nsCOMPtr<nsITransaction> txn;
result = mTxnMgr->PeekUndoStack(getter_AddRefs(txn));
nsITransaction *txn;
result = mTxnMgr->PeekUndoStack(&txn);
// PeekUndoStack does not addref
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
if (plcTxn)
{
@ -1589,6 +1616,9 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
BeginUpdateViewBatch();
result = Do(aggTxn);
result = Do(txn);
// The transaction system (if any) has taken ownwership of txns.
// aggTxn released at end of routine.
NS_IF_RELEASE(txn);
EndUpdateViewBatch();
}
else if (NS_ERROR_EDITOR_NO_SELECTION==result)
@ -1599,9 +1629,9 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
{
// only execute the aggTxn if we actually populated it with at least one sub-txn
PRInt32 count=0;
PRUint32 count=0;
aggTxn->GetCount(&count);
if (0!=count)
if (count)
{
result = Do(aggTxn);
}
@ -1609,6 +1639,8 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
{
result = NS_OK;
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(aggTxn);
// create the text node
if (NS_SUCCEEDED(result))
@ -1645,6 +1677,8 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
}
}
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(aggTxn);
return result;
}
@ -1981,6 +2015,8 @@ NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert)
if (NS_SUCCEEDED(result)) {
result = Do(insertTxn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(insertTxn);
}
else {
result = NS_ERROR_UNEXPECTED;
@ -1988,6 +2024,8 @@ NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert)
}
}
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
}
}
return result;
@ -2003,6 +2041,8 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
result = Do(txn);
// HACKForceRedraw();
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}
@ -3961,6 +4001,9 @@ nsEditor::DeleteSelectionImpl(ESelectionCollapseDirection aAction)
result = Do(txn);
}
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
return result;
}

Просмотреть файл

@ -91,7 +91,9 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
switch (info->action)
{
case kInsertText:
return WillInsertText(aSelection,
case kInsertTextIME:
return WillInsertText(info->action,
aSelection,
aCancel,
aHandled,
info->inString,
@ -148,13 +150,14 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
********************************************************/
nsresult
nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
nsHTMLEditRules::WillInsertText(PRInt32 aAction,
nsIDOMSelection *aSelection,
PRBool *aCancel,
PRBool *aHandled,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength)
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength)
{ if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
// initialize out param
@ -249,9 +252,11 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
if (priorNode && IsBreak(priorNode) && HasMozAttr(priorNode)
&& (blockParent == mEditor->GetBlockNodeParent(priorNode)))
{
needMozDiv = PR_TRUE;
res = mEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
// but we only need to make a moz div if we weren't in a listitem
if (!IsListItem(blockParent))
needMozDiv = PR_TRUE;
}
// if we are directly in a body or (non-moz) div, create a moz-div.
@ -271,59 +276,62 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
}
char nbspStr[2] = {nbsp, 0};
PRBool bCancel;
nsString theString(*inString); // copy instring for now
PRInt32 pos = theString.FindCharInSet(specialChars);
if(0 == theString.Length()) {
// special case for IME. We need this to remove the last
// unconverted text.
PRBool bCancel;
nsString partialString;
res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState);
if(aAction == kInsertTextIME)
{
// special case for IME. We need this to :
// a) handle null strings, which are meaningful for IME
// b) prevent the string from being broken into substrings,
// which can happen in non-IME processing below.
// I should probably convert runs of spaces and tabs here as well
res = DoTextInsertion(aSelection, &bCancel, &theString, typeInState);
}
while (theString.Length())
else // aAction == kInsertText
{
PRBool bCancel;
nsString partialString;
// if first char is special, then use just it
if (pos == 0) pos = 1;
if (pos == -1) pos = theString.Length();
theString.Left(partialString, pos);
theString.Cut(0, pos);
// is it a solo tab?
if (partialString == "\t" )
while (theString.Length())
{
res = InsertTab(aSelection,outString);
nsString partialString;
// if first char is special, then use just it
if (pos == 0) pos = 1;
if (pos == -1) pos = theString.Length();
theString.Left(partialString, pos);
theString.Cut(0, pos);
// is it a solo tab?
if (partialString == "\t" )
{
res = InsertTab(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo space?
else if (partialString == " ")
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo nbsp?
else if (partialString == nbspStr)
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo return?
else if (partialString == "\n")
{
res = mEditor->InsertBreak();
}
else
{
res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState);
}
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
pos = theString.FindCharInSet(specialChars);
}
// is it a solo space?
else if (partialString == " ")
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo nbsp?
else if (partialString == nbspStr)
{
res = InsertSpace(aSelection,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
}
// is it a solo return?
else if (partialString == "\n")
{
res = mEditor->InsertBreak();
}
else
{
res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState);
}
if (NS_FAILED(res)) return res;
pos = theString.FindCharInSet(specialChars);
}
return res;
}
@ -423,15 +431,8 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P
if (NS_FAILED(res)) return res;
}
nsCOMPtr<nsIDOMNode> brNode;
res = mEditor->InsertBR(&brNode); // only inserts a br node
res = InsertMozBR(); // inserts a br node with moz attr
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
if (brElem)
{
res = mEditor->SetAttribute(brElem, "type", "_moz");
if (NS_FAILED(res)) return res;
}
*aHandled = PR_TRUE;
}
else if (bIsMozDiv && AtStartOfBlock(node, offset, blockParent))
@ -1671,6 +1672,17 @@ nsHTMLEditRules::IsMozDiv(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsMozBR: true if node an html br node with type = _moz
//
PRBool
nsHTMLEditRules::IsMozBR(nsIDOMNode *node)
{
if (IsBreak(node) && HasMozAttr(node)) return PR_TRUE;
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// HasMozAttr: true if node has type attribute = _moz
// (used to indicate the div's and br's we use in
@ -1744,7 +1756,10 @@ nsHTMLEditRules::InBody(nsIDOMNode *node)
// if the children are empty or non-editable.
//
nsresult
nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount,
PRBool aListItemsNotEmpty)
{
if (!aNode || !outIsEmptyBlock) return NS_ERROR_NULL_POINTER;
*outIsEmptyBlock = PR_TRUE;
@ -1752,10 +1767,11 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
// nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> nodeToTest;
if (nsEditor::IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
else nsCOMPtr<nsIDOMElement> block;
// else nsCOMPtr<nsIDOMElement> block;
// looks like I forgot to finish this. Wonder what I was going to do?
if (!nodeToTest) return NS_ERROR_NULL_POINTER;
return IsEmptyNode(nodeToTest, outIsEmptyBlock);
return IsEmptyNode(nodeToTest, outIsEmptyBlock, aMozBRDoesntCount, aListItemsNotEmpty);
}
@ -1765,7 +1781,10 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
// if the children are empty or non-editable.
//
nsresult
nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
nsHTMLEditRules::IsEmptyNode( nsIDOMNode *aNode,
PRBool *outIsEmptyNode,
PRBool aMozBRDoesntCount,
PRBool aListItemsNotEmpty)
{
if (!aNode || !outIsEmptyNode) return NS_ERROR_NULL_POINTER;
*outIsEmptyNode = PR_TRUE;
@ -1785,9 +1804,9 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
// then we dont call it empty (it's an <hr>, or <br>, etc).
// Also, if it's an anchor then dont treat it as empty - even though
// anchors are containers, named anchors are "empty" but we don't
// want to treat them as such. Also, don't call ListItems empty:
// empty list items still render and might be wanted.
if (!mEditor->IsContainer(aNode) || IsAnchor(aNode) || IsListItem(aNode))
// want to treat them as such. Also, don't call ListItems empty
// if caller desires.
if (!mEditor->IsContainer(aNode) || IsAnchor(aNode) || (aListItemsNotEmpty &&IsListItem(aNode)))
{
*outIsEmptyNode = PR_FALSE;
return NS_OK;
@ -1830,8 +1849,13 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
{
// is it the node we are iterating over?
if (node.get() == aNode) break;
// otherwise it ain't empty
*outIsEmptyNode = PR_FALSE;
// is it a moz-BR and did the caller ask us not to consider those relevant?
if (!(aMozBRDoesntCount && IsMozBR(node)))
{
// otherwise it ain't empty
*outIsEmptyNode = PR_FALSE;
break;
}
}
}
res = iter->Next();
@ -2634,6 +2658,25 @@ nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection,
}
///////////////////////////////////////////////////////////////////////////
// InsertMozBR: put a BR node with moz attribute at current insertion point
//
nsresult
nsHTMLEditRules::InsertMozBR()
{
nsCOMPtr<nsIDOMNode> brNode;
nsresult res = mEditor->InsertBR(&brNode); // only inserts a br node
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
if (brElem)
{
res = mEditor->SetAttribute(brElem, "type", "_moz");
if (NS_FAILED(res)) return res;
}
return res;
}
///////////////////////////////////////////////////////////////////////////
// ReturnInHeader: do the right thing for returns pressed in headers
//
@ -2658,7 +2701,7 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
// if the new (righthand) header node is empty, delete it
PRBool isEmpty;
res = IsEmptyBlock(aHeader, &isEmpty);
res = IsEmptyBlock(aHeader, &isEmpty, PR_TRUE);
if (NS_FAILED(res)) return res;
if (isEmpty)
{
@ -2797,7 +2840,7 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
// if we are in an empty listitem, then we want to pop up out of the list
PRBool isEmpty;
res = IsEmptyBlock(aListItem, &isEmpty);
res = IsEmptyBlock(aListItem, &isEmpty, PR_TRUE, PR_FALSE);
if (NS_FAILED(res)) return res;
if (isEmpty)
{
@ -2832,6 +2875,8 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(aListItem,0);
// insert a moz-br
InsertMozBR();
return res;
}
@ -3342,7 +3387,7 @@ nsHTMLEditRules::CleanUpSelection(nsIDOMSelection *aSelection)
if (!node) return NS_ERROR_FAILURE;
PRBool bIsEmptyNode;
res = IsEmptyNode(node, &bIsEmptyNode);
res = IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode && !IsBody(node))
{

Просмотреть файл

@ -47,13 +47,14 @@ protected:
// nsHTMLEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PRBool *aHandled,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength);
nsresult WillInsertText( PRInt32 aAction,
nsIDOMSelection *aSelection,
PRBool *aCancel,
PRBool *aHandled,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
PRInt32 aMaxLength);
nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction,
PRBool *aCancel, PRBool *aHandled);
@ -66,6 +67,7 @@ protected:
nsresult InsertTab(nsIDOMSelection *aSelection, nsString *outString);
nsresult InsertSpace(nsIDOMSelection *aSelection, nsString *outString);
nsresult InsertMozBR();
nsresult ReturnInHeader(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
@ -88,13 +90,20 @@ protected:
static PRBool IsDiv(nsIDOMNode *aNode);
static PRBool IsNormalDiv(nsIDOMNode *aNode);
static PRBool IsMozDiv(nsIDOMNode *aNode);
static PRBool IsMozBR(nsIDOMNode *aNode);
static PRBool IsMailCite(nsIDOMNode *aNode);
static PRBool HasMozAttr(nsIDOMNode *aNode);
static PRBool InBody(nsIDOMNode *aNode);
nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock);
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode);
nsresult IsEmptyBlock(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
nsresult IsEmptyNode(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);

Просмотреть файл

@ -1260,6 +1260,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert)
if (!selection) return NS_ERROR_NULL_POINTER;
nsAutoString resultString;
nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertText);
// set a different action flag if we are an IME event
if (mInIMEMode) ruleInfo.action = nsTextEditRules::kInsertTextIME;
ruleInfo.inString = &aStringToInsert;
ruleInfo.outString = &resultString;
ruleInfo.typeInState = *mTypeInState;
@ -2881,6 +2883,8 @@ nsHTMLEditor::AddStyleSheet(nsICSSStyleSheet* aSheet)
mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one
}
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(txn);
return rv;
}
@ -2900,6 +2904,8 @@ nsHTMLEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet)
mLastStyleSheet = nsnull; // forget it
}
}
// The transaction system (if any) has taken ownwership of txns
NS_IF_RELEASE(txn);
return rv;
}