diff --git a/mozilla/editor/base/EditAggregateTxn.cpp b/mozilla/editor/base/EditAggregateTxn.cpp index ec5ad8403bf..d0ea70afa85 100644 --- a/mozilla/editor/base/EditAggregateTxn.cpp +++ b/mozilla/editor/base/EditAggregateTxn.cpp @@ -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; iElementAt(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; iElementAt(i)); + nsCOMPtr isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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 isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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; iElementAt(i)); + nsCOMPtr isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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 (0ElementAt(count-1)); + nsCOMPtr isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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 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 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; } diff --git a/mozilla/editor/base/EditAggregateTxn.h b/mozilla/editor/base/EditAggregateTxn.h index c3fa9e2f2b1..777dad28ad6 100644 --- a/mozilla/editor/base/EditAggregateTxn.h +++ b/mozilla/editor/base/EditAggregateTxn.h @@ -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 mChildren; nsCOMPtr mName; }; diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index 77c837772d9..144780ba1cf 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -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 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 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 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 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 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 txn; - result = mTxnMgr->PeekUndoStack(getter_AddRefs(txn)); + nsITransaction *txn; + result = mTxnMgr->PeekUndoStack(&txn); + // PeekUndoStack does not addref nsCOMPtr 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; } diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index 15c1350f779..a6a7846e20a 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -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 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 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 nodeToTest; if (nsEditor::IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode); - else nsCOMPtr block; +// else nsCOMPtr 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
, or
, 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 brNode; + nsresult res = mEditor->InsertBR(&brNode); // only inserts a br node + if (NS_FAILED(res)) return res; + // give it special moz attr + nsCOMPtr 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)) { diff --git a/mozilla/editor/base/nsHTMLEditRules.h b/mozilla/editor/base/nsHTMLEditRules.h index 946d9d729b6..e7924fe0637 100644 --- a/mozilla/editor/base/nsHTMLEditRules.h +++ b/mozilla/editor/base/nsHTMLEditRules.h @@ -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); diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index 08c8e9e8e17..2dbb94b7062 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -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; } diff --git a/mozilla/editor/libeditor/base/EditAggregateTxn.cpp b/mozilla/editor/libeditor/base/EditAggregateTxn.cpp index ec5ad8403bf..d0ea70afa85 100644 --- a/mozilla/editor/libeditor/base/EditAggregateTxn.cpp +++ b/mozilla/editor/libeditor/base/EditAggregateTxn.cpp @@ -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; iElementAt(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; iElementAt(i)); + nsCOMPtr isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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 isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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; iElementAt(i)); + nsCOMPtr isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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 (0ElementAt(count-1)); + nsCOMPtr isupports = (dont_AddRef)(mChildren->ElementAt(i)); + nsCOMPtr 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 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 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; } diff --git a/mozilla/editor/libeditor/base/EditAggregateTxn.h b/mozilla/editor/libeditor/base/EditAggregateTxn.h index c3fa9e2f2b1..777dad28ad6 100644 --- a/mozilla/editor/libeditor/base/EditAggregateTxn.h +++ b/mozilla/editor/libeditor/base/EditAggregateTxn.h @@ -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 mChildren; nsCOMPtr mName; }; diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index 77c837772d9..144780ba1cf 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -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 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 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 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 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 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 txn; - result = mTxnMgr->PeekUndoStack(getter_AddRefs(txn)); + nsITransaction *txn; + result = mTxnMgr->PeekUndoStack(&txn); + // PeekUndoStack does not addref nsCOMPtr 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; } diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index 15c1350f779..a6a7846e20a 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -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 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 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 nodeToTest; if (nsEditor::IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode); - else nsCOMPtr block; +// else nsCOMPtr 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
, or
, 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 brNode; + nsresult res = mEditor->InsertBR(&brNode); // only inserts a br node + if (NS_FAILED(res)) return res; + // give it special moz attr + nsCOMPtr 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)) { diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h index 946d9d729b6..e7924fe0637 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h @@ -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); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 08c8e9e8e17..2dbb94b7062 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -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; }