diff --git a/mozilla/editor/base/Makefile.in b/mozilla/editor/base/Makefile.in index da0ddf0283e..fbb5230148e 100644 --- a/mozilla/editor/base/Makefile.in +++ b/mozilla/editor/base/Makefile.in @@ -31,50 +31,83 @@ LIBRARY_NAME = editor IS_COMPONENT = 1 REQUIRES = xpcom dom js locale layout uriloader widget txmgr htmlparser necko pref view appshell rdf webshell timer txtsvc intl lwbrk docshell chrome caps appcomps -CPPSRCS = \ - nsEditor.cpp \ - nsEditorService.cpp \ - nsEditorController.cpp \ - nsEditorCommands.cpp \ - nsComposerCommands.cpp \ - nsEditorUtils.cpp \ - nsEditorRegistration.cpp \ - nsEditorParserObserver.cpp \ - nsTextEditRules.cpp \ - nsHTMLEditUtils.cpp \ - TextEditorTest.cpp \ - nsHTMLEditRules.cpp \ - nsEditorEventListeners.cpp \ - nsEditorShellMouseListener.cpp \ - nsEditProperty.cpp \ - nsHTMLEditor.cpp \ - ChangeAttributeTxn.cpp \ - EditTxn.cpp \ - EditAggregateTxn.cpp \ - nsTableEditor.cpp \ - InsertTextTxn.cpp \ - PlaceholderTxn.cpp \ - DeleteTextTxn.cpp \ - CreateElementTxn.cpp \ - InsertElementTxn.cpp \ - DeleteElementTxn.cpp \ - DeleteRangeTxn.cpp \ - SplitElementTxn.cpp \ - JoinElementTxn.cpp \ - nsStyleSheetTxns.cpp \ - TransactionFactory.cpp \ - TypeInState.cpp \ - nsInternetCiter.cpp \ - nsAOLCiter.cpp \ - nsWrapUtils.cpp \ - nsInterfaceState.cpp \ - nsEditorShell.cpp \ - IMETextTxn.cpp \ - IMECommitTxn.cpp \ - nsHTMLEditorLog.cpp \ - nsEditorTxnLog.cpp \ +CPPSRCS = \ + ChangeAttributeTxn.cpp \ + CreateElementTxn.cpp \ + DeleteElementTxn.cpp \ + DeleteRangeTxn.cpp \ + DeleteTextTxn.cpp \ + EditAggregateTxn.cpp \ + EditTxn.cpp \ + IMECommitTxn.cpp \ + IMETextTxn.cpp \ + InsertElementTxn.cpp \ + InsertTextTxn.cpp \ + JoinElementTxn.cpp \ + nsComposerCommands.cpp \ + nsEditorCommands.cpp \ + nsEditorController.cpp \ + nsEditor.cpp \ + nsEditorEventListeners.cpp \ + nsEditorUtils.cpp \ + nsEditProperty.cpp \ + nsHTMLEditUtils.cpp \ + nsPlaintextDataTransfer.cpp \ + nsPlaintextEditor.cpp \ + nsSelectionState.cpp \ + nsStyleSheetTxns.cpp \ + nsTextEditRules.cpp \ + PlaceholderTxn.cpp \ + SplitElementTxn.cpp \ + TransactionFactory.cpp \ $(NULL) +# MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY=1 + +ifdef MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY + +# We're only building the Core PlainText Editor Source so just include +# the plain text registration file. + +CPPSRCS += nsTextEditorReg.cpp + +else + +# Building the full blown HTML Editor so add it's source files and objects: + +CPPSRCS += nsAOLCiter.cpp \ + nsEditorParserObserver.cpp \ + nsEditorRegistration.cpp \ + nsEditorShell.cpp \ + nsEditorShellMouseListener.cpp \ + nsEditorService.cpp \ + nsHTMLDataTransfer.cpp \ + nsHTMLEditor.cpp \ + nsHTMLEditorStyle.cpp \ + nsHTMLEditRules.cpp \ + nsInterfaceState.cpp \ + nsInternetCiter.cpp \ + nsTableEditor.cpp \ + nsWrapUtils.cpp \ + TextEditorTest.cpp \ + TypeInState.cpp \ + $(NULL) + +# Enable Editor API Logging! +ENABLE_EDITOR_API_LOG=1 + +ifdef ENABLE_EDITOR_API_LOG + +CPPSRCS += nsHTMLEditorLog.cpp \ + nsEditorTxnLog.cpp \ + $(NULL) + +DEFINES += -DENABLE_EDITOR_API_LOG + +endif + +endif + EXTRA_DSO_LDOPTS = \ $(MOZ_NECKO_UTIL_LIBS) \ $(MOZ_COMPONENT_LIBS) \ @@ -83,5 +116,3 @@ EXTRA_DSO_LDOPTS = \ include $(topsrcdir)/config/rules.mk -DEFINES += -DENABLE_EDITOR_API_LOG - diff --git a/mozilla/editor/base/makefile.win b/mozilla/editor/base/makefile.win index 98fc2e2b6c7..d2d17baf351 100644 --- a/mozilla/editor/base/makefile.win +++ b/mozilla/editor/base/makefile.win @@ -25,95 +25,127 @@ include <$(DEPTH)/config/config.mak> LIBRARY_NAME=editor -CPPSRCS = \ - nsEditor.cpp \ - nsEditorService.cpp \ - nsEditorController.cpp \ - nsEditorCommands.cpp \ - nsComposerCommands.cpp \ - nsEditorUtils.cpp \ - nsEditorRegistration.cpp \ - nsEditorParserObserver.cpp \ - nsTextEditRules.cpp \ - nsHTMLEditRules.cpp \ - nsHTMLEditUtils.cpp \ - TextEditorTest.cpp \ - nsEditorEventListeners.cpp \ - nsEditorShellMouseListener.cpp \ - nsEditProperty.cpp \ - EditTxn.cpp \ - EditAggregateTxn.cpp \ - ChangeAttributeTxn.cpp \ - InsertTextTxn.cpp \ - DeleteTextTxn.cpp \ - PlaceholderTxn.cpp \ - CreateElementTxn.cpp \ - InsertElementTxn.cpp \ - DeleteElementTxn.cpp \ - DeleteRangeTxn.cpp \ - SplitElementTxn.cpp \ - JoinElementTxn.cpp \ - nsStyleSheetTxns.cpp \ - TransactionFactory.cpp \ - TypeInState.cpp \ - nsHTMLEditor.cpp \ - nsTableEditor.cpp \ - nsInternetCiter.cpp \ - nsAOLCiter.cpp \ - nsWrapUtils.cpp \ - nsInterfaceState.cpp \ - nsEditorShell.cpp \ - IMETextTxn.cpp \ - IMECommitTxn.cpp \ - $(NULL) +# Uncomment the line below, or define MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY +# in your environment, to build only the plain text editor core files: +# MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY=1 -CPP_OBJS = \ - .\$(OBJDIR)\nsEditor.obj \ - .\$(OBJDIR)\nsEditorService.obj \ - .\$(OBJDIR)\nsEditorController.obj \ - .\$(OBJDIR)\nsEditorCommands.obj \ - .\$(OBJDIR)\nsComposerCommands.obj \ - .\$(OBJDIR)\nsEditorUtils.obj \ - .\$(OBJDIR)\nsEditorRegistration.obj \ - .\$(OBJDIR)\nsEditorParserObserver.obj \ - .\$(OBJDIR)\nsTextEditRules.obj \ - .\$(OBJDIR)\TextEditorTest.obj \ - .\$(OBJDIR)\nsHTMLEditRules.obj \ - .\$(OBJDIR)\nsHTMLEditUtils.obj \ - .\$(OBJDIR)\nsEditorEventListeners.obj \ - .\$(OBJDIR)\nsEditorShellMouseListener.obj \ - .\$(OBJDIR)\nsEditProperty.obj \ - .\$(OBJDIR)\EditTxn.obj \ - .\$(OBJDIR)\EditAggregateTxn.obj \ - .\$(OBJDIR)\ChangeAttributeTxn.obj \ - .\$(OBJDIR)\InsertTextTxn.obj \ - .\$(OBJDIR)\DeleteTextTxn.obj \ - .\$(OBJDIR)\PlaceholderTxn.obj \ - .\$(OBJDIR)\CreateElementTxn.obj \ - .\$(OBJDIR)\InsertElementTxn.obj \ - .\$(OBJDIR)\DeleteElementTxn.obj \ - .\$(OBJDIR)\DeleteRangeTxn.obj \ - .\$(OBJDIR)\SplitElementTxn.obj \ - .\$(OBJDIR)\JoinElementTxn.obj \ - .\$(OBJDIR)\nsStyleSheetTxns.obj \ - .\$(OBJDIR)\TransactionFactory.obj \ - .\$(OBJDIR)\TypeInState.obj \ - .\$(OBJDIR)\nsHTMLEditor.obj \ - .\$(OBJDIR)\nsTableEditor.obj \ - .\$(OBJDIR)\nsInternetCiter.obj \ - .\$(OBJDIR)\nsAOLCiter.obj \ - .\$(OBJDIR)\nsWrapUtils.obj \ - .\$(OBJDIR)\nsInterfaceState.obj \ - .\$(OBJDIR)\nsEditorShell.obj \ - .\$(OBJDIR)\IMETextTxn.obj \ - .\$(OBJDIR)\IMECommitTxn.obj \ - $(NULL) +# Editor Core Source Files and Objects: -MODULE=editor +CPPSRCS = \ + ChangeAttributeTxn.cpp \ + CreateElementTxn.cpp \ + DeleteElementTxn.cpp \ + DeleteRangeTxn.cpp \ + DeleteTextTxn.cpp \ + EditAggregateTxn.cpp \ + EditTxn.cpp \ + IMECommitTxn.cpp \ + IMETextTxn.cpp \ + InsertElementTxn.cpp \ + InsertTextTxn.cpp \ + JoinElementTxn.cpp \ + nsComposerCommands.cpp \ + nsEditorCommands.cpp \ + nsEditorController.cpp \ + nsEditor.cpp \ + nsEditorEventListeners.cpp \ + nsEditorUtils.cpp \ + nsEditProperty.cpp \ + nsHTMLEditUtils.cpp \ + nsPlaintextDataTransfer.cpp \ + nsPlaintextEditor.cpp \ + nsSelectionState.cpp \ + nsStyleSheetTxns.cpp \ + nsTextEditRules.cpp \ + PlaceholderTxn.cpp \ + SplitElementTxn.cpp \ + TransactionFactory.cpp \ + $(NULL) -MAKE_OBJ_TYPE = DLL -DLLNAME = editor -DLL=.\$(OBJDIR)\$(DLLNAME).dll +CPP_OBJS = \ + .\$(OBJDIR)\ChangeAttributeTxn.obj \ + .\$(OBJDIR)\CreateElementTxn.obj \ + .\$(OBJDIR)\DeleteElementTxn.obj \ + .\$(OBJDIR)\DeleteRangeTxn.obj \ + .\$(OBJDIR)\DeleteTextTxn.obj \ + .\$(OBJDIR)\EditAggregateTxn.obj \ + .\$(OBJDIR)\EditTxn.obj \ + .\$(OBJDIR)\IMECommitTxn.obj \ + .\$(OBJDIR)\IMETextTxn.obj \ + .\$(OBJDIR)\InsertElementTxn.obj \ + .\$(OBJDIR)\InsertTextTxn.obj \ + .\$(OBJDIR)\JoinElementTxn.obj \ + .\$(OBJDIR)\nsComposerCommands.obj \ + .\$(OBJDIR)\nsEditorCommands.obj \ + .\$(OBJDIR)\nsEditorController.obj \ + .\$(OBJDIR)\nsEditorEventListeners.obj \ + .\$(OBJDIR)\nsEditor.obj \ + .\$(OBJDIR)\nsEditorUtils.obj \ + .\$(OBJDIR)\nsEditProperty.obj \ + .\$(OBJDIR)\nsHTMLEditUtils.obj \ + .\$(OBJDIR)\nsPlaintextDataTransfer.obj \ + .\$(OBJDIR)\nsPlaintextEditor.obj \ + .\$(OBJDIR)\nsSelectionState.obj \ + .\$(OBJDIR)\nsStyleSheetTxns.obj \ + .\$(OBJDIR)\nsTextEditRules.obj \ + .\$(OBJDIR)\PlaceholderTxn.obj \ + .\$(OBJDIR)\SplitElementTxn.obj \ + .\$(OBJDIR)\TransactionFactory.obj \ + $(NULL) + +!if defined(MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY) + +# We're only building the Core PlainText Editor Source so just include +# the plain text registration file. + +CPPSRCS = $(CPPSRCS) \ + nsTextEditorReg.cpp \ + $(NULL) + +CPP_OBJS = $(CPP_OBJS) \ + .\$(OBJDIR)\nsTextEditorReg.obj \ + $(NULL) +!else + +# Building the full blown HTML Editor so add it's source files and objects: + +CPPSRCS = $(CPPSRCS) \ + nsAOLCiter.cpp \ + nsEditorParserObserver.cpp \ + nsEditorRegistration.cpp \ + nsEditorService.cpp \ + nsEditorShell.cpp \ + nsEditorShellMouseListener.cpp \ + nsHTMLDataTransfer.cpp \ + nsHTMLEditor.cpp \ + nsHTMLEditorStyle.cpp \ + nsHTMLEditRules.cpp \ + nsInterfaceState.cpp \ + nsInternetCiter.cpp \ + nsTableEditor.cpp \ + nsWrapUtils.cpp \ + TextEditorTest.cpp \ + TypeInState.cpp \ + $(NULL) + +CPP_OBJS = $(CPP_OBJS) \ + .\$(OBJDIR)\nsAOLCiter.obj \ + .\$(OBJDIR)\nsEditorParserObserver.obj \ + .\$(OBJDIR)\nsEditorRegistration.obj \ + .\$(OBJDIR)\nsEditorService.obj \ + .\$(OBJDIR)\nsEditorShellMouseListener.obj \ + .\$(OBJDIR)\nsEditorShell.obj \ + .\$(OBJDIR)\nsHTMLDataTransfer.obj \ + .\$(OBJDIR)\nsHTMLEditor.obj \ + .\$(OBJDIR)\nsHTMLEditorStyle.obj \ + .\$(OBJDIR)\nsHTMLEditRules.obj \ + .\$(OBJDIR)\nsInterfaceState.obj \ + .\$(OBJDIR)\nsInternetCiter.obj \ + .\$(OBJDIR)\nsTableEditor.obj \ + .\$(OBJDIR)\nsWrapUtils.obj \ + .\$(OBJDIR)\TextEditorTest.obj \ + .\$(OBJDIR)\TypeInState.obj \ + $(NULL) # Enable Editor API Logging! ENABLE_EDITOR_API_LOG=1 @@ -122,17 +154,26 @@ ENABLE_EDITOR_API_LOG=1 CPPSRCS = $(CPPSRCS) \ nsHTMLEditorLog.cpp \ nsEditorTxnLog.cpp \ - $(NULL) + $(NULL) CPP_OBJS = $(CPP_OBJS) \ .\$(OBJDIR)\nsHTMLEditorLog.obj \ .\$(OBJDIR)\nsEditorTxnLog.obj \ - $(NULL) + $(NULL) DEFINES = -DENABLE_EDITOR_API_LOG $(DEFINES) !endif +!endif + +MODULE=editor + +MAKE_OBJ_TYPE = DLL +DLLNAME = editor +DLL=.\$(OBJDIR)\$(DLLNAME).dll + + LCFLAGS = \ $(LCFLAGS) \ $(DEFINES) \ diff --git a/mozilla/editor/base/nsEditRules.h b/mozilla/editor/base/nsEditRules.h index 2af710fd2dc..c3b8223de97 100644 --- a/mozilla/editor/base/nsEditRules.h +++ b/mozilla/editor/base/nsEditRules.h @@ -28,7 +28,7 @@ 0xa6cf911d, 0x15b3, 0x11d2, \ {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} } -class nsHTMLEditor; +class nsPlaintextEditor; class nsISelection; /*************************************************************************** @@ -57,7 +57,7 @@ public: //Interfaces for addref and release and queryinterface //NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules - NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags)=0; + NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)=0; NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0; NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0; NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0; diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index c12b2d45e6a..7a21c310d40 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -59,6 +59,7 @@ #include "nsIKBStateControl.h" #include "nsIWidget.h" #include "nsIScrollbar.h" +#include "nsIPlaintextEditor.h" #include "nsIFrame.h" // Needed by IME code @@ -93,7 +94,7 @@ #include "JoinElementTxn.h" #include "nsStyleSheetTxns.h" #include "IMETextTxn.h" -#include "nsIHTMLEditor.h" + // included for nsEditor::CreateHTMLContent #include "nsIElementFactory.h" #include "nsINodeInfo.h" @@ -148,738 +149,6 @@ const PRUnichar nbsp = 160; PRInt32 nsEditor::gInstanceCount = 0; -/*************************************************************************** - * class for recording selection info. stores selection as collection of - * { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store - * ranges since dom gravity will possibly change the ranges. - */ -nsSelectionState::nsSelectionState() : mArray(){} - -nsSelectionState::~nsSelectionState() -{ - MakeEmpty(); -} - -nsresult -nsSelectionState::SaveSelection(nsISelection *aSel) -{ - if (!aSel) return NS_ERROR_NULL_POINTER; - nsresult res = NS_OK; - PRInt32 i,rangeCount, arrayCount = mArray.Count(); - nsRangeStore *item; - aSel->GetRangeCount(&rangeCount); - - // if we need more items in the array, new them - if (arrayCountarrayCount) - { - while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount))) - { - delete item; - mArray.RemoveElementAt(rangeCount); - } - } - - // now store the selection ranges - for (i=0; i range; - res = aSel->GetRangeAt(i, getter_AddRefs(range)); - item->StoreRange(range); - } - - return res; -} - -nsresult -nsSelectionState::RestoreSelection(nsISelection *aSel) -{ - if (!aSel) return NS_ERROR_NULL_POINTER; - nsresult res = NS_OK; - PRInt32 i, arrayCount = mArray.Count(); - nsRangeStore *item; - - // clear out selection - aSel->RemoveAllRanges(); - - // set the selection ranges anew - for (i=0; i range; - item->GetRange(address_of(range)); - if (!range) return NS_ERROR_UNEXPECTED; - - res = aSel->AddRange(range); - if(NS_FAILED(res)) return res; - - } - return NS_OK; -} - -PRBool -nsSelectionState::IsCollapsed() -{ - if (1 != mArray.Count()) return PR_FALSE; - nsRangeStore *item; - item = (nsRangeStore*)mArray.ElementAt(0); - if (!item) return PR_FALSE; - nsCOMPtr range; - item->GetRange(address_of(range)); - if (!range) return PR_FALSE; - PRBool bIsCollapsed; - range->GetCollapsed(&bIsCollapsed); - return bIsCollapsed; -} - -PRBool -nsSelectionState::IsEqual(nsSelectionState *aSelState) -{ - if (!aSelState) return NS_ERROR_NULL_POINTER; - PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count(); - if (myCount != itsCount) return PR_FALSE; - if (myCount < 1) return PR_FALSE; - - nsRangeStore *myItem, *itsItem; - - for (i=0; imArray.ElementAt(0)); - if (!myItem || !itsItem) return PR_FALSE; - - nsCOMPtr myRange, itsRange; - myItem->GetRange(address_of(myRange)); - itsItem->GetRange(address_of(itsRange)); - if (!myRange || !itsRange) return PR_FALSE; - - PRInt32 compResult; - myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult); - if (compResult) return PR_FALSE; - myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult); - if (compResult) return PR_FALSE; - } - // if we got here, they are equal - return PR_TRUE; -} - -void -nsSelectionState::MakeEmpty() -{ - // free any items in the array - nsRangeStore *item; - while ((item = (nsRangeStore*)mArray.ElementAt(0))) - { - delete item; - mArray.RemoveElementAt(0); - } -} - -PRBool -nsSelectionState::IsEmpty() -{ - return (mArray.Count() == 0); -} - -/*************************************************************************** - * nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions. - */ - -nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {} - -nsRangeUpdater::~nsRangeUpdater() -{ - // free any items in the array - nsRangeStore *item; - while ((item = (nsRangeStore*)mArray.ElementAt(0))) - { - delete item; - mArray.RemoveElementAt(0); - } -} - -void* -nsRangeUpdater::RegisterRange(nsIDOMRange *aRange) -{ - nsRangeStore *item = new nsRangeStore; - if (!item) return nsnull; - item->StoreRange(aRange); - mArray.AppendElement(item); - return item; -} - -nsCOMPtr -nsRangeUpdater::ReclaimRange(void *aCookie) -{ - nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie); - if (!item) return nsnull; - nsCOMPtr outRange; - item->GetRange(address_of(outRange)); - mArray.RemoveElement(aCookie); - delete item; - return outRange; -} - -void -nsRangeUpdater::DropRange(void *aCookie) -{ - nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie); - if (!item) return; - mArray.RemoveElement(aCookie); - delete item; -} - -void -nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem) -{ - if (!aRangeItem) return; - mArray.AppendElement(aRangeItem); - return; -} - -void -nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem) -{ - if (!aRangeItem) return; - mArray.RemoveElement(aRangeItem); - return; -} - -nsresult -nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState) -{ - PRInt32 i, theCount = aSelState.mArray.Count(); - if (theCount < 1) return NS_ERROR_FAILURE; - - nsRangeStore *item; - - for (i=0; istartNode.get() == aParent) && (item->startOffset > aPosition)) - item->startOffset++; - if ((item->endNode.get() == aParent) && (item->endOffset > aPosition)) - item->endOffset++; - } - return NS_OK; -} - -nsresult -nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition) -{ - return SelAdjCreateNode(aParent, aPosition); -} - - -nsresult -nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset) -{ - if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... - if (!aNode) return NS_ERROR_NULL_POINTER; - PRInt32 i, count = mArray.Count(); - if (!count) return NS_OK; - - nsRangeStore *item; - - for (i=0; istartNode.get() == aParent) && (item->startOffset > aOffset)) - item->startOffset--; - if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) - item->endOffset--; - } - // MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't - // actually hit this case in the usage i forsee for this. - return NS_OK; -} - - -nsresult -nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode) -{ - if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... - if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER; - PRInt32 i, count = mArray.Count(); - if (!count) return NS_OK; - - nsCOMPtr parent; - PRInt32 offset; - nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset); - if (NS_FAILED(result)) return result; - - // first part is same as inserting aNewLeftnode - result = SelAdjInsertNode(parent,offset-1); - if (NS_FAILED(result)) return result; - - // next step is to check for range enpoints inside aOldRightNode - nsRangeStore *item; - - for (i=0; istartNode.get() == aOldRightNode) - { - if (item->startOffset > aOffset) - { - item->startOffset -= aOffset; - } - else - { - item->startNode = aNewLeftNode; - } - } - if (item->endNode.get() == aOldRightNode) - { - if (item->endOffset > aOffset) - { - item->endOffset -= aOffset; - } - else - { - item->endNode = aNewLeftNode; - } - } - } - return NS_OK; -} - - -nsresult -nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode, - nsIDOMNode *aRightNode, - nsIDOMNode *aParent, - PRInt32 aOffset, - PRInt32 aOldLeftNodeLength) -{ - if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... - if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER; - PRInt32 i, count = mArray.Count(); - if (!count) return NS_OK; - - nsRangeStore *item; - - for (i=0; istartNode.get() == aParent) - { - if (item->startOffset > aOffset) - { - item->startOffset--; - } - else if (item->startOffset == aOffset) - { - // join keeps right hand node - item->startNode = aRightNode; - item->startOffset = aOldLeftNodeLength; - } - } - if (item->endNode.get() == aParent) - { - if (item->endOffset > aOffset) - { - item->endOffset--; - } - else if (item->endOffset == aOffset) - { - // join keeps right hand node - item->endNode = aRightNode; - item->endOffset = aOldLeftNodeLength; - } - } - // adjust endpoints in aRightNode - if (item->startNode.get() == aRightNode) - item->startOffset += aOldLeftNodeLength; - if (item->endNode.get() == aRightNode) - item->endOffset += aOldLeftNodeLength; - } - - return NS_OK; -} - - -nsresult -nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) -{ - if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... - return NS_OK; -} - - -nsresult -nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) -{ - if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... - return NS_OK; -} - - -nsresult -nsRangeUpdater::WillReplaceContainer() -{ - if (mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_TRUE; - return NS_OK; -} - - -nsresult -nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) -{ - if (!mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_FALSE; - - if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER; - PRInt32 i, count = mArray.Count(); - if (!count) return NS_OK; - - nsRangeStore *item; - - for (i=0; istartNode.get() == aOriginalNode) - item->startNode = aNewNode; - if (item->endNode.get() == aOriginalNode) - item->endNode = aNewNode; - } - return NS_OK; -} - - -nsresult -nsRangeUpdater::WillRemoveContainer() -{ - if (mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_TRUE; - return NS_OK; -} - - -nsresult -nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen) -{ - if (!mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_FALSE; - - if (!aNode || !aParent) return NS_ERROR_NULL_POINTER; - PRInt32 i, count = mArray.Count(); - if (!count) return NS_OK; - - nsRangeStore *item; - - for (i=0; istartNode.get() == aNode) - { - item->startNode = aParent; - item->startOffset += aOffset; - } - if (item->endNode.get() == aNode) - { - item->endNode = aParent; - item->endOffset += aOffset; - } - if ((item->startNode.get() == aParent) && (item->startOffset > aOffset)) - item->startOffset += (PRInt32)aNodeOrigLen-1; - if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) - item->endOffset += (PRInt32)aNodeOrigLen-1; - } - return NS_OK; -} - - -nsresult -nsRangeUpdater::WillInsertContainer() -{ - if (mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_TRUE; - return NS_OK; -} - - -nsresult -nsRangeUpdater::DidInsertContainer() -{ - if (!mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_FALSE; - return NS_OK; -} - - -nsresult -nsRangeUpdater::WillMoveNode() -{ - if (mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_TRUE; - return NS_OK; -} - - -nsresult -nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset) -{ - if (!mLock) return NS_ERROR_UNEXPECTED; - mLock = PR_FALSE; - - if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER; - PRInt32 i, count = mArray.Count(); - if (!count) return NS_OK; - - nsRangeStore *item; - - for (i=0; istartNode.get() == aOldParent) && (item->startOffset > aOldOffset)) - item->startOffset--; - if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset)) - item->endOffset--; - - // and like an insert in aNewParent - if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset)) - item->startOffset++; - if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset)) - item->endOffset++; - } - return NS_OK; -} - - - -/*************************************************************************** - * helper class for nsSelectionState. nsRangeStore stores range endpoints. - */ - - // DEBUG: PRInt32 nsRangeStore::n = 0; - -nsRangeStore::nsRangeStore() -{ - // DEBUG: n++; printf("range store alloc count=%d\n", n); -} -nsRangeStore::~nsRangeStore() -{ - // DEBUG: n--; printf("range store alloc count=%d\n", n); -} - -nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange) -{ - if (!aRange) return NS_ERROR_NULL_POINTER; - aRange->GetStartContainer(getter_AddRefs(startNode)); - aRange->GetEndContainer(getter_AddRefs(endNode)); - aRange->GetStartOffset(&startOffset); - aRange->GetEndOffset(&endOffset); - return NS_OK; -} - -nsresult nsRangeStore::GetRange(nsCOMPtr *outRange) -{ - if (!outRange) return NS_ERROR_NULL_POINTER; - nsresult res = nsComponentManager::CreateInstance(kCRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if(NS_FAILED(res)) return res; - - res = (*outRange)->SetStart(startNode, startOffset); - if(NS_FAILED(res)) return res; - - res = (*outRange)->SetEnd(endNode, endOffset); - return res; -} - -/*************************************************************************** - * another helper class for nsSelectionState. stack based class for doing - * Will/DidReplaceContainer() - */ - -class nsAutoReplaceContainerSelNotify -{ - private: - nsRangeUpdater &mRU; - nsIDOMNode *mOriginalNode; - nsIDOMNode *mNewNode; - - public: - nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) : - mRU(aRangeUpdater) - ,mOriginalNode(aOriginalNode) - ,mNewNode(aNewNode) - { - mRU.WillReplaceContainer(); - } - - ~nsAutoReplaceContainerSelNotify() - { - mRU.DidReplaceContainer(mOriginalNode, mNewNode); - } -}; - - -/*************************************************************************** - * another helper class for nsSelectionState. stack based class for doing - * Will/DidRemoveContainer() - */ - -class nsAutoRemoveContainerSelNotify -{ - private: - nsRangeUpdater &mRU; - nsIDOMNode *mNode; - nsIDOMNode *mParent; - PRInt32 mOffset; - PRUint32 mNodeOrigLen; - - public: - nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater, - nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aOffset, - PRUint32 aNodeOrigLen) : - mRU(aRangeUpdater) - ,mNode(aNode) - ,mParent(aParent) - ,mOffset(aOffset) - ,mNodeOrigLen(aNodeOrigLen) - { - mRU.WillRemoveContainer(); - } - - ~nsAutoRemoveContainerSelNotify() - { - mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen); - } -}; - -/*************************************************************************** - * another helper class for nsSelectionState. stack based class for doing - * Will/DidInsertContainer() - */ - -class nsAutoInsertContainerSelNotify -{ - private: - nsRangeUpdater &mRU; - - public: - nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) : - mRU(aRangeUpdater) - { - mRU.WillInsertContainer(); - } - - ~nsAutoInsertContainerSelNotify() - { - mRU.DidInsertContainer(); - } -}; - - -/*************************************************************************** - * another helper class for nsSelectionState. stack based class for doing - * Will/DidMoveNode() - */ - -class nsAutoMoveNodeSelNotify -{ - private: - nsRangeUpdater &mRU; - nsIDOMNode *mOldParent; - nsIDOMNode *mNewParent; - PRInt32 mOldOffset; - PRInt32 mNewOffset; - - public: - nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater, - nsIDOMNode *aOldParent, - PRInt32 aOldOffset, - nsIDOMNode *aNewParent, - PRInt32 aNewOffset) : - mRU(aRangeUpdater) - ,mOldParent(aOldParent) - ,mNewParent(aNewParent) - ,mOldOffset(aOldOffset) - ,mNewOffset(aNewOffset) - { - mRU.WillMoveNode(); - } - - ~nsAutoMoveNodeSelNotify() - { - mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset); - } -}; - - //--------------------------------------------------------------------------- // // nsEditor: base editor class implementation @@ -2606,7 +1875,7 @@ nsEditor::ForceCompositionEnd() #endif #ifdef XP_UNIX - if(mFlags & nsIHTMLEditor::eEditorPasswordMask) + if(mFlags & nsIPlaintextEditor::eEditorPasswordMask) return NS_OK; #endif @@ -5059,7 +4328,6 @@ nsEditor::IsNextCharWhitespace(nsIDOMNode *aParentNode, textNode->GetLength(&strLength); if (strLength) { - // you could use nsITextContent::IsOnlyWhitespace here textNode->SubstringData(0,1,tempString); *outIsSpace = nsCRT::IsAsciiSpace(tempString.First()); *outIsNBSP = (tempString.First() == nbsp); @@ -5391,7 +4659,7 @@ nsresult nsEditor::EndUpdateViewBatch() PRBool forceReflow = PR_TRUE; - if (flags & nsIHTMLEditor::eEditorDisableForcedReflowsMask) + if (flags & nsIPlaintextEditor::eEditorDisableForcedReflowsMask) forceReflow = PR_FALSE; nsCOMPtr presShell; @@ -5401,7 +4669,7 @@ nsresult nsEditor::EndUpdateViewBatch() PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE; - if (flags & nsIHTMLEditor::eEditorDisableForcedUpdatesMask) + if (flags & nsIPlaintextEditor::eEditorDisableForcedUpdatesMask) updateFlag = NS_VMREFRESH_NO_SYNC; #ifdef HACK_FORCE_REDRAW @@ -5482,9 +4750,129 @@ nsEditor::DeleteSelectionImpl(nsIEditor::EDirection aAction) return res; } +// XXX: error handling in this routine needs to be cleaned up! +NS_IMETHODIMP +nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, + nsIDOMNode ** aNewNode) +{ + nsCOMPtr parentSelectedNode; + PRInt32 offsetOfNewNode; + nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, + offsetOfNewNode); + if (!NS_SUCCEEDED(result)) + return result; + + nsCOMPtr newNode; + result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, + getter_AddRefs(newNode)); + // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases + *aNewNode = newNode; + NS_IF_ADDREF(*aNewNode); + + // we want the selection to be just after the new node + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1); + + return result; +} + /* Non-interface, protected methods */ +NS_IMETHODIMP +nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode) +{ + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + PRBool collapsed; + result = selection->GetIsCollapsed(&collapsed); + if (NS_SUCCEEDED(result) && !collapsed) + { + result = DeleteSelection(nsIEditor::eNone); + if (NS_FAILED(result)) { + return result; + } + // get the new selection + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { + return result; + } +#ifdef NS_DEBUG + nsCOMPtrtestSelectedNode; + nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode)); + // no selection is ok. + // if there is a selection, it must be collapsed + if (testSelectedNode) + { + PRBool testCollapsed; + debugResult = selection->GetIsCollapsed(&testCollapsed); + NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); + NS_ASSERTION(testCollapsed, "selection not reset after deletion"); + } +#endif + } + // split the selected node + PRInt32 offsetOfSelectedNode; + result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode) + { + nsCOMPtr selectedNode; + PRUint32 selectedNodeContentCount=0; + nsCOMPtrselectedParentNodeAsText; + selectedParentNodeAsText = do_QueryInterface(parentSelectedNode); + + offsetOfNewNode = offsetOfSelectedNode; + + /* if the selection is a text node, split the text node if necesary + and compute where to put the new node + */ + if (selectedParentNodeAsText) + { + PRInt32 indexOfTextNodeInParent; + selectedNode = do_QueryInterface(parentSelectedNode); + selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode)); + selectedParentNodeAsText->GetLength(&selectedNodeContentCount); + GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent); + + if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) + { + nsCOMPtr newSiblingNode; + result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); + // now get the node's offset in it's parent, and insert the new tag there + if (NS_SUCCEEDED(result)) { + result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + } + } + else + { // determine where to insert the new node + if (0==offsetOfSelectedNode) { + offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent + } + else { // insert new node as last child + GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node + } + } + } + // Here's where the new node was inserted + } +#ifdef DEBUG + else { + printf("InsertLineBreak into an empty document is not yet supported\n"); + } +#endif + return result; +} + + + NS_IMETHODIMP nsEditor::DoAfterDoTransaction(nsITransaction *aTxn) { diff --git a/mozilla/editor/base/nsEditor.h b/mozilla/editor/base/nsEditor.h index 9c2ed1ca15c..9197fd038e6 100644 --- a/mozilla/editor/base/nsEditor.h +++ b/mozilla/editor/base/nsEditor.h @@ -46,6 +46,7 @@ #include "nsIDTD.h" #include "nsIDOMElement.h" #include "nsVoidArray.h" +#include "nsSelectionState.h" class nsIEditActionListener; class nsIDocumentStateListener; @@ -72,92 +73,6 @@ class nsIFile; class nsISelectionController; -/*************************************************************************** - * class for recording selection info. stores selection as collection of - * { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store - * ranges since dom gravity will possibly change the ranges. - */ - -// first a helper struct for saving/setting ranges -struct nsRangeStore -{ - nsRangeStore(); - ~nsRangeStore(); - nsresult StoreRange(nsIDOMRange *aRange); - nsresult GetRange(nsCOMPtr *outRange); - - nsCOMPtr startNode; - PRInt32 startOffset; - nsCOMPtr endNode; - PRInt32 endOffset; - // DEBUG: static PRInt32 n; -}; - -class nsSelectionState -{ - public: - - nsSelectionState(); - ~nsSelectionState(); - - nsresult SaveSelection(nsISelection *aSel); - nsresult RestoreSelection(nsISelection *aSel); - PRBool IsCollapsed(); - PRBool IsEqual(nsSelectionState *aSelState); - void MakeEmpty(); - PRBool IsEmpty(); - protected: - nsVoidArray mArray; - - friend class nsRangeUpdater; -}; - -class nsRangeUpdater -{ - public: - - nsRangeUpdater(); - ~nsRangeUpdater(); - - void* RegisterRange(nsIDOMRange *aRange); - nsCOMPtr ReclaimRange(void *aCookie); - void DropRange(void *aCookie); - void RegisterRangeItem(nsRangeStore *aRangeItem); - void DropRangeItem(nsRangeStore *aRangeItem); - nsresult RegisterSelectionState(nsSelectionState &aSelState); - nsresult DropSelectionState(nsSelectionState &aSelState); - - // editor selection gravity routines. Note that we can't always depend on - // DOM Range gravity to do what we want to the "real" selection. For instance, - // if you move a node, that corresponds to deleting it and reinserting it. - // DOM Range gravity will promote the selection out of the node on deletion, - // which is not what you want if you know you are reinserting it. - nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition); - nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition); - nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset); - nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode); - nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, - nsIDOMNode *aRightNode, - nsIDOMNode *aParent, - PRInt32 aOffset, - PRInt32 aOldLeftNodeLength); - nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); - nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); - // the following gravity routines need will/did sandwiches, because the other gravity - // routines will be called inside of these sandwiches, but should be ignored. - nsresult WillReplaceContainer(); - nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode); - nsresult WillRemoveContainer(); - nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen); - nsresult WillInsertContainer(); - nsresult DidInsertContainer(); - nsresult WillMoveNode(); - nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset); - protected: - nsVoidArray mArray; - PRBool mLock; -}; - /** implementation of an editor object. it will be the controller/focal point * for the main editor services. i.e. the GUIManager, publishing, transaction * manager, event interfaces. the idea for the event interfaces is to have them @@ -181,18 +96,17 @@ public: kOpNone = 0, kOpUndo, kOpRedo, - kOpSetTextProperty, - kOpRemoveTextProperty, kOpInsertNode, kOpCreateNode, kOpDeleteNode, kOpSplitNode, kOpJoinNode, - kOpDeleteText, - kOpInsertText, - kOpInsertIMEText, kOpDeleteSelection, - kOpHTMLPaste + // text commands + kOpInsertBreak = 1000, + kOpInsertText = 1001, + kOpInsertIMEText = 1002, + kOpDeleteText = 1003 }; static const char* kMOZEditorBogusNodeAttr; @@ -341,6 +255,8 @@ public: nsIDOMCharacterData *aTextNode, PRInt32 aOffset); NS_IMETHOD DeleteSelectionImpl(EDirection aAction); + NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, + nsIDOMNode ** aNewNode); /* helper routines for node/parent manipulations */ nsresult ReplaceContainer(nsIDOMNode *inNode, @@ -454,6 +370,8 @@ protected: nsIDOMNode *aRightNode, JoinElementTxn **aTxn); + NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, + PRInt32& offsetOfNewNode); // called each time we modify the document. Increments the mod // count of the doc. @@ -775,7 +693,7 @@ public: protected: - PRUint32 mFlags; // behavior flags. See nsIHTMLEditor.h for the flags we use. + PRUint32 mFlags; // behavior flags. See nsPlaintextEditor.h for the flags we use. nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController diff --git a/mozilla/editor/base/nsEditorEventListeners.cpp b/mozilla/editor/base/nsEditorEventListeners.cpp index 5497194948f..e48410b3fc7 100644 --- a/mozilla/editor/base/nsEditorEventListeners.cpp +++ b/mozilla/editor/base/nsEditorEventListeners.cpp @@ -21,7 +21,7 @@ * Pierre Phaneuf */ #include "nsEditorEventListeners.h" -#include "nsIPlaintextEditor.h" +#include "nsIHTMLEditor.h" #include "nsEditor.h" #include "nsVoidArray.h" #include "nsString.h" @@ -178,8 +178,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) // if we are readonly or disabled, then do nothing. if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) { - if (flags & nsIHTMLEditor::eEditorReadonlyMask || - flags & nsIHTMLEditor::eEditorDisabledMask) + if (flags & nsIPlaintextEditor::eEditorReadonlyMask || + flags & nsIPlaintextEditor::eEditorDisabledMask) return NS_OK; } else @@ -246,7 +246,7 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) break; case nsIDOMKeyEvent::DOM_VK_TAB: - if ((flags & nsIHTMLEditor::eEditorSingleLineMask)) + if ((flags & nsIPlaintextEditor::eEditorSingleLineMask)) return NS_OK; // let it be used for focus switching // else we insert the tab straight through @@ -257,9 +257,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_RETURN: case nsIDOMKeyEvent::DOM_VK_ENTER: - if (!(flags & nsIHTMLEditor::eEditorSingleLineMask)) + if (!(flags & nsIPlaintextEditor::eEditorSingleLineMask)) { - //htmlEditor->InsertBreak(); textEditor->HandleKeyPress(keyEvent); ScrollSelectionIntoView(mEditor); aKeyEvent->PreventDefault(); // consumed @@ -642,8 +641,8 @@ nsTextEditorDragListener::DragOver(nsIDOMEvent* aDragEvent) if ( dragSession ) { PRUint32 flags; if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) { - if ((flags & nsIHTMLEditor::eEditorDisabledMask) || - (flags & nsIHTMLEditor::eEditorReadonlyMask)) { + if ((flags & nsIPlaintextEditor::eEditorDisabledMask) || + (flags & nsIPlaintextEditor::eEditorReadonlyMask)) { dragSession->SetCanDrop(PR_FALSE); return NS_OK; } @@ -1041,7 +1040,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) PRUint32 flags; aEvent->PreventBubble(); mEditor->GetFlags(&flags); - if (! (flags & nsIHTMLEditor::eEditorDisabledMask)) + if (! (flags & nsIPlaintextEditor::eEditorDisabledMask)) { // only enable caret and selection if the editor is not disabled nsCOMPtreditor = do_QueryInterface(mEditor); if (editor) @@ -1050,7 +1049,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) editor->GetSelectionController(getter_AddRefs(selCon)); if (selCon) { - if (! (flags & nsIHTMLEditor::eEditorReadonlyMask)) + if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask)) { // only enable caret if the editor is not readonly PRInt32 pixelWidth; nsresult result; @@ -1059,7 +1058,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) if (NS_SUCCEEDED(result) && look) { - if(flags & nsIHTMLEditor::eEditorSingleLineMask) + if(flags & nsIPlaintextEditor::eEditorSingleLineMask) look->GetMetric(nsILookAndFeel::eMetric_SingleLineCaretWidth, pixelWidth); else look->GetMetric(nsILookAndFeel::eMetric_MultiLineCaretWidth, pixelWidth); @@ -1109,11 +1108,11 @@ nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent) if (selCon) { selCon->SetCaretEnabled(PR_FALSE); - if((flags & nsIHTMLEditor::eEditorWidgetMask) || - (flags & nsIHTMLEditor::eEditorPasswordMask) || - (flags & nsIHTMLEditor::eEditorReadonlyMask) || - (flags & nsIHTMLEditor::eEditorDisabledMask) || - (flags & nsIHTMLEditor::eEditorFilterInputMask)) + if((flags & nsIPlaintextEditor::eEditorWidgetMask) || + (flags & nsIPlaintextEditor::eEditorPasswordMask) || + (flags & nsIPlaintextEditor::eEditorReadonlyMask) || + (flags & nsIPlaintextEditor::eEditorDisabledMask) || + (flags & nsIPlaintextEditor::eEditorFilterInputMask)) { selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);//hide but do NOT turn off } diff --git a/mozilla/editor/base/nsEditorEventListeners.h b/mozilla/editor/base/nsEditorEventListeners.h index 39db78bdd2a..f1eb28e711b 100644 --- a/mozilla/editor/base/nsEditorEventListeners.h +++ b/mozilla/editor/base/nsEditorEventListeners.h @@ -35,7 +35,7 @@ #include "nsIDOMFocusListener.h" #include "nsIEditor.h" -#include "nsIHTMLEditor.h" +#include "nsIPlaintextEditor.h" /** The nsTextEditorKeyListener public nsIDOMKeyListener * This class will delegate events to its editor according to the translation diff --git a/mozilla/editor/base/nsEditorRegistration.cpp b/mozilla/editor/base/nsEditorRegistration.cpp index ff5f86e2f0a..f262c18cdd5 100644 --- a/mozilla/editor/base/nsEditorRegistration.cpp +++ b/mozilla/editor/base/nsEditorRegistration.cpp @@ -27,6 +27,7 @@ #include "nsEditor.h" // for gInstanceCount #include "nsEditorController.h" //CID #include "nsEditorService.h" +#include "nsPlaintextEditor.h" //////////////////////////////////////////////////////////////////////// // Define the contructor function for the objects @@ -38,6 +39,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorShell) NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController) NS_GENERIC_FACTORY_CONSTRUCTOR(nsComposerController) NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor) #ifdef ENABLE_EDITOR_API_LOG #include "nsHTMLEditorLog.h" @@ -52,6 +54,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLEditor) // class name. // static nsModuleComponentInfo components[] = { + { "Text Editor", NS_TEXTEDITOR_CID, + "@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, }, #ifdef ENABLE_EDITOR_API_LOG { "HTML Editor", NS_HTMLEDITOR_CID, "@mozilla.org/editor/htmleditor;1", nsHTMLEditorLogConstructor, }, diff --git a/mozilla/editor/base/nsEditorShell.cpp b/mozilla/editor/base/nsEditorShell.cpp index a66e2df3842..88ded6ceb97 100644 --- a/mozilla/editor/base/nsEditorShell.cpp +++ b/mozilla/editor/base/nsEditorShell.cpp @@ -910,7 +910,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) { if (mEditorTypeString.EqualsWithConversion("text")) { - err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorPlaintextMask | nsIHTMLEditor::eEditorEnableWrapHackMask); + err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorPlaintextMask | nsIPlaintextEditor::eEditorEnableWrapHackMask); mEditorType = ePlainTextEditorType; } else if (mEditorTypeString.EqualsWithConversion("html") || mEditorTypeString.IsEmpty()) // empty string default to HTML editor @@ -920,7 +920,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) } else if (mEditorTypeString.EqualsWithConversion("htmlmail")) // HTML editor with special mail rules { - err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorMailMask); + err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorMailMask); mEditorType = eHTMLTextEditorType; } else @@ -3423,7 +3423,7 @@ nsEditorShell::GetDocumentEditable(PRBool *aDocumentEditable) PRUint32 editorFlags; editor->GetFlags(&editorFlags); - if (editorFlags & nsIHTMLEditor::eEditorReadonlyMask) + if (editorFlags & nsIPlaintextEditor::eEditorReadonlyMask) return NS_OK; nsCOMPtr doc; diff --git a/mozilla/editor/base/nsEditorUtils.h b/mozilla/editor/base/nsEditorUtils.h index 9f715271bb4..4827800df70 100644 --- a/mozilla/editor/base/nsEditorUtils.h +++ b/mozilla/editor/base/nsEditorUtils.h @@ -32,6 +32,7 @@ #include "nsIAtom.h" #include "nsVoidArray.h" #include "nsEditor.h" +#include "nsPlaintextEditor.h" #include "nsIContentIterator.h" /*************************************************************************** @@ -48,6 +49,22 @@ class nsAutoPlaceHolderBatch ~nsAutoPlaceHolderBatch() { if (mEd) mEd->EndPlaceHolderTransaction(); } }; +/*************************************************************************** + * stack based helper class for detecting end of editor initialization, in + * order to triger "end of init" initialization of the edit rules. + */ +class nsAutoEditInitRulesTrigger +{ + private: + nsPlaintextEditor *mEd; + nsresult &mRes; + public: + nsAutoEditInitRulesTrigger( nsPlaintextEditor *aEd, nsresult &aRes) : mEd(aEd), mRes(aRes) + { if (mEd) mEd->BeginEditorInit(); } + ~nsAutoEditInitRulesTrigger() { if (mEd) mRes = mEd->EndEditorInit(); } +}; + + /*************************************************************************** * stack based helper class for batching a collection of txns. diff --git a/mozilla/editor/base/nsHTMLDataTransfer.cpp b/mozilla/editor/base/nsHTMLDataTransfer.cpp new file mode 100644 index 00000000000..99b54b9854a --- /dev/null +++ b/mozilla/editor/base/nsHTMLDataTransfer.cpp @@ -0,0 +1,1619 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ +#include "nsICaret.h" + + +#include "nsHTMLEditor.h" +#include "nsHTMLEditRules.h" +#include "nsHTMLEditUtils.h" + +#include "nsEditorEventListeners.h" + +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines +#include "nsIIndependentSelection.h" //domselections answer to frameselection + + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// netwerk +#include "nsIURI.h" +#include "nsNetUtil.h" + +// Drag & Drop, Clipboard +#include "nsWidgetsCID.h" +#include "nsIClipboard.h" +#include "nsITransferable.h" +#include "nsIDragService.h" +#include "nsIDOMNSUIEvent.h" + +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" +#include "nsIPref.h" +const PRUnichar nbsp = 160; + +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } + + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); +static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); +static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); + +// Drag & Drop, Clipboard Support +static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); +static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); +static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); +static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); +// private clipboard data flavors for html copy/paste +#define kHTMLContext "text/_moz_htmlcontext" +#define kHTMLInfo "text/_moz_htmlinfo" + + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif +static nsCOMPtr GetListParent(nsIDOMNode* aNode) +{ + if (!aNode) return nsnull; + nsCOMPtr parent, tmp; + aNode->GetParentNode(getter_AddRefs(parent)); + while (parent) + { + if (nsHTMLEditUtils::IsList(parent)) return parent; + parent->GetParentNode(getter_AddRefs(tmp)); + parent = tmp; + } + return nsnull; +} + +static nsCOMPtr GetTableParent(nsIDOMNode* aNode) +{ + if (!aNode) return nsnull; + nsCOMPtr parent, tmp; + aNode->GetParentNode(getter_AddRefs(parent)); + while (parent) + { + if (nsHTMLEditUtils::IsTable(parent)) return parent; + parent->GetParentNode(getter_AddRefs(tmp)); + parent = tmp; + } + return nsnull; +} + + +NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) +{ + nsAutoString charset; + return InsertHTMLWithCharset(aInputString, charset); +} + +nsresult nsHTMLEditor::InsertHTMLWithContext(const nsString& aInputString, const nsString& aContextStr, const nsString& aInfoStr) +{ + nsAutoString charset; + return InsertHTMLWithCharsetAndContext(aInputString, charset, aContextStr, aInfoStr); +} + + +NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset) +{ + return InsertHTMLWithCharsetAndContext(aInputString, aCharset, nsAutoString(), nsAutoString()); +} + + +nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsString& aInputString, + const nsString& aCharset, + const nsString& aContextStr, + const nsString& aInfoStr) +{ + if (!mRules) return NS_ERROR_NOT_INITIALIZED; + + // First, make sure there are no return chars in the document. + // Bad things happen if you insert returns (instead of dom newlines, \n) + // into an editor document. + nsAutoString inputString (aInputString); // hope this does copy-on-write + + // Windows linebreaks: Map CRLF to LF: + inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r\n"), + NS_ConvertASCIItoUCS2("\n")); + + // Mac linebreaks: Map any remaining CR to LF: + inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r"), + NS_ConvertASCIItoUCS2("\n")); + + // force IME commit; set up rules sniffing and batching + ForceCompositionEnd(); + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpHTMLPaste, nsIEditor::eNext); + + // Get selection + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // Get the first range in the selection, for context: + nsCOMPtr range; + res = selection->GetRangeAt(0, getter_AddRefs(range)); + if (NS_FAILED(res)) + return res; + + nsCOMPtr nsrange (do_QueryInterface(range)); + if (!nsrange) + return NS_ERROR_NO_INTERFACE; + + // create a dom document fragment that represents the structure to paste + nsCOMPtr fragmentAsNode; + PRInt32 rangeStartHint, rangeEndHint; + res = CreateDOMFragmentFromPaste(nsrange, inputString, aContextStr, aInfoStr, + address_of(fragmentAsNode), + &rangeStartHint, &rangeEndHint); + NS_ENSURE_SUCCESS(res, res); + + + // make a list of what nodes in docFrag we need to move + nsCOMPtr nodeList; + res = CreateListOfNodesToPaste(fragmentAsNode, address_of(nodeList), rangeStartHint, rangeEndHint); + NS_ENSURE_SUCCESS(res, res); + + // are there any table elements in the list? + // node and offset for insertion + nsCOMPtr parentNode; + PRInt32 offsetOfNewNode; + + // check for table cell selection mode + PRBool cellSelectionMode = PR_FALSE; + nsCOMPtr cell; + res = GetFirstSelectedCell(getter_AddRefs(cell), nsnull); + if (NS_SUCCEEDED(res) && cell) + { + cellSelectionMode = PR_TRUE; + } + + if (cellSelectionMode) + { + // do we have table content to paste? If so, we want to delete + // the selected table cells and replace with new table elements; + // but if not we want to delete _contents_ of cells and replace + // with non-table elements. Use cellSelectionMode bool to + // indicate results. + nsCOMPtr isupports = nodeList->ElementAt(0); + nsCOMPtr firstNode( do_QueryInterface(isupports) ); + if (!nsHTMLEditUtils::IsTableElement(firstNode)) + cellSelectionMode = PR_FALSE; + } + + if (!cellSelectionMode) + { + res = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode); + NS_ENSURE_SUCCESS(res, res); + + // pasting does not inherit local inline styles + res = RemoveAllInlineProperties(); + NS_ENSURE_SUCCESS(res, res); + } + else + { + // delete whole cells: we will replace with new table content + if (1) + { + // Save current selection since DeleteTableCell perturbs it + nsAutoSelectionReset selectionResetter(selection, this); + res = DeleteTableCell(1); + NS_ENSURE_SUCCESS(res, res); + } + // colapse selection to beginning of deleted table content + selection->CollapseToStart(); + } + + // give rules a chance to handle or cancel + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement); + PRBool cancel, handled; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (cancel) return NS_OK; // rules canceled the operation + if (!handled) + { + // The rules code (WillDoAction above) might have changed the selection. + // refresh our memory... + res = GetStartNodeAndOffset(selection, address_of(parentNode), &offsetOfNewNode); + if (!parentNode) res = NS_ERROR_FAILURE; + if (NS_FAILED(res)) return res; + + // are we in a text node? If so, split it. + if (IsTextNode(parentNode)) + { + nsCOMPtr temp; + res = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode); + if (NS_FAILED(res)) return res; + res = parentNode->GetParentNode(getter_AddRefs(temp)); + if (NS_FAILED(res)) return res; + parentNode = temp; + } + + // build up list of parents of first node in lst that are either: + // lists, or tables. + nsCOMPtr isup = nodeList->ElementAt(0); + nsCOMPtr pNode( do_QueryInterface(isup) ); + nsCOMPtr listAndTableArray; + res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); + NS_ENSURE_SUCCESS(res, res); + while (pNode) + { + if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) + { + isup = do_QueryInterface(pNode); + listAndTableArray->AppendElement(isup); + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + + // remember number of lists and tables above us + PRUint32 listAndTableParents; + PRInt32 highWaterMark = -1; + listAndTableArray->Count(&listAndTableParents); + + PRUint32 listCount, j; + if (listAndTableParents) + { + // scan insertion list for table elements (other than table). + nodeList->Count(&listCount); + for (j=0; j isupports = nodeList->ElementAt(j); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) + { + nsCOMPtr theTable = GetTableParent(curNode); + if (theTable) + { + nsCOMPtr isupTable(do_QueryInterface(theTable)); + PRInt32 indexT = listAndTableArray->IndexOf(isupTable); + if (indexT >= 0) + { + highWaterMark = indexT; + if ((PRUint32)highWaterMark == listAndTableParents-1) break; + } + else + { + break; + } + } + } + if (nsHTMLEditUtils::IsListItem(curNode)) + { + nsCOMPtr theList = GetListParent(curNode); + if (theList) + { + nsCOMPtr isupList(do_QueryInterface(theList)); + PRInt32 indexL = listAndTableArray->IndexOf(isupList); + if (indexL >= 0) + { + highWaterMark = indexL; + if ((PRUint32)highWaterMark == listAndTableParents-1) break; + } + else + { + break; + } + } + } + } + } + // if we have pieces of tables or lists to be inserted, let's force the paste + // to deal with table elements right away, so that it doesn't orphan some + // table or list contents outside the table or list. + if (highWaterMark >= 0) + { + nsCOMPtr isupports = listAndTableArray->ElementAt(highWaterMark); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + nsCOMPtr replaceNode, tmp; + if (nsHTMLEditUtils::IsTable(curNode)) + { + // look upward from curNode for a piece of this table + isup = nodeList->ElementAt(0); + pNode = do_QueryInterface(isup); + while (pNode) + { + if (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode)) + { + nsCOMPtr tableP = GetTableParent(pNode); + if (tableP == curNode) + { + replaceNode = pNode; + break; + } + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + } + else // list case + { + // look upward from curNode for a piece of this list + isup = nodeList->ElementAt(0); + pNode = do_QueryInterface(isup); + while (pNode) + { + if (nsHTMLEditUtils::IsListItem(pNode)) + { + nsCOMPtr listP = GetListParent(pNode); + if (listP == curNode) + { + replaceNode = pNode; + break; + } + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + } + + if (replaceNode) + { + isupports = do_QueryInterface(replaceNode); + nodeList->ReplaceElementAt(isupports, 0); + // postprocess list to remove any descendants of this node + // so that we dont insert them twice. + do + { + isupports = nodeList->ElementAt(1); + tmp = do_QueryInterface(isupports); + if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) + nodeList->RemoveElementAt(1); + else + break; + } while(tmp); + } + } + + // Loop over the node list and paste the nodes: + PRBool bDidInsert = PR_FALSE; + nsCOMPtr lastInsertNode, insertedContextParent; + nodeList->Count(&listCount); + for (j=0; j isupports = nodeList->ElementAt(j); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(!nsHTMLEditUtils::IsBody(curNode), NS_ERROR_FAILURE); + + if (insertedContextParent) + { + // if we had to insert something higher up in the paste heirarchy, we want to + // skip any further paste nodes that descend from that. Else we will paste twice. + if (nsHTMLEditUtils::IsDescendantOf(curNode, insertedContextParent)) + continue; + } + + // give the user a hand on table element insertion. if they have + // a table or table row on the clipboard, and are trying to insert + // into a table or table row, insert the appropriate children instead. + if ( (nsHTMLEditUtils::IsTableRow(curNode) && nsHTMLEditUtils::IsTableRow(parentNode)) + && (nsHTMLEditUtils::IsTable(curNode) || nsHTMLEditUtils::IsTable(parentNode)) ) + { + nsCOMPtr child; + curNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + InsertNodeAtPoint(child, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = curNode; + offsetOfNewNode++; + } + curNode->GetFirstChild(getter_AddRefs(child)); + } + } + else + { + // try to insert + res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = curNode; + } + + // assume failure means no legal parent in the document heirarchy. + // try again with the parent of curNode in the paste heirarchy. + nsCOMPtr parent; + while (NS_FAILED(res) && curNode) + { + curNode->GetParentNode(getter_AddRefs(parent)); + if (parent && !nsHTMLEditUtils::IsBody(parent)) + { + res = InsertNodeAtPoint(parent, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + insertedContextParent = parent; + lastInsertNode = parent; + } + } + curNode = parent; + } + } + if (bDidInsert) + { + res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode); + NS_ENSURE_SUCCESS(res, res); + offsetOfNewNode++; + } + } + + // Now collapse the selection to the end of what we just inserted: + if (lastInsertNode) + { + res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode); + NS_ENSURE_SUCCESS(res, res); + selection->Collapse(parentNode, offsetOfNewNode+1); + } + } + + res = mRules->DidDoAction(selection, &ruleInfo, res); + return res; +} + +nsresult +nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + + nsresult res = NS_OK; + nsCOMPtr content = do_QueryInterface(aNode); + if (IsEmptyTextContent(content)) + { + nsCOMPtr parent, ignored; + aNode->GetParentNode(getter_AddRefs(parent)); + if (parent) + { + res = parent->RemoveChild(aNode, getter_AddRefs(ignored)); + return res; + } + } + + if (!nsHTMLEditUtils::IsPre(aNode)) + { + nsCOMPtr child; + aNode->GetLastChild(getter_AddRefs(child)); + + while (child) + { + nsCOMPtr tmp; + child->GetPreviousSibling(getter_AddRefs(tmp)); + res = StripFormattingNodes(child); + NS_ENSURE_SUCCESS(res, res); + child = tmp; + } + } + return res; +} + +NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable) +{ + // Create generic Transferable for getting the data + nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + (void**)transferable); + if (NS_FAILED(rv)) + return rv; + + // Get the nsITransferable interface for getting the data from the clipboard + if (transferable) + { + // Create the desired DataFlavor for the type of data + // we want to get out of the transferable + if ((mFlags & eEditorPlaintextMask) == 0) // This should only happen in html editors, not plaintext + { + (*transferable)->AddDataFlavor(kJPEGImageMime); + (*transferable)->AddDataFlavor(kHTMLMime); + (*transferable)->AddDataFlavor(kFileMime); + } + (*transferable)->AddDataFlavor(kUnicodeMime); + } + + return NS_OK; +} + +NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, + const nsString& aContextStr, + const nsString& aInfoStr) +{ + nsresult rv = NS_OK; + char* bestFlavor = nsnull; + nsCOMPtr genericDataObj; + PRUint32 len = 0; + if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) ) + { + nsAutoTxnsConserveSelection dontSpazMySelection(this); + nsAutoString flavor, stuffToPaste; + flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() +#ifdef DEBUG_akkana + printf("Got flavor [%s]\n", bestFlavor); +#endif + if (flavor.EqualsWithConversion(kHTMLMime)) + { + nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + if (textDataObj && len > 0) + { + PRUnichar* text = nsnull; + + textDataObj->ToString ( &text ); + stuffToPaste.Assign ( text, len / 2 ); + nsAutoEditBatch beginBatching(this); + rv = InsertHTMLWithContext(stuffToPaste, aContextStr, aInfoStr); + if (text) + nsMemory::Free(text); + } + } + else if (flavor.EqualsWithConversion(kUnicodeMime)) + { + nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + if (textDataObj && len > 0) + { + PRUnichar* text = nsnull; + textDataObj->ToString ( &text ); + stuffToPaste.Assign ( text, len / 2 ); + nsAutoEditBatch beginBatching(this); + // pasting does not inherit local inline styles + RemoveAllInlineProperties(); + rv = InsertText(stuffToPaste.GetUnicode()); + if (text) + nsMemory::Free(text); + } + } + else if (flavor.EqualsWithConversion(kFileMime)) + { + nsCOMPtr fileObj ( do_QueryInterface(genericDataObj) ); + if (fileObj && len > 0) + { + nsCOMPtr fileURL; + rv = nsComponentManager::CreateInstance("@mozilla.org/network/standard-url;1", nsnull, + NS_GET_IID(nsIURL), getter_AddRefs(fileURL)); + if (NS_FAILED(rv)) + return rv; + + if ( fileURL ) + { + rv = fileURL->SetFile( fileObj ); + if (NS_FAILED(rv)) + return rv; + + PRBool insertAsImage = PR_FALSE; + char *fileextension = nsnull; + rv = fileURL->GetFileExtension( &fileextension ); + if ( NS_SUCCEEDED(rv) && fileextension ) + { + if ( (nsCRT::strcasecmp( fileextension, "jpg" ) == 0 ) + || (nsCRT::strcasecmp( fileextension, "jpeg" ) == 0 ) + || (nsCRT::strcasecmp( fileextension, "gif" ) == 0 ) + || (nsCRT::strcasecmp( fileextension, "png" ) == 0 ) ) + { + insertAsImage = PR_TRUE; + } + } + if (fileextension) nsCRT::free(fileextension); + + char *urltext = nsnull; + rv = fileURL->GetSpec( &urltext ); + if ( NS_SUCCEEDED(rv) && urltext && urltext[0] != 0) + { + len = strlen(urltext); + if ( insertAsImage ) + { + stuffToPaste.AssignWithConversion ( "" ); + } + else /* insert as link */ + { + stuffToPaste.AssignWithConversion ( "" ); + stuffToPaste.AppendWithConversion ( urltext, len ); + stuffToPaste.AppendWithConversion ( "" ); + } + nsAutoEditBatch beginBatching(this); + rv = InsertHTML(stuffToPaste); + } + if (urltext) nsCRT::free(urltext); + } + } + } + else if (flavor.EqualsWithConversion(kJPEGImageMime)) + { + // Insert Image code here + printf("Don't know how to insert an image yet!\n"); + //nsIImage* image = (nsIImage *)data; + //NS_RELEASE(image); + rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code + } + } + nsCRT::free(bestFlavor); + + // Try to scroll the selection into view if the paste/drop succeeded + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr selCon; + if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon) + selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION); + } + + return rv; +} + +NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) +{ + ForceCompositionEnd(); + + nsresult rv; + NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr dragSession(do_QueryInterface(dragService)); + + if (!dragSession) return NS_OK; + + // Get the nsITransferable interface for getting the data from the drop + nsCOMPtr trans; + rv = PrepareTransferable(getter_AddRefs(trans)); + if (NS_FAILED(rv)) return rv; + if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL? + + PRUint32 numItems = 0; + rv = dragSession->GetNumDropItems(&numItems); + if (NS_FAILED(rv)) return rv; + + // Combine any deletion and drop insertion into one transaction + nsAutoEditBatch beginBatching(this); + + PRUint32 i; + PRBool doPlaceCaret = PR_TRUE; + for (i = 0; i < numItems; ++i) + { + rv = dragSession->GetData(trans, i); + if (NS_FAILED(rv)) return rv; + if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail? + + if ( doPlaceCaret ) + { + // check if the user pressed the key to force a copy rather than a move + // if we run into problems here, we'll just assume the user doesn't want a copy + PRBool userWantsCopy = PR_FALSE; + + nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent)); + if (!nsuiEvent) return NS_ERROR_FAILURE; + + nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) ); + if (mouseEvent) + +#ifdef XP_MAC + mouseEvent->GetAltKey(&userWantsCopy); +#else + mouseEvent->GetCtrlKey(&userWantsCopy); +#endif + // Source doc is null if source is *not* the current editor document + nsCOMPtr srcdomdoc; + rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc)); + if (NS_FAILED(rv)) return rv; + + // Current doc is destination + nsCOMPtrdestdomdoc; + rv = GetDocument(getter_AddRefs(destdomdoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + if (!selection) return NS_ERROR_FAILURE; + + PRBool isCollapsed; + rv = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(rv)) return rv; + + // Parent and offset under the mouse cursor + nsCOMPtr newSelectionParent; + PRInt32 newSelectionOffset = 0; + rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent)); + if (NS_FAILED(rv)) return rv; + if (!newSelectionParent) return NS_ERROR_FAILURE; + + rv = nsuiEvent->GetRangeOffset(&newSelectionOffset); + if (NS_FAILED(rv)) return rv; + /* Creating a range to store insert position because when + we delete the selection, range gravity will make sure the insertion + point is in the correct place */ + nsCOMPtr destinationRange; + rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange)); + if (NS_FAILED(rv)) + return rv; + if(!destinationRange) + return NS_ERROR_FAILURE; + + // We never have to delete if selection is already collapsed + PRBool deleteSelection = PR_FALSE; + PRBool cursorIsInSelection = PR_FALSE; + + // Check if mouse is in the selection + if (!isCollapsed) + { + PRInt32 rangeCount; + rv = selection->GetRangeCount(&rangeCount); + if (NS_FAILED(rv)) + return rv?rv:NS_ERROR_FAILURE; + + for (PRInt32 j = 0; j < rangeCount; j++) + { + nsCOMPtr range; + + rv = selection->GetRangeAt(j, getter_AddRefs(range)); + if (NS_FAILED(rv) || !range) + continue;//dont bail yet, iterate through them all + + nsCOMPtr nsrange(do_QueryInterface(range)); + if (NS_FAILED(rv) || !nsrange) + continue;//dont bail yet, iterate through them all + + rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection); + if(cursorIsInSelection) + break; + } + if (cursorIsInSelection) + { + // Dragging within same doc can't drop on itself -- leave! + // (We shouldn't get here - drag event shouldn't have started if over selection) + if (srcdomdoc == destdomdoc) + return NS_OK; + + // Dragging from another window onto a selection + // XXX Decision made to NOT do this, + // note that 4.x does replace if dropped on + //deleteSelection = PR_TRUE; + } + else + { + // We are NOT over the selection + if (srcdomdoc == destdomdoc) + { + // Within the same doc: delete if user doesn't want to copy + deleteSelection = !userWantsCopy; + } + else + { + // Different source doc: Don't delete + deleteSelection = PR_FALSE; + } + } + } + + if (deleteSelection) + { + rv = DeleteSelection(eNone); + if (NS_FAILED(rv)) return rv; + } + + // If we deleted the selection because we dropped from another doc, + // then we don't have to relocate the caret (insert at the deletion point) + if (!(deleteSelection && srcdomdoc != destdomdoc)) + { + // Move the selection to the point under the mouse cursor + rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent)); + if (NS_FAILED(rv)) + return rv; + if(!newSelectionParent) + return NS_ERROR_FAILURE; + + rv = destinationRange->GetStartOffset(&newSelectionOffset); + if (NS_FAILED(rv)) + return rv; + selection->Collapse(newSelectionParent, newSelectionOffset); + } + // We have to figure out whether to delete and relocate caret only once + doPlaceCaret = PR_FALSE; + } + + rv = InsertFromTransferable(trans, nsAutoString(), nsAutoString()); + } + + return rv; +} + +NS_IMETHODIMP nsHTMLEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag) +{ + /* we really should be checking the XY coordinates of the mouseevent and ensure that + * that particular point is actually within the selection (not just that there is a selection) + */ + aCanDrag = PR_FALSE; + + // KLUDGE to work around bug 50703 + // After double click and object property editing, + // we get a spurious drag event + if (mIgnoreSpuriousDragEvent) + { +#ifdef DEBUG_cmanske + printf(" *** IGNORING SPURIOUS DRAG EVENT!\n"); +#endif + mIgnoreSpuriousDragEvent = PR_FALSE; + return NS_OK; + } + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + // if we are collapsed, we have no selection so nothing to drag + if ( isCollapsed ) + return NS_OK; + + nsCOMPtr eventTarget; + res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget)); + if (NS_FAILED(res)) return res; + if ( eventTarget ) + { + nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget); + if ( eventTargetDomNode ) + { + PRBool amTargettedCorrectly = PR_FALSE; + res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly); + if (NS_FAILED(res)) return res; + + aCanDrag = amTargettedCorrectly; + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) +{ + nsresult rv; + + nsCOMPtr eventTarget; + rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget)); + if (NS_FAILED(rv)) return rv; + nsCOMPtr domnode = do_QueryInterface(eventTarget); + + /* get the selection to be dragged */ + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + + /* create an array of transferables */ + nsCOMPtr transferableArray; + NS_NewISupportsArray(getter_AddRefs(transferableArray)); + if (transferableArray == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + + /* get the drag service */ + NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv); + if (NS_FAILED(rv)) return rv; + + /* create html flavor transferable */ + nsCOMPtr trans; + rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + getter_AddRefs(trans)); + if (NS_FAILED(rv)) return rv; + if ( !trans ) return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr domdoc; + rv = GetDocument(getter_AddRefs(domdoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr doc = do_QueryInterface(domdoc); + if (doc) + { + nsCOMPtr docEncoder; + + docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html"); + NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); + + docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0); + docEncoder->SetSelection(selection); + + nsAutoString buffer; + + rv = docEncoder->EncodeToString(buffer); + + if (NS_FAILED(rv)) + return rv; + + if ( !buffer.IsEmpty() ) + { + nsCOMPtr htmlConverter; + rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter), + getter_AddRefs(htmlConverter)); + if (NS_FAILED(rv)) return rv; + if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr dataWrapper; + rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper)); + if (NS_FAILED(rv)) return rv; + if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY; + + rv = trans->AddDataFlavor(kHTMLMime); + if (NS_FAILED(rv)) return rv; + rv = trans->SetConverter(htmlConverter); + if (NS_FAILED(rv)) return rv; + + rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) ); + if (NS_FAILED(rv)) return rv; + + // QI the data object an |nsISupports| so that when the transferable holds + // onto it, it will addref the correct interface. + nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) ); + rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2); + if (NS_FAILED(rv)) return rv; + + /* add the transferable to the array */ + rv = transferableArray->AppendElement(trans); + if (NS_FAILED(rv)) return rv; + + /* invoke drag */ + unsigned int flags; + // in some cases we'll want to cut rather than copy... hmmmmm... + // if ( wantToCut ) + // flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE; + // else + flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; + + rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags); + if (NS_FAILED(rv)) return rv; + + aDragEvent->PreventBubble(); + } + } + + return rv; +} + +NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) +{ + ForceCompositionEnd(); + + // Get Clipboard Service + nsresult rv; + NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv ); + if ( NS_FAILED(rv) ) + return rv; + + // Get the nsITransferable interface for getting the data from the clipboard + nsCOMPtr trans; + rv = PrepareTransferable(getter_AddRefs(trans)); + if (NS_SUCCEEDED(rv) && trans) + { + // Get the Data from the clipboard + if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) + { + // also get additional html copy hints, if present + nsAutoString contextStr, infoStr; + nsCOMPtr contextDataObj, infoDataObj; + PRUint32 contextLen, infoLen; + nsCOMPtr textDataObj; + + nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); + contextTrans->AddDataFlavor(kHTMLContext); + clipboard->GetData(contextTrans, aSelectionType); + contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); + + nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); + infoTrans->AddDataFlavor(kHTMLInfo); + clipboard->GetData(infoTrans, aSelectionType); + infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); + + if (contextDataObj) + { + PRUnichar* text = nsnull; + textDataObj = do_QueryInterface(contextDataObj); + textDataObj->ToString ( &text ); + contextStr.Assign ( text, contextLen / 2 ); + if (text) + nsMemory::Free(text); + } + + if (infoDataObj) + { + PRUnichar* text = nsnull; + textDataObj = do_QueryInterface(infoDataObj); + textDataObj->ToString ( &text ); + infoStr.Assign ( text, infoLen / 2 ); + if (text) + nsMemory::Free(text); + } + rv = InsertFromTransferable(trans, contextStr, infoStr); + } + } + + return rv; +} + + +NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste) +{ + aCanPaste = PR_FALSE; + + // can't paste if readonly + if (!IsModifiable()) + return NS_OK; + + nsresult rv; + NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv); + if (NS_FAILED(rv)) return rv; + + // the flavors that we can deal with + char* textEditorFlavors[] = { kUnicodeMime, nsnull }; + char* htmlEditorFlavors[] = { kJPEGImageMime, kHTMLMime, nsnull }; + + nsCOMPtr flavorsList; + rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList)); + if (NS_FAILED(rv)) return rv; + + PRUint32 editorFlags; + GetFlags(&editorFlags); + + // add the flavors for all editors + for (char** flavor = textEditorFlavors; *flavor; flavor++) + { + nsCOMPtr flavorString; + nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString)); + if (flavorString) + { + flavorString->SetData(*flavor); + flavorsList->AppendElement(flavorString); + } + } + + // add the HTML-editor only flavors + if ((editorFlags & eEditorPlaintextMask) == 0) + { + for (char** htmlFlavor = htmlEditorFlavors; *htmlFlavor; htmlFlavor++) + { + nsCOMPtr flavorString; + nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString)); + if (flavorString) + { + flavorString->SetData(*htmlFlavor); + flavorsList->AppendElement(flavorString); + } + } + } + + PRBool haveFlavors; + rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors); + if (NS_FAILED(rv)) return rv; + + aCanPaste = haveFlavors; + return NS_OK; +} + + +// +// HTML PasteAsQuotation: Paste in a blockquote type=cite +// +NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation(PRInt32 aSelectionType) +{ + if (mFlags & eEditorPlaintextMask) + return PasteAsPlaintextQuotation(aSelectionType); + + nsAutoString citation; + return PasteAsCitedQuotation(citation, aSelectionType); +} + +NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation, + PRInt32 aSelectionType) +{ + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext); + + // get selection + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + + // give rules a chance to handle or cancel + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement); + PRBool cancel, handled; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (cancel) return NS_OK; // rules canceled the operation + if (!handled) + { + nsCOMPtr newNode; + nsAutoString tag; tag.AssignWithConversion("blockquote"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (NS_FAILED(res)) return res; + if (!newNode) return NS_ERROR_NULL_POINTER; + + // Try to set type=cite. Ignore it if this fails. + nsCOMPtr newElement (do_QueryInterface(newNode)); + if (newElement) + { + nsAutoString type; type.AssignWithConversion("type"); + nsAutoString cite; cite.AssignWithConversion("cite"); + newElement->SetAttribute(type, cite); + } + + // Set the selection to the underneath the node we just inserted: + res = selection->Collapse(newNode, 0); + if (NS_FAILED(res)) + { +#ifdef DEBUG_akkana + printf("Couldn't collapse"); +#endif + // XXX: error result: should res be returned here? + } + + res = Paste(aSelectionType); + } + return res; +} + +// +// Paste a plaintext quotation +// +NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(PRInt32 aSelectionType) +{ + // Get Clipboard Service + nsresult rv; + NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv); + if (NS_FAILED(rv)) return rv; + + // Create generic Transferable for getting the data + nsCOMPtr trans; + rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + (void**) getter_AddRefs(trans)); + if (NS_SUCCEEDED(rv) && trans) + { + // We only handle plaintext pastes here + trans->AddDataFlavor(kUnicodeMime); + + // Get the Data from the clipboard + clipboard->GetData(trans, aSelectionType); + + // Now we ask the transferable for the data + // it still owns the data, we just have a pointer to it. + // If it can't support a "text" output of the data the call will fail + nsCOMPtr genericDataObj; + PRUint32 len = 0; + char* flav = 0; + rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj), + &len); + if (NS_FAILED(rv)) + { +#ifdef DEBUG_akkana + printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv); +#endif + return rv; + } +#ifdef DEBUG_akkana + printf("Got flavor [%s]\n", flav); +#endif + nsAutoString flavor; flavor.AssignWithConversion(flav); + nsAutoString stuffToPaste; + if (flavor.EqualsWithConversion(kUnicodeMime)) + { + nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + if (textDataObj && len > 0) + { + PRUnichar* text = nsnull; + textDataObj->ToString ( &text ); + stuffToPaste.Assign ( text, len / 2 ); + nsAutoEditBatch beginBatching(this); + rv = InsertAsPlaintextQuotation(stuffToPaste, 0); + if (text) + nsMemory::Free(text); + } + } + nsCRT::free(flav); + } + + return rv; +} + +NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText, + nsIDOMNode **aNodeInserted) +{ + if (mFlags & eEditorPlaintextMask) + return InsertAsPlaintextQuotation(aQuotedText, aNodeInserted); + + nsAutoString citation; + nsAutoString charset; + return InsertAsCitedQuotation(aQuotedText, citation, PR_FALSE, + charset, aNodeInserted); +} + +// text insert. +NS_IMETHODIMP +nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText, + nsIDOMNode **aNodeInserted) +{ + // We have the text. Cite it appropriately: + nsCOMPtr citer; + nsresult rv; + NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + char *citationType = 0; + rv = prefs->CopyCharPref("mail.compose.citationType", &citationType); + + if (NS_SUCCEEDED(rv) && citationType[0]) + { + if (!strncmp(citationType, "aol", 3)) + citer = new nsAOLCiter; + else + citer = new nsInternetCiter; + PL_strfree(citationType); + } + else + citer = new nsInternetCiter; + + // Let the citer quote it for us: + nsString quotedStuff; + rv = citer->GetCiteString(aQuotedText, quotedStuff); + if (!NS_SUCCEEDED(rv)) + return rv; + + // It's best to put a blank line after the quoted text so that mails + // written without thinking won't be so ugly. + quotedStuff.Append(PRUnichar('\n')); + + nsCOMPtr preNode; + // get selection + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + if (!selection) return NS_ERROR_NULL_POINTER; + else + { + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext); + + // give rules a chance to handle or cancel + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement); + PRBool cancel, handled; + rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(rv)) return rv; + if (cancel) return NS_OK; // rules canceled the operation + if (!handled) + { + // Wrap the inserted quote in a
 so it won't be wrapped:
+      nsAutoString tag; tag.AssignWithConversion("pre");
+      rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
+      
+      // If this succeeded, then set selection inside the pre
+      // so the inserted text will end up there.
+      // If it failed, we don't care what the return value was,
+      // but we'll fall through and try to insert the text anyway.
+      if (NS_SUCCEEDED(rv) && preNode)
+      {
+        // Add an attribute on the pre node so we'll know it's a quotation.
+        // Do this after the insertion, so that 
+        nsCOMPtr preElement (do_QueryInterface(preNode));
+        if (preElement)
+        {
+          preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"),
+                                   NS_LITERAL_STRING("true"));
+          // set style to not have unwanted vertical margins
+          preElement->SetAttribute(NS_LITERAL_STRING("style"),
+                                   NS_LITERAL_STRING("margin: 0 0 0 0px;"));
+        }
+
+        // and set the selection inside it:
+        selection->Collapse(preNode, 0);
+      }
+
+      rv = InsertText(quotedStuff.GetUnicode());
+
+      if (aNodeInserted && NS_SUCCEEDED(rv))
+      {
+        *aNodeInserted = preNode;
+        NS_IF_ADDREF(*aNodeInserted);
+      }
+    }
+  }
+    
+  // Set the selection to just after the inserted node:
+  if (NS_SUCCEEDED(rv) && preNode)
+  {
+    nsCOMPtr parent;
+    PRInt32 offset;
+    if (NS_SUCCEEDED(GetNodeLocation(preNode, address_of(parent), &offset)) && parent)
+      selection->Collapse(parent, offset+1);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
+                                     const nsString& aCitation,
+                                     PRBool aInsertHTML,
+                                     const nsString& aCharset,
+                                     nsIDOMNode **aNodeInserted)
+{
+  nsCOMPtr newNode;
+  nsresult res = NS_OK;
+
+  // get selection
+  nsCOMPtr selection;
+  res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+  else
+  {
+    nsAutoEditBatch beginBatching(this);
+    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+
+    // give rules a chance to handle or cancel
+    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
+    PRBool cancel, handled;
+    res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+    if (NS_FAILED(res)) return res;
+    if (cancel) return NS_OK; // rules canceled the operation
+    if (!handled)
+    {
+      nsAutoString tag; tag.AssignWithConversion("blockquote");
+      res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+      if (NS_FAILED(res)) return res;
+      if (!newNode) return NS_ERROR_NULL_POINTER;
+
+      // Try to set type=cite.  Ignore it if this fails.
+      nsCOMPtr newElement (do_QueryInterface(newNode));
+      if (newElement)
+      {
+        nsAutoString type; type.AssignWithConversion("type");
+        nsAutoString cite; cite.AssignWithConversion("cite");
+        newElement->SetAttribute(type, cite);
+
+        if (aCitation.Length() > 0)
+          newElement->SetAttribute(cite, aCitation);
+
+        // Set the selection inside the blockquote so aQuotedText will go there:
+        selection->Collapse(newNode, 0);
+      }
+
+      if (aInsertHTML)
+        res = InsertHTMLWithCharset(aQuotedText, aCharset);
+
+      else
+        res = InsertText(aQuotedText.GetUnicode());  // XXX ignore charset
+
+      if (aNodeInserted)
+      {
+        if (NS_SUCCEEDED(res))
+        {
+          *aNodeInserted = newNode;
+          NS_IF_ADDREF(*aNodeInserted);
+        }
+      }
+    }
+  }
+
+  // Set the selection to just after the inserted node:
+  if (NS_SUCCEEDED(res) && newNode)
+  {
+    nsCOMPtr parent;
+    PRInt32 offset;
+    if (NS_SUCCEEDED(GetNodeLocation(newNode, address_of(parent), &offset)) && parent)
+      selection->Collapse(parent, offset+1);
+  }
+  return res;
+}
+
+nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *nsrange,
+                                                  const nsString& aInputString,
+                                                  const nsString& aContextStr,
+                                                  const nsString& aInfoStr,
+                                                  nsCOMPtr *outFragNode,
+                                                  PRInt32 *outRangeStartHint,
+                                                  PRInt32 *outRangeEndHint)
+{
+  if (!outFragNode || !outRangeStartHint || !outRangeEndHint) 
+    return NS_ERROR_NULL_POINTER;
+  nsCOMPtr docfrag;
+  nsresult res = nsrange->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
+  NS_ENSURE_SUCCESS(res, res);
+  *outFragNode = do_QueryInterface(docfrag);
+
+  res = StripFormattingNodes(*outFragNode);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // if we have context info, create a fragment for that too
+  nsCOMPtr contextfrag;
+  nsCOMPtr contextLeaf;
+  PRInt32 contextDepth = 0;
+  if (aContextStr.Length())
+  {
+    res = nsrange->CreateContextualFragment(aContextStr, getter_AddRefs(contextfrag));
+    NS_ENSURE_SUCCESS(res, res);
+    nsCOMPtr contextAsNode (do_QueryInterface(contextfrag));
+    res = StripFormattingNodes(contextAsNode);
+    NS_ENSURE_SUCCESS(res, res);
+    // cache the deepest leaf in the context
+    nsCOMPtr junk, child, tmp = contextAsNode;
+    while (tmp)
+    {
+      contextDepth++;
+      contextLeaf = tmp;
+      contextLeaf->GetFirstChild(getter_AddRefs(tmp));
+    }
+    // unite the two trees
+    contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk));
+    *outFragNode = contextAsNode;
+    // no longer have fragmentAsNode in tree
+    contextDepth--;
+  }
+  
+  // get the infoString contents
+  nsAutoString numstr1, numstr2;
+  if (aInfoStr.Length())
+  {
+    PRInt32 err, sep;
+    sep = aInfoStr.FindChar((PRUnichar)',');
+    aInfoStr.Left(numstr1, sep);
+    aInfoStr.Mid(numstr2, sep+1, -1);
+    *outRangeStartHint = numstr1.ToInteger(&err) + contextDepth;
+    *outRangeEndHint   = numstr2.ToInteger(&err) + contextDepth;
+  }
+  else
+  {
+    *outRangeStartHint = contextDepth;
+    *outRangeEndHint = contextDepth;
+  }
+  return res;
+}
+
+nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode  *aFragmentAsNode,
+                                                nsCOMPtr *outNodeList,
+                                                PRInt32 aRangeStartHint,
+                                                PRInt32 aRangeEndHint)
+{
+  if (!outNodeList || !aFragmentAsNode) 
+    return NS_ERROR_NULL_POINTER;
+
+  // First off create a range over the portion of docFrag indicated by
+  // the range hints.
+  nsCOMPtr docFragRange;
+  docFragRange = do_CreateInstance(kCRangeCID);
+  nsCOMPtr startParent, endParent, tmp;
+  PRInt32 endOffset;
+  startParent = aFragmentAsNode;
+  while (aRangeStartHint > 0)
+  {
+    startParent->GetFirstChild(getter_AddRefs(tmp));
+    startParent = tmp;
+    aRangeStartHint--;
+    NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
+  }
+  endParent = aFragmentAsNode;
+  while (aRangeEndHint > 0)
+  {
+    endParent->GetLastChild(getter_AddRefs(tmp));
+    endParent = tmp;
+    aRangeEndHint--;
+    NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE);
+  }
+  nsresult res = GetLengthOfDOMNode(endParent, (PRUint32&)endOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  res = docFragRange->SetStart(startParent, 0);
+  NS_ENSURE_SUCCESS(res, res);
+  res = docFragRange->SetEnd(endParent, endOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // now use a subtree iterator over the range to create a list of nodes
+  nsTrivialFunctor functor;
+  nsDOMSubtreeIterator iter;
+  res = NS_NewISupportsArray(getter_AddRefs(*outNodeList));
+  NS_ENSURE_SUCCESS(res, res);
+  res = iter.Init(docFragRange);
+  NS_ENSURE_SUCCESS(res, res);
+  res = iter.AppendList(functor, *outNodeList);
+
+  return res;
+}
+
+
diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp
index f84cd25c849..c188cd3a2e5 100644
--- a/mozilla/editor/base/nsHTMLEditRules.cpp
+++ b/mozilla/editor/base/nsHTMLEditRules.cpp
@@ -94,11 +94,11 @@ class nsBRNodeFunctor : public nsBoolDomIterFunctor
 class nsEmptyFunctor : public nsBoolDomIterFunctor
 {
   public:
-    nsEmptyFunctor(nsHTMLEditor* editor) : mEditor(editor) {}
+    nsEmptyFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
     virtual PRBool operator()(nsIDOMNode* aNode)  // used to build list of empty li's and td's
     {
       PRBool bIsEmptyNode;
-      nsresult res = mEditor->IsEmptyNode(aNode, &bIsEmptyNode, PR_FALSE, PR_FALSE);
+      nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, PR_FALSE, PR_FALSE);
       if (NS_FAILED(res)) return PR_FALSE;
       if (bIsEmptyNode
         && (nsHTMLEditUtils::IsListItem(aNode) || nsHTMLEditUtils::IsTableCellOrCaption(aNode)))
@@ -108,23 +108,23 @@ class nsEmptyFunctor : public nsBoolDomIterFunctor
       return PR_FALSE;
     }
   protected:
-    nsHTMLEditor* mEditor;
+    nsHTMLEditor* mHTMLEditor;
 };
 
 class nsEditableTextFunctor : public nsBoolDomIterFunctor
 {
   public:
-    nsEditableTextFunctor(nsHTMLEditor* editor) : mEditor(editor) {}
+    nsEditableTextFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
     virtual PRBool operator()(nsIDOMNode* aNode)  // used to build list of empty li's and td's
     {
-      if (nsEditor::IsTextNode(aNode) && mEditor->IsEditable(aNode)) 
+      if (nsEditor::IsTextNode(aNode) && mHTMLEditor->IsEditable(aNode)) 
       {
         return PR_TRUE;
       }
       return PR_FALSE;
     }
   protected:
-    nsHTMLEditor* mEditor;
+    nsHTMLEditor* mHTMLEditor;
 };
 
 
@@ -161,7 +161,7 @@ nsHTMLEditRules::~nsHTMLEditRules()
   // ~nsHTMLEditor, in which case we will get an error here
   // which we ignore.  But this allows us to add the ability to
   // switch rule sets on the fly if we want.
-  mEditor->RemoveEditActionListener(this);
+  mHTMLEditor->RemoveEditActionListener(this);
 }
 
 /********************************************************
@@ -178,10 +178,13 @@ NS_IMPL_QUERY_INTERFACE3(nsHTMLEditRules, nsIHTMLEditRules, nsIEditRules, nsIEdi
  ********************************************************/
 
 NS_IMETHODIMP
-nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
+nsHTMLEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)
 {
-  // call through to base class Init first
-  nsresult res = nsTextEditRules::Init(aEditor, aFlags);
+  mHTMLEditor = NS_STATIC_CAST(nsHTMLEditor*, aEditor);
+  nsresult res;
+  
+  // call through to base class Init 
+  res = nsTextEditRules::Init(aEditor, aFlags);
   if (NS_FAILED(res)) return res;
 
   // cache any prefs we care about
@@ -207,15 +210,11 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
   // make a utility range for use by the listenter
   mUtilRange = do_CreateInstance(kRangeCID);
   if (!mUtilRange) return NS_ERROR_NULL_POINTER;
-  
-  // pass over document and add any needed mozBRs
-  // first turn off undo
-  mEditor->EnableUndo(PR_FALSE);
-  
+   
   // set up mDocChangeRange to be whole doc
   nsCOMPtr bodyElem;
   nsCOMPtr bodyNode;
-  mEditor->GetRootElement(getter_AddRefs(bodyElem));
+  mHTMLEditor->GetRootElement(getter_AddRefs(bodyElem));
   bodyNode = do_QueryInterface(bodyElem);
   if (bodyNode)
   {
@@ -227,17 +226,12 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
       if (!mDocChangeRange) return NS_ERROR_NULL_POINTER;
     }
     mDocChangeRange->SelectNode(bodyNode);
-    res = ReplaceNewlines(mDocChangeRange);
-    if (NS_FAILED(res)) return res;
     res = AdjustSpecialBreaks();
     if (NS_FAILED(res)) return res;
   }
-  
-  // turn on undo
-  mEditor->EnableUndo(PR_TRUE);
-  
+
   // add ourselves as a listener to edit actions
-  res = mEditor->AddEditActionListener(this);
+  res = mHTMLEditor->AddEditActionListener(this);
 
   return res;
 }
@@ -270,7 +264,7 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     }
     // turn off caret
     nsCOMPtr selCon;
-    mEditor->GetSelectionController(getter_AddRefs(selCon));
+    mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
     if (selCon) selCon->SetCaretEnabled(PR_FALSE);
     // check that selection is in subtree defined by body node
     ConfirmSelectionInBody();
@@ -297,7 +291,7 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     res = AfterEditInner(action, aDirection);
     // turn on caret
     nsCOMPtr selCon;
-    mEditor->GetSelectionController(getter_AddRefs(selCon));
+    mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
     if (selCon) selCon->SetCaretEnabled(PR_TRUE);
   }
 
@@ -312,7 +306,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
   if (action == nsEditor::kOpIgnore) return NS_OK;
   
   nsCOMPtrselection;
-  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
   
   // do we have a real range to act on?
@@ -330,7 +324,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
   {
     // dont let any txns in here move the selection around behind our back.
     // Note that this won't prevent explicit selection setting from working.
-    nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
+    nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    
     // expand the "changed doc range" as needed
     res = PromoteRange(mDocChangeRange, action);
@@ -344,7 +338,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
     if ( (action != nsEditor::kOpInsertText &&
          action != nsEditor::kOpInsertIMEText) )
     {
-      res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange);
+      res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
       if (NS_FAILED(res)) return res;
     }
     
@@ -377,7 +371,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
     if ((action == nsEditor::kOpInsertText) || 
         (action == nsEditor::kOpInsertIMEText) ||
         (action == nsEditor::kOpDeleteSelection) ||
-        (action == nsEditor::kOpHTMLPaste))
+        (action == nsHTMLEditor::kOpHTMLPaste))
     {
       res = AdjustSelection(selection, aDirection);
       if (NS_FAILED(res)) return res;
@@ -451,6 +445,8 @@ nsHTMLEditRules::DidDoAction(nsISelection *aSelection,
   nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo);
   switch (info->action)
   {
+    case kInsertBreak:
+      return DidInsertBreak(aSelection, aResult);
     case kMakeBasicBlock:
     case kIndent:
     case kOutdent:
@@ -488,11 +484,11 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL, PRBool &
     nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
     nsCOMPtr curNode( do_QueryInterface(isupports) );
     
-    if (mEditor->NodeIsType(curNode,nsIEditProperty::ul))
+    if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ul))
       aUL = PR_TRUE;
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::ol))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ol))
       aOL = PR_TRUE;
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::li))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::li))
     {
       nsCOMPtr parent;
       PRInt32 offset;
@@ -503,9 +499,9 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL, PRBool &
       else if (nsHTMLEditUtils::IsOrderedList(parent))
         aOL = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dl) ||
-             mEditor->NodeIsType(curNode,nsIEditProperty::dt) ||
-             mEditor->NodeIsType(curNode,nsIEditProperty::dd) )
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dl) ||
+             mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dt) ||
+             mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dd) )
     {
       aDL = PR_TRUE;
     }
@@ -540,21 +536,21 @@ nsHTMLEditRules::GetListItemState(PRBool &aMixed, PRBool &aLI, PRBool &aDT, PRBo
     nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
     nsCOMPtr curNode( do_QueryInterface(isupports) );
     
-    if (mEditor->NodeIsType(curNode,nsIEditProperty::ul) ||
-        mEditor->NodeIsType(curNode,nsIEditProperty::ol) ||
-        mEditor->NodeIsType(curNode,nsIEditProperty::li) )
+    if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ul) ||
+        mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ol) ||
+        mHTMLEditor->NodeIsType(curNode,nsIEditProperty::li) )
     {
       aLI = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dt))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dt))
     {
       aDT = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dd))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dd))
     {
       aDD = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dl))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dl))
     {
       // need to look inside dl and see which types of items it has
       PRBool bDT, bDD;
@@ -589,13 +585,13 @@ nsHTMLEditRules::GetAlignment(PRBool &aMixed, nsIHTMLEditor::EAlignment &aAlign)
   
   // get selection
   nsCOMPtrselection;
-  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
 
   // get selection location
   nsCOMPtr parent;
   PRInt32 offset;
-  res = mEditor->GetStartNodeAndOffset(selection, address_of(parent), &offset);
+  res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(parent), &offset);
   if (NS_FAILED(res)) return res;
   
   // is the selection collapsed?
@@ -609,7 +605,7 @@ nsHTMLEditRules::GetAlignment(PRBool &aMixed, nsIHTMLEditor::EAlignment &aAlign)
     // for divs with alignment on them
     nodeToExamine = parent;
   }
-  else if (mEditor->IsTextNode(parent)) 
+  else if (mHTMLEditor->IsTextNode(parent)) 
   {
     // if we are in a text node, then that is the node of interest
     nodeToExamine = parent;
@@ -618,7 +614,7 @@ nsHTMLEditRules::GetAlignment(PRBool &aMixed, nsIHTMLEditor::EAlignment &aAlign)
   {
     // otherwise we want to look at the first editable node after
     // {parent,offset} and it's ancestors for divs with alignment on them
-    mEditor->GetNextNode(parent, offset, PR_TRUE, getter_AddRefs(nodeToExamine));
+    mHTMLEditor->GetNextNode(parent, offset, PR_TRUE, getter_AddRefs(nodeToExamine));
   }
   
   if (!nodeToExamine) return NS_ERROR_NULL_POINTER;
@@ -719,9 +715,9 @@ nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat)
     nsCOMPtr selNode;
     PRInt32 selOffset;
     nsCOMPtrselection;
-    res = mEditor->GetSelection(getter_AddRefs(selection));
+    res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
     if (NS_FAILED(res)) return res;
-    res = mEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
+    res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
     if (NS_FAILED(res)) return res;
     isupports = do_QueryInterface(selNode);
     if (!isupports) return NS_ERROR_NULL_POINTER;
@@ -737,14 +733,14 @@ nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat)
     nsCOMPtr curNode( do_QueryInterface(isupports) );
 
     nsAutoString format;
-    nsCOMPtr atom = mEditor->GetTag(curNode);
+    nsCOMPtr atom = mHTMLEditor->GetTag(curNode);
     
-    if (mEditor->IsInlineNode(curNode))
+    if (mHTMLEditor->IsInlineNode(curNode))
     {
-      nsCOMPtr block = mEditor->GetBlockNodeParent(curNode);
+      nsCOMPtr block = mHTMLEditor->GetBlockNodeParent(curNode);
       if (block)
       {
-        nsCOMPtr blockAtom = mEditor->GetTag(block);
+        nsCOMPtr blockAtom = mHTMLEditor->GetTag(block);
         if ( nsIEditProperty::p == blockAtom.get()           ||
              nsIEditProperty::blockquote == blockAtom.get()  ||
              nsIEditProperty::address == blockAtom.get()     ||
@@ -811,9 +807,45 @@ nsHTMLEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel)
   nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel);
   if (NS_FAILED(res)) return res; 
   
+  // Adjust selection to prevent insertion after a moz-BR.
+  // this next only works for collapsed selections right now,
+  // because selection is a pain to work with when not collapsed.
+  // (no good way to extend start or end of selection)
+  PRBool bCollapsed;
+  res = aSelection->GetIsCollapsed(&bCollapsed);
+  if (NS_FAILED(res)) return res;
+  if (!bCollapsed) return NS_OK;
+
+  // if we are after a mozBR in the same block, then move selection
+  // to be before it
+  nsCOMPtr selNode, priorNode;
+  PRInt32 selOffset;
+  // get the (collapsed) selection location
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
+  if (NS_FAILED(res)) return res;
+  // get prior node
+  res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, &priorNode);
+  if (NS_SUCCEEDED(res) && priorNode && nsHTMLEditUtils::IsMozBR(priorNode))
+  {
+    nsCOMPtr block1, block2;
+    if (mHTMLEditor->IsBlockNode(selNode)) block1 = selNode;
+    else block1 = mHTMLEditor->GetBlockNodeParent(selNode);
+    block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
+  
+    if (block1 != block2) return NS_OK; 
+  
+    // if we are here then the selection is right after a mozBR
+    // that is in the same block as the selection.  We need to move
+    // the selection start to be before the mozBR.
+    res = nsEditor::GetNodeLocation(priorNode, &selNode, &selOffset);
+    if (NS_FAILED(res)) return res;
+    res = aSelection->Collapse(selNode,selOffset);
+    if (NS_FAILED(res)) return res;
+  }
+
   // we need to get the doc
   nsCOMPtrdoc;
-  res = mEditor->GetDocument(getter_AddRefs(doc));
+  res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
   if (NS_FAILED(res)) return res;
   if (!doc) return NS_ERROR_NULL_POINTER;
     
@@ -864,7 +896,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
   if (NS_FAILED(res)) return res;
   if (!bCollapsed)
   {
-    res = mEditor->DeleteSelection(nsIEditor::eNone);
+    res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
     if (NS_FAILED(res)) return res;
   }
 
@@ -875,23 +907,23 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
   *aCancel = PR_FALSE;
   
   // get the (collapsed) selection location
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
   if (NS_FAILED(res)) return res;
 
   // dont put text in places that cant have it
   nsAutoString textTag; textTag.AssignWithConversion("__moz_text");
-  if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, textTag))
+  if (!mHTMLEditor->IsTextNode(selNode) && !mHTMLEditor->CanContainTag(selNode, textTag))
     return NS_ERROR_FAILURE;
 
   // we need to get the doc
   nsCOMPtrdoc;
-  res = mEditor->GetDocument(getter_AddRefs(doc));
+  res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
   if (NS_FAILED(res)) return res;
   if (!doc) return NS_ERROR_NULL_POINTER;
     
   if (aAction == kInsertTextIME) 
   { 
-    res = mEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
+    res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
     if (NS_FAILED(res)) return res;
   }
   else // aAction == kInsertText
@@ -903,7 +935,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
     // is our text going to be PREformatted?  
     // We remember this so that we know how to handle tabs.
     PRBool isPRE;
-    res = mEditor->IsPreformatted(selNode, &isPRE);
+    res = mHTMLEditor->IsPreformatted(selNode, &isPRE);
     if (NS_FAILED(res)) return res;    
     
     // turn off the edit listener: we know how to
@@ -913,7 +945,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
     nsAutoLockListener lockit(&mListenerEnabled); 
     
     // dont spaz my selection in subtransactions
-    nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
+    nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
     nsSubsumeStr subStr;
     const PRUnichar *unicodeBuf = inString->GetUnicode();
     nsCOMPtr unused;
@@ -949,12 +981,12 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         // is it a return?
         if (subStr.EqualsWithConversion("\n"))
         {
-          res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
+          res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
           pos++;
         }
         else
         {
-          res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
+          res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
         }
         if (NS_FAILED(res)) return res;
       }
@@ -987,18 +1019,18 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         // is it a tab?
         if (subStr.EqualsWithConversion("\t"))
         {
-          res = mEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
+          res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
           pos++;
         }
         // is it a return?
         else if (subStr.EqualsWithConversion("\n"))
         {
-          res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
+          res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
           pos++;
         }
         else
         {
-          res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
+          res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
         }
         if (NS_FAILED(res)) return res;
       }
@@ -1039,7 +1071,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   if (NS_FAILED(res)) return res;
   if (!bCollapsed)
   {
-    res = mEditor->DeleteSelection(nsIEditor::eNone);
+    res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
     if (NS_FAILED(res)) return res;
   }
   
@@ -1049,26 +1081,26 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = PR_FALSE;
-
-
-  // split any mailcites in the way
-  if (mFlags & nsIHTMLEditor::eEditorMailMask)
+  
+  // split any mailcites in the way.
+  // should we abort this if we encounter table cell boundaries?
+  if (mFlags & nsIPlaintextEditor::eEditorMailMask)
   {
     nsCOMPtr citeNode, selNode;
     PRInt32 selOffset, newOffset;
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
     if (NS_FAILED(res)) return res;
-    res = GetTopEnclosingMailCite(selNode, address_of(citeNode));
+    PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
+    res = GetTopEnclosingMailCite(selNode, address_of(citeNode), bPlaintext);
     if (NS_FAILED(res)) return res;
-    
     if (citeNode)
     {
       nsCOMPtr brNode;
-      res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
+      res = mHTMLEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
       if (NS_FAILED(res)) return res;
       res = citeNode->GetParentNode(getter_AddRefs(selNode));
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateBR(selNode, newOffset, address_of(brNode));
+      res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
       if (NS_FAILED(res)) return res;
       // want selection before the break, and on same line
       selPriv->SetInterlinePosition(PR_TRUE);
@@ -1083,7 +1115,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   nsCOMPtr node;
   PRInt32 offset;
   
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
   if (NS_FAILED(res)) return res;
   if (!node) return NS_ERROR_FAILURE;
     
@@ -1093,7 +1125,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   if (nsEditor::IsBlockNode(node)) 
     blockParent = node;
   else 
-    blockParent = mEditor->GetBlockNodeParent(node);
+    blockParent = mHTMLEditor->GetBlockNodeParent(node);
     
   if (!blockParent) return NS_ERROR_FAILURE;
   
@@ -1123,7 +1155,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   else
   {
     nsCOMPtr brNode;
-    res = mEditor->CreateBR(node, offset, address_of(brNode));
+    res = mHTMLEditor->CreateBR(node, offset, address_of(brNode));
     if (NS_FAILED(res)) return res;
     res = nsEditor::GetNodeLocation(brNode, address_of(node), &offset);
     if (NS_FAILED(res)) return res;
@@ -1136,7 +1168,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
     // Then we stick to the left to aviod an uber caret.
     nsCOMPtr siblingNode;
     brNode->GetNextSibling(getter_AddRefs(siblingNode));
-    if (siblingNode && mEditor->IsBlockNode(siblingNode))
+    if (siblingNode && mHTMLEditor->IsBlockNode(siblingNode))
       selPriv->SetInterlinePosition(PR_FALSE);
     else 
       selPriv->SetInterlinePosition(PR_TRUE);
@@ -1149,6 +1181,12 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
 }
 
 
+nsresult
+nsHTMLEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
+{
+  return NS_OK;
+}
+
 
 nsresult
 nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, 
@@ -1182,22 +1220,22 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
   // first check for table selection mode.  If so,
   // hand off to table editor.
   nsCOMPtr cell;
-  res = mEditor->GetFirstSelectedCell(getter_AddRefs(cell), nsnull);
+  res = mHTMLEditor->GetFirstSelectedCell(getter_AddRefs(cell), nsnull);
   if (NS_SUCCEEDED(res) && cell)
   {
-    res = mEditor->DeleteTableCellContents();
+    res = mHTMLEditor->DeleteTableCellContents();
     *aHandled = PR_TRUE;
     return res;
   }
   
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
   if (NS_FAILED(res)) return res;
   if (!startNode) return NS_ERROR_FAILURE;
     
   if (bCollapsed)
   {
     // easy case, in a text node:
-    if (mEditor->IsTextNode(startNode))
+    if (mHTMLEditor->IsTextNode(startNode))
     {
       nsCOMPtr textNode = do_QueryInterface(startNode);
       PRUint32 strLength;
@@ -1208,7 +1246,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       if (!startOffset && (aAction == nsIEditor::ePrevious))
       {
         nsCOMPtr priorNode;
-        res = mEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
+        res = mHTMLEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
         if (NS_FAILED(res)) return res;
         
         // if there is no prior node then cancel the deletion
@@ -1219,24 +1257,24 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
         
         // block parents the same?  
-        if (mEditor->HasSameBlockNodeParent(startNode, priorNode)) 
+        if (mHTMLEditor->HasSameBlockNodeParent(startNode, priorNode)) 
         {
           // is prior node not a container?  (ie, a br, hr, image...)
-          if (!mEditor->IsContainer(priorNode))   // MOOSE: anchors not handled
+          if (!mHTMLEditor->IsContainer(priorNode))   // MOOSE: anchors not handled
           {
             // delete the break, and join like nodes if appropriate
-            res = mEditor->DeleteNode(priorNode);
+            res = mHTMLEditor->DeleteNode(priorNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
             // get new prior node
-            res = mEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
+            res = mHTMLEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
             if (NS_FAILED(res)) return res;
             // are they in same block?
-            if (mEditor->HasSameBlockNodeParent(startNode, priorNode)) 
+            if (mHTMLEditor->HasSameBlockNodeParent(startNode, priorNode)) 
             {
               // are they same type?
-              if (mEditor->IsTextNode(priorNode))
+              if (mHTMLEditor->IsTextNode(priorNode))
               {
                 // if so, join them!
                 nsCOMPtr topParent;
@@ -1250,7 +1288,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             return res;
           }
           // is prior node a text node?
-          else if ( mEditor->IsTextNode(priorNode) )
+          else if ( mHTMLEditor->IsTextNode(priorNode) )
           {
             // delete last character
             PRUint32 offset;
@@ -1262,15 +1300,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             // default code will take care of actual deletion
             return res;
           }
-          else if ( mEditor->IsInlineNode(priorNode) )
+          else if ( mHTMLEditor->IsInlineNode(priorNode) )
           {
             // remember where we are
             PRInt32 offset;
             nsCOMPtr node;
-            res = mEditor->GetNodeLocation(priorNode, address_of(node), &offset);
+            res = mHTMLEditor->GetNodeLocation(priorNode, address_of(node), &offset);
             if (NS_FAILED(res)) return res;
             // delete it
-            res = mEditor->DeleteNode(priorNode);
+            res = mHTMLEditor->DeleteNode(priorNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
@@ -1282,8 +1320,8 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
         
         // deleting across blocks
-        nsCOMPtr leftParent = mEditor->GetBlockNodeParent(priorNode);
-        nsCOMPtr rightParent = mEditor->GetBlockNodeParent(startNode);
+        nsCOMPtr leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
+        nsCOMPtr rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
         
         // if leftParent or rightParent is null, it's because the
         // corresponding selection endpoint is in the body node.
@@ -1298,7 +1336,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
                 
         // are the blocks of same type?
-        if (mEditor->NodesSameType(leftParent, rightParent))
+        if (mHTMLEditor->NodesSameType(leftParent, rightParent))
         {
           nsCOMPtr topParent;
           leftParent->GetParentNode(getter_AddRefs(topParent));
@@ -1321,35 +1359,35 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
           && (aAction == nsIEditor::eNext))
       {
         nsCOMPtr nextNode;
-        res = mEditor->GetNextHTMLNode(startNode, address_of(nextNode));
+        res = mHTMLEditor->GetNextHTMLNode(startNode, address_of(nextNode));
         if (NS_FAILED(res)) return res;
          
         // if there is no next node, or it's not in the body, then cancel the deletion
-        if (!nextNode || !nsHTMLEditUtils::InBody(nextNode, mEditor))
+        if (!nextNode || !nsHTMLEditUtils::InBody(nextNode, mHTMLEditor))
         {
           *aCancel = PR_TRUE;
           return res;
         }
        
         // block parents the same?  
-        if (mEditor->HasSameBlockNodeParent(startNode, nextNode)) 
+        if (mHTMLEditor->HasSameBlockNodeParent(startNode, nextNode)) 
         {
           // is next node not a container?  (ie, a br, hr, image...)
-          if (!mEditor->IsContainer(nextNode))  // MOOSE: anchors not handled
+          if (!mHTMLEditor->IsContainer(nextNode))  // MOOSE: anchors not handled
           {
             // delete the break, and join like nodes if appropriate
-            res = mEditor->DeleteNode(nextNode);
+            res = mHTMLEditor->DeleteNode(nextNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
             // get new next node
-            res = mEditor->GetNextHTMLNode(startNode, address_of(nextNode));
+            res = mHTMLEditor->GetNextHTMLNode(startNode, address_of(nextNode));
             if (NS_FAILED(res)) return res;
             // are they in same block?
-            if (mEditor->HasSameBlockNodeParent(startNode, nextNode)) 
+            if (mHTMLEditor->HasSameBlockNodeParent(startNode, nextNode)) 
             {
               // are they same type?
-              if ( mEditor->IsTextNode(nextNode) )
+              if ( mHTMLEditor->IsTextNode(nextNode) )
               {
                 // if so, join them!
                 nsCOMPtr topParent;
@@ -1363,7 +1401,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             return res;
           }
           // is next node a text node?
-          else if ( mEditor->IsTextNode(nextNode) )
+          else if ( mHTMLEditor->IsTextNode(nextNode) )
           {
             // delete first character
             nsCOMPtrnodeAsText;
@@ -1373,15 +1411,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             // default code will take care of actual deletion
             return res;
           }
-          else if ( mEditor->IsInlineNode(nextNode) )
+          else if ( mHTMLEditor->IsInlineNode(nextNode) )
           {
             PRInt32 offset;
             nsCOMPtr node;
             // remember where we are
-            res = mEditor->GetNodeLocation(nextNode, address_of(node), &offset);
+            res = mHTMLEditor->GetNodeLocation(nextNode, address_of(node), &offset);
             if (NS_FAILED(res)) return res;
             // delete it
-            res = mEditor->DeleteNode(nextNode);
+            res = mHTMLEditor->DeleteNode(nextNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
@@ -1393,8 +1431,8 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
                 
         // deleting across blocks
-        nsCOMPtr leftParent = mEditor->GetBlockNodeParent(startNode);
-        nsCOMPtr rightParent = mEditor->GetBlockNodeParent(nextNode);
+        nsCOMPtr leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
+        nsCOMPtr rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
 
         // if leftParent or rightParent is null, it's because the
         // corresponding selection endpoint is in the body node.
@@ -1409,7 +1447,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
                 
         // are the blocks of same type?
-        if (mEditor->NodesSameType(leftParent, rightParent))
+        if (mHTMLEditor->NodesSameType(leftParent, rightParent))
         {
           nsCOMPtr topParent;
           leftParent->GetParentNode(getter_AddRefs(topParent));
@@ -1439,7 +1477,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       // see if we are in an "empty" node.
       // Note: do NOT delete table elements this way.
       PRBool bIsEmptyNode;
-      res = mEditor->IsEmptyNode(startNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
+      res = mHTMLEditor->IsEmptyNode(startNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
       if (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(startNode))
         nodeToDelete = startNode;
       else 
@@ -1455,16 +1493,16 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         
         if (aAction == nsIEditor::ePrevious)
         {
-          res = mEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBlock));
+          res = mHTMLEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBlock));
           if (NS_FAILED(res)) return res;
-          res = mEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBreak));
+          res = mHTMLEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBreak));
           if (NS_FAILED(res)) return res;
         }
         else if (aAction == nsIEditor::eNext)
         {
-          res = mEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBreak));
+          res = mHTMLEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBreak));
           if (NS_FAILED(res)) return res;
-          res = mEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBlock));
+          res = mHTMLEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBlock));
           if (NS_FAILED(res)) return res;
         }
         
@@ -1472,9 +1510,9 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             nsHTMLEditUtils::IsBreak(maybeBreak) && nsEditor::IsBlockNode(maybeBlock))
           nodeToDelete = maybeBreak;
         else if (aAction == nsIEditor::ePrevious)
-          res = mEditor->GetPriorHTMLNode(startNode, startOffset, address_of(nodeToDelete));
+          res = mHTMLEditor->GetPriorHTMLNode(startNode, startOffset, address_of(nodeToDelete));
         else if (aAction == nsIEditor::eNext)
-          res = mEditor->GetNextHTMLNode(startNode, startOffset, address_of(nodeToDelete));
+          res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(nodeToDelete));
         else
           return NS_OK;
       } 
@@ -1513,19 +1551,19 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         if (nsHTMLEditUtils::IsMozBR(nodeToDelete))
         {
           nsCOMPtr brNode;
-          res = mEditor->GetPriorHTMLNode(nodeToDelete, address_of(brNode));
+          res = mHTMLEditor->GetPriorHTMLNode(nodeToDelete, address_of(brNode));
           if (nsHTMLEditUtils::IsBreak(brNode))
           {
             // is brNode also a descendant of same block?
             nsCOMPtr block, brBlock;
-            block = mEditor->GetBlockNodeParent(nodeToDelete);
-            brBlock = mEditor->GetBlockNodeParent(brNode);
+            block = mHTMLEditor->GetBlockNodeParent(nodeToDelete);
+            brBlock = mHTMLEditor->GetBlockNodeParent(brNode);
             if (block == brBlock)
             {
               // delete both breaks
-              res = mEditor->DeleteNode(brNode);
+              res = mHTMLEditor->DeleteNode(brNode);
               if (NS_FAILED(res)) return res;
-              res = mEditor->DeleteNode(nodeToDelete);
+              res = mHTMLEditor->DeleteNode(nodeToDelete);
               *aHandled = PR_TRUE;
               return res;
             }
@@ -1536,7 +1574,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         // adjust selection to be right after it
         res = aSelection->Collapse(node, offset+1);
         if (NS_FAILED(res)) return res;
-        res = mEditor->DeleteNode(nodeToDelete);
+        res = mHTMLEditor->DeleteNode(nodeToDelete);
         *aHandled = PR_TRUE;
         return res;
       }
@@ -1549,7 +1587,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
   // figure out if the enpoints are in nodes that can be merged
   nsCOMPtr endNode;
   PRInt32 endOffset;
-  res = mEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
+  res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
   if (NS_FAILED(res)) 
   { 
     return res; 
@@ -1557,7 +1595,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
   if (endNode.get() != startNode.get())
   {
     // block parents the same?  use default deletion
-    if (mEditor->HasSameBlockNodeParent(startNode, endNode)) return NS_OK;
+    if (mHTMLEditor->HasSameBlockNodeParent(startNode, endNode)) return NS_OK;
     
     // deleting across blocks
     // are the blocks of same type?
@@ -1574,12 +1612,12 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
     if (nsHTMLEditUtils::IsBody(startNode))
       leftParent = startNode;
     else
-      leftParent = mEditor->GetBlockNodeParent(startNode);
+      leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
 
     if (nsHTMLEditUtils::IsBody(endNode))
       rightParent = endNode;
     else
-      rightParent = mEditor->GetBlockNodeParent(endNode);
+      rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
     
     // are the blocks siblings?
     nsCOMPtr leftBlockParent;
@@ -1589,7 +1627,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
 
     // MOOSE: this could conceivably screw up a table.. fix me.
     if (   (leftBlockParent.get() == rightBlockParent.get())
-        && (mEditor->NodesSameType(leftParent, rightParent))  )
+        && (mHTMLEditor->NodesSameType(leftParent, rightParent))  )
     {
       nsCOMPtr topParent;
       leftParent->GetParentNode(getter_AddRefs(topParent));
@@ -1598,10 +1636,10 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       {
         // first delete the selection
         *aHandled = PR_TRUE;
-        res = mEditor->DeleteSelectionImpl(aAction);
+        res = mHTMLEditor->DeleteSelectionImpl(aAction);
         if (NS_FAILED(res)) return res;
         // then join para's, insert break
-        res = mEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
+        res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
         if (NS_FAILED(res)) return res;
         // fix up selection
         res = aSelection->Collapse(selNode,selOffset);
@@ -1612,10 +1650,10 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       {
         // first delete the selection
         *aHandled = PR_TRUE;
-        res = mEditor->DeleteSelectionImpl(aAction);
+        res = mHTMLEditor->DeleteSelectionImpl(aAction);
         if (NS_FAILED(res)) return res;
         // join blocks
-        res = mEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
+        res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
         if (NS_FAILED(res)) return res;
         // fix up selection
         res = aSelection->Collapse(selNode,selOffset);
@@ -1669,7 +1707,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
     // delete to end or to begining as appropriate,
     // since the case where both sel endpoints in same
     // text node was already handled (we wouldn't be here)
-    if ( mEditor->IsTextNode(startNode) )
+    if ( mHTMLEditor->IsTextNode(startNode) )
     {
       // delete to last character
       nsCOMPtrnodeAsText;
@@ -1678,18 +1716,18 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       nodeAsText->GetLength(&len);
       if (len > (PRUint32)startOffset)
       {
-        res = mEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
+        res = mHTMLEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
         if (NS_FAILED(res)) return res;
       }
     }
-    if ( mEditor->IsTextNode(endNode) )
+    if ( mHTMLEditor->IsTextNode(endNode) )
     {
       // delete to first character
       nsCOMPtrnodeAsText;
       nodeAsText = do_QueryInterface(endNode);
       if (endOffset)
       {
-        res = mEditor->DeleteText(nodeAsText,0,endOffset);
+        res = mHTMLEditor->DeleteText(nodeAsText,0,endOffset);
         if (NS_FAILED(res)) return res;
       }
     }
@@ -1733,7 +1771,7 @@ nsHTMLEditRules::DeleteNonTableElements(nsIDOMNode *aNode)
   }
   else
   {
-    res = mEditor->DeleteNode(aNode);
+    res = mHTMLEditor->DeleteNode(aNode);
     if (NS_FAILED(res)) return res;
   }
   return res;
@@ -1773,7 +1811,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
   
   *aHandled = PR_TRUE;
 
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   nsCOMPtr arrayOfNodes;
   res = GetListActionNodes(address_of(arrayOfNodes), aEntireList);
@@ -1810,21 +1848,21 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
       {
         nsCOMPtr isupports  = (dont_AddRef)(arrayOfNodes->ElementAt(j));
         nsCOMPtr curNode( do_QueryInterface(isupports ) );
-        res = mEditor->DeleteNode(curNode);
+        res = mHTMLEditor->DeleteNode(curNode);
         if (NS_FAILED(res)) return res;
       }
     }
     
     // get selection location
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     
     // make sure we can put a list here
     res = SplitAsNeeded(aListType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
+    res = mHTMLEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
+    res = mHTMLEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
     if (NS_FAILED(res)) return res;
     // put selection in new list item
     res = aSelection->Collapse(theListItem,0);
@@ -1846,7 +1884,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     {
       // dive as long as there is only one child, and it is a list, div, blockquote
       PRUint32 numChildren;
-      res = mEditor->CountEditableChildren(curNode, numChildren);
+      res = mHTMLEditor->CountEditableChildren(curNode, numChildren);
       if (NS_FAILED(res)) return res;
       
       if (numChildren == 1)
@@ -1899,20 +1937,20 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     // if curNode is a Break, delete it, and quit remembering prev list item
     if (nsHTMLEditUtils::IsBreak(curNode)) 
     {
-      res = mEditor->DeleteNode(curNode);
+      res = mHTMLEditor->DeleteNode(curNode);
       if (NS_FAILED(res)) return res;
       prevListItem = 0;
       continue;
     }
     // if curNode is an empty inline container, delete it
-    else if (mEditor->IsInlineNode(curNode) && mEditor->IsContainer(curNode)) 
+    else if (mHTMLEditor->IsInlineNode(curNode) && mHTMLEditor->IsContainer(curNode)) 
     {
       PRBool bEmpty;
-      res = mEditor->IsEmptyNode(curNode, &bEmpty);
+      res = mHTMLEditor->IsEmptyNode(curNode, &bEmpty);
       if (NS_FAILED(res)) return res;
       if (bEmpty)
       {
-        res = mEditor->DeleteNode(curNode);
+        res = mHTMLEditor->DeleteNode(curNode);
         if (NS_FAILED(res)) return res;
         continue;
       }
@@ -1921,7 +1959,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     if (nsHTMLEditUtils::IsList(curNode))
     {
       nsAutoString existingListStr;
-      res = mEditor->GetTagString(curNode, existingListStr);
+      res = mHTMLEditor->GetTagString(curNode, existingListStr);
       existingListStr.ToLowerCase();
       // do we have a curList already?
       if (curList && !nsHTMLEditUtils::IsDescendantOf(curNode, curList))
@@ -1931,11 +1969,11 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // RemoveContainer() on the list.
         // ConvertListType first: that routine
         // handles converting the list item types, if needed
-        res = mEditor->MoveNode(curNode, curList, -1);
+        res = mHTMLEditor->MoveNode(curNode, curList, -1);
         if (NS_FAILED(res)) return res;
         res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
         if (NS_FAILED(res)) return res;
-        res = mEditor->RemoveContainer(newBlock);
+        res = mHTMLEditor->RemoveContainer(newBlock);
         if (NS_FAILED(res)) return res;
       }
       else
@@ -1951,7 +1989,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     if (nsHTMLEditUtils::IsListItem(curNode))
     {
       nsAutoString existingListStr;
-      res = mEditor->GetTagString(curParent, existingListStr);
+      res = mHTMLEditor->GetTagString(curParent, existingListStr);
       existingListStr.ToLowerCase();
       if ( existingListStr != *aListType )
       {
@@ -1960,22 +1998,22 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // and make a new list of correct type.
         if (!curList || nsHTMLEditUtils::IsDescendantOf(curNode, curList))
         {
-          res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
+          res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
           if (NS_FAILED(res)) return res;
           nsCOMPtr p;
           PRInt32 o;
           res = nsEditor::GetNodeLocation(curParent, address_of(p), &o);
           if (NS_FAILED(res)) return res;
-          res = mEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
+          res = mHTMLEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
           if (NS_FAILED(res)) return res;
         }
         // move list item to new list
-        res = mEditor->MoveNode(curNode, curList, -1);
+        res = mHTMLEditor->MoveNode(curNode, curList, -1);
         if (NS_FAILED(res)) return res;
         // convert list item type if needed
-        if (!mEditor->NodeIsType(curNode,itemType))
+        if (!mHTMLEditor->NodeIsType(curNode,itemType))
         {
-          res = mEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
+          res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
           if (NS_FAILED(res)) return res;
         }
       }
@@ -1990,13 +2028,13 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
           if (curParent != curList)
           {
             // move list item to new list
-            res = mEditor->MoveNode(curNode, curList, -1);
+            res = mHTMLEditor->MoveNode(curNode, curList, -1);
             if (NS_FAILED(res)) return res;
           }
         }
-        if (!mEditor->NodeIsType(curNode,itemType))
+        if (!mHTMLEditor->NodeIsType(curNode,itemType))
         {
-          res = mEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
+          res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
           if (NS_FAILED(res)) return res;
         }
       }
@@ -2009,7 +2047,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     {
       res = SplitAsNeeded(aListType, address_of(curParent), &offset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
+      res = mHTMLEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
       if (NS_FAILED(res)) return res;
       // curList is now the correct thing to put curNode in
       prevListItem = 0;
@@ -2024,9 +2062,9 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // this is a continuation of some inline nodes that belong together in
         // the same list item.  use prevListItem
         PRUint32 listItemLen;
-        res = mEditor->GetLengthOfDOMNode(prevListItem, listItemLen);
+        res = mHTMLEditor->GetLengthOfDOMNode(prevListItem, listItemLen);
         if (NS_FAILED(res)) return res;
-        res = mEditor->MoveNode(curNode, prevListItem, listItemLen);
+        res = mHTMLEditor->MoveNode(curNode, prevListItem, listItemLen);
         if (NS_FAILED(res)) return res;
       }
       else
@@ -2034,11 +2072,11 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // don't wrap li around a paragraph.  instead replace paragraph with li
         if (nsHTMLEditUtils::IsParagraph(curNode))
         {
-          res = mEditor->ReplaceContainer(curNode, address_of(listItem), itemType);
+          res = mHTMLEditor->ReplaceContainer(curNode, address_of(listItem), itemType);
         }
         else
         {
-          res = mEditor->InsertContainerAbove(curNode, address_of(listItem), itemType);
+          res = mHTMLEditor->InsertContainerAbove(curNode, address_of(listItem), itemType);
         }
         if (NS_FAILED(res)) return res;
         if (nsEditor::IsInlineNode(curNode)) 
@@ -2054,9 +2092,9 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     {
       // tuck the listItem into the end of the active list
       PRUint32 listLen;
-      res = mEditor->GetLengthOfDOMNode(curList, listLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(listItem, curList, listLen);
+      res = mHTMLEditor->MoveNode(listItem, curList, listLen);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2076,7 +2114,7 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
   *aCancel = PR_FALSE;
   *aHandled = PR_TRUE;
   
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   
   nsCOMPtr arrayOfRanges;
   nsresult res = GetPromotedRanges(aSelection, address_of(arrayOfRanges), kMakeList);
@@ -2096,7 +2134,7 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
   {
     nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
     nsCOMPtr testNode( do_QueryInterface(isupports ) );
-    if (!mEditor->IsEditable(testNode))
+    if (!mHTMLEditor->IsEditable(testNode))
     {
       arrayOfNodes->RemoveElementAt(i);
     }
@@ -2141,13 +2179,13 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
         else
         {
           // delete any non- list items for now
-          res = mEditor->DeleteNode(child);
+          res = mHTMLEditor->DeleteNode(child);
           if (NS_FAILED(res)) return res;
         }
         curNode->GetLastChild(getter_AddRefs(child));
       }
       // delete the now-empty list
-      res = mEditor->DeleteNode(curNode);
+      res = mHTMLEditor->DeleteNode(curNode);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2184,8 +2222,8 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = PR_FALSE;
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
-  nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
+  nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
   *aHandled = PR_TRUE;
 
   nsCOMPtr arrayOfRanges;
@@ -2206,13 +2244,13 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
     PRInt32 offset;
     
     // get selection location
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     
     // make sure we can put a block here
     res = SplitAsNeeded(aBlockType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
+    res = mHTMLEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
     if (NS_FAILED(res)) return res;
     // put selection in new block
     res = aSelection->Collapse(theBlock,0);
@@ -2266,7 +2304,7 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
   *aCancel = PR_FALSE;
   *aHandled = PR_TRUE;
 
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
@@ -2292,13 +2330,13 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
     nsAutoString quoteType; quoteType.AssignWithConversion("blockquote");
     
     // get selection location
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     
     // make sure we can put a block here
     res = SplitAsNeeded("eType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
+    res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
     if (NS_FAILED(res)) return res;
     // put selection in new block
     res = aSelection->Collapse(theBlock,0);
@@ -2341,15 +2379,15 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         // create a new nested list of correct type
         res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
         if (NS_FAILED(res)) return res;
-        res = mEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
+        res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
         if (NS_FAILED(res)) return res;
         // curList is now the correct thing to put curNode in
       }
       // tuck the node into the end of the active list
       PRUint32 listLen;
-      res = mEditor->GetLengthOfDOMNode(curList, listLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(curNode, curList, listLen);
+      res = mHTMLEditor->MoveNode(curNode, curList, listLen);
       if (NS_FAILED(res)) return res;
     }
     
@@ -2362,13 +2400,13 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         nsAutoString quoteType; quoteType.AssignWithConversion("blockquote");
         res = SplitAsNeeded("eType, address_of(curParent), &offset);
         if (NS_FAILED(res)) return res;
-        res = mEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
+        res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
         if (NS_FAILED(res)) return res;
         
 /* !!!!!!!!!!!!!!!  TURNED OFF PER BUG 33213 !!!!!!!!!!!!!!!!!!!!
         // set style to not have unwanted vertical margins
         nsCOMPtr quoteElem = do_QueryInterface(curQuote);
-        res = mEditor->SetAttribute(quoteElem, NS_ConvertASCIItoUCS2("style"), NS_ConvertASCIItoUCS2("margin: 0 0 0 40px;"));
+        res = mHTMLEditor->SetAttribute(quoteElem, NS_ConvertASCIItoUCS2("style"), NS_ConvertASCIItoUCS2("margin: 0 0 0 40px;"));
         if (NS_FAILED(res)) return res;
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
 
@@ -2377,9 +2415,9 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         
       // tuck the node into the end of the active blockquote
       PRUint32 quoteLen;
-      res = mEditor->GetLengthOfDOMNode(curQuote, quoteLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curQuote, quoteLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(curNode, curQuote, quoteLen);
+      res = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2395,7 +2433,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
   *aCancel = PR_FALSE;
   *aHandled = PR_TRUE;
   
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   nsresult res = NS_OK;
   
   // convert the selection ranges into "promoted" selection ranges:
@@ -2439,7 +2477,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
     {
       if (nsHTMLEditUtils::IsList(curNode))  // just unwrap this sublist
       {
-        res = mEditor->RemoveContainer(curNode);
+        res = mHTMLEditor->RemoveContainer(curNode);
         if (NS_FAILED(res)) return res;
       }
       else  // we are moving a list item, but not whole list
@@ -2464,13 +2502,13 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         else
         {
           // delete any non- list items for now
-          res = mEditor->DeleteNode(child);
+          res = mHTMLEditor->DeleteNode(child);
           if (NS_FAILED(res)) return res;
         }
         curNode->GetLastChild(getter_AddRefs(child));
       }
       // delete the now-empty list
-      res = mEditor->DeleteNode(curNode);
+      res = mHTMLEditor->DeleteNode(curNode);
       if (NS_FAILED(res)) return res;
     }
     else if (transitionList[i])  // not list related - look for enclosing blockquotes and remove
@@ -2485,7 +2523,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         {
           res = AddTerminatingBR(n);
           if (NS_FAILED(res)) return res;
-          mEditor->RemoveContainer(n);
+          mHTMLEditor->RemoveContainer(n);
           break;
         }
         n->GetParentNode(getter_AddRefs(tmp));
@@ -2515,24 +2553,24 @@ nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
   aList->GetFirstChild(getter_AddRefs(child));
   while (child)
   {
-    if (nsHTMLEditUtils::IsListItem(child) && !mEditor->NodeIsType(child, aItemType))
+    if (nsHTMLEditUtils::IsListItem(child) && !mHTMLEditor->NodeIsType(child, aItemType))
     {
-      res = mEditor->ReplaceContainer(child, address_of(temp), aItemType);
+      res = mHTMLEditor->ReplaceContainer(child, address_of(temp), aItemType);
       if (NS_FAILED(res)) return res;
       child = temp;
     }
-    else if (nsHTMLEditUtils::IsList(child) && !mEditor->NodeIsType(child, aListType))
+    else if (nsHTMLEditUtils::IsList(child) && !mHTMLEditor->NodeIsType(child, aListType))
     {
-      res = mEditor->ReplaceContainer(child, address_of(temp), aListType);
+      res = mHTMLEditor->ReplaceContainer(child, address_of(temp), aListType);
       if (NS_FAILED(res)) return res;
       child = temp;
     }
     child->GetNextSibling(getter_AddRefs(temp));
     child = temp;
   }
-  if (!mEditor->NodeIsType(aList, aListType))
+  if (!mHTMLEditor->NodeIsType(aList, aListType))
   {
-    res = mEditor->ReplaceContainer(aList, outList, aListType);
+    res = mHTMLEditor->ReplaceContainer(aList, outList, aListType);
   }
   return res;
 }
@@ -2547,32 +2585,32 @@ nsresult
 nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc) 
 {
   if (!aSelection || !aDoc) return NS_ERROR_NULL_POINTER;
-  if (!mEditor->mTypeInState) return NS_ERROR_NULL_POINTER;
+  if (!mHTMLEditor->mTypeInState) return NS_ERROR_NULL_POINTER;
   
   PRBool weDidSometing = PR_FALSE;
   nsCOMPtr node;
   PRInt32 offset;
-  nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
+  nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
   if (NS_FAILED(res)) return res;
   PropItem *item = nsnull;
   
   // process clearing any styles first
-  mEditor->mTypeInState->TakeClearProperty(&item);
+  mHTMLEditor->mTypeInState->TakeClearProperty(&item);
   while (item)
   {
     nsCOMPtr leftNode, rightNode, secondSplitParent, newSelParent;
-    res = mEditor->SplitStyleAbovePoint(address_of(node), &offset, item->tag, &item->attr, address_of(leftNode), address_of(rightNode));
+    res = mHTMLEditor->SplitStyleAbovePoint(address_of(node), &offset, item->tag, &item->attr, address_of(leftNode), address_of(rightNode));
     if (NS_FAILED(res)) return res;
     if (rightNode)
     {
-      res = mEditor->GetLeftmostChild(rightNode, getter_AddRefs(secondSplitParent));
+      res = mHTMLEditor->GetLeftmostChild(rightNode, getter_AddRefs(secondSplitParent));
       if (NS_FAILED(res)) return res;
       offset = 0;
-      res = mEditor->SplitStyleAbovePoint(address_of(secondSplitParent), &offset, item->tag, &(item->attr), address_of(leftNode), address_of(rightNode));
+      res = mHTMLEditor->SplitStyleAbovePoint(address_of(secondSplitParent), &offset, item->tag, &(item->attr), address_of(leftNode), address_of(rightNode));
       if (NS_FAILED(res)) return res;
       // should be impossible to not get a new leftnode here
       if (!leftNode) return NS_ERROR_FAILURE;
-      res = mEditor->GetLeftmostChild(leftNode, getter_AddRefs(newSelParent));
+      res = mHTMLEditor->GetLeftmostChild(leftNode, getter_AddRefs(newSelParent));
       if (NS_FAILED(res)) return res;
       // register a rangeStore item that points at the new heirarchy.
       // This is so we can know where to put the selection after we call
@@ -2584,36 +2622,36 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
       rangeItem->endNode = newSelParent;
       rangeItem->startOffset = 0;
       rangeItem->endOffset = 0;
-      mEditor->mRangeUpdater.RegisterRangeItem(rangeItem);
+      mHTMLEditor->mRangeUpdater.RegisterRangeItem(rangeItem);
       // remove the style on this new heirarchy
-      res = mEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
+      res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
       if (NS_FAILED(res)) return res;
       // reset our node offset values to the resulting new sel point
-      mEditor->mRangeUpdater.DropRangeItem(rangeItem);
+      mHTMLEditor->mRangeUpdater.DropRangeItem(rangeItem);
       node  = rangeItem->startNode;
       offset = rangeItem->startOffset;
       delete rangeItem;
     }
     // we own item now (TakeClearProperty hands ownership to us)
     delete item;
-    mEditor->mTypeInState->TakeClearProperty(&item);
+    mHTMLEditor->mTypeInState->TakeClearProperty(&item);
     weDidSometing = PR_TRUE;
   }
   
   // then process setting any styles
   PRInt32 relFontSize;
   
-  res = mEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
+  res = mHTMLEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
   if (NS_FAILED(res)) return res;
-  res = mEditor->mTypeInState->TakeSetProperty(&item);
+  res = mHTMLEditor->mTypeInState->TakeSetProperty(&item);
   if (NS_FAILED(res)) return res;
   
   if (item || relFontSize) // we have at least one style to add; make a
   {                        // new text node to insert style nodes above.
-    if (mEditor->IsTextNode(node))
+    if (mHTMLEditor->IsTextNode(node))
     {
       // if we are in a text node, split it
-      res = mEditor->SplitNodeDeep(node, node, offset, &offset);
+      res = mHTMLEditor->SplitNodeDeep(node, node, offset, &offset);
       if (NS_FAILED(res)) return res;
       nsCOMPtr tmp;
       node->GetParentNode(getter_AddRefs(tmp));
@@ -2625,7 +2663,7 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
     if (NS_FAILED(res)) return res;
     if (!nodeAsText) return NS_ERROR_NULL_POINTER;
     newNode = do_QueryInterface(nodeAsText);
-    res = mEditor->InsertNode(newNode, node, offset);
+    res = mHTMLEditor->InsertNode(newNode, node, offset);
     if (NS_FAILED(res)) return res;
     node = newNode;
     offset = 0;
@@ -2639,18 +2677,18 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
       else dir = -1;
       for (j=0; jRelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
+        res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
         if (NS_FAILED(res)) return res;
       }
     }
     
     while (item)
     {
-      res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
+      res = mHTMLEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
       if (NS_FAILED(res)) return res;
       // we own item now (TakeSetProperty hands ownership to us)
       delete item;
-      mEditor->mTypeInState->TakeSetProperty(&item);
+      mHTMLEditor->mTypeInState->TakeSetProperty(&item);
     }
   }
   if (weDidSometing)
@@ -2681,7 +2719,7 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
 //  looks like I forgot to finish this.  Wonder what I was going to do?
 
   if (!nodeToTest) return NS_ERROR_NULL_POINTER;
-  return mEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
+  return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
                      aMozBRDoesntCount, aListItemsNotEmpty);
 }
 
@@ -2702,7 +2740,7 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
   *aCancel = PR_FALSE;
   *aHandled = PR_FALSE;
   
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
@@ -2739,33 +2777,33 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
     PRInt32 offset;
     nsCOMPtr brNode, parent, theDiv, sib;
     nsAutoString divType; divType.AssignWithConversion("div");
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     res = SplitAsNeeded(&divType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     // consume a trailing br, if any.  This is to keep an alignment from
     // creating extra lines, if possible.
-    res = mEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
+    res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
     if (NS_FAILED(res)) return res;
     if (brNode && nsHTMLEditUtils::IsBreak(brNode))
     {
       // making use of html structure... if next node after where
       // we are putting our div is not a block, then the br we 
       // found is in same block we are, so its safe to consume it.
-      res = mEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
+      res = mHTMLEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
       if (NS_FAILED(res)) return res;
       if (!nsEditor::IsBlockNode(sib))
       {
-        res = mEditor->DeleteNode(brNode);
+        res = mHTMLEditor->DeleteNode(brNode);
         if (NS_FAILED(res)) return res;
       }
     }
-    res = mEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
+    res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
     if (NS_FAILED(res)) return res;
     // set up the alignment on the div
     nsCOMPtr divElem = do_QueryInterface(theDiv);
     nsAutoString attr; attr.AssignWithConversion("align");
-    res = mEditor->SetAttribute(divElem, attr, *alignType);
+    res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
     if (NS_FAILED(res)) return res;
     *aHandled = PR_TRUE;
     // put in a moz-br so that it won't get deleted
@@ -2803,7 +2841,7 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
     {
       nsCOMPtr divElem = do_QueryInterface(curNode);
       nsAutoString attr; attr.AssignWithConversion("align");
-      res = mEditor->SetAttribute(divElem, attr, *alignType);
+      res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
       if (NS_FAILED(res)) return res;
       // clear out curDiv so that we don't put nodes after this one into it
       curDiv = 0;
@@ -2829,21 +2867,21 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
       nsAutoString divType; divType.AssignWithConversion("div");
       res = SplitAsNeeded(&divType, address_of(curParent), &offset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
+      res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
       if (NS_FAILED(res)) return res;
       // set up the alignment on the div
       nsCOMPtr divElem = do_QueryInterface(curDiv);
       nsAutoString attr; attr.AssignWithConversion("align");
-      res = mEditor->SetAttribute(divElem, attr, *alignType);
+      res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
       if (NS_FAILED(res)) return res;
       // curDiv is now the correct thing to put curNode in
     }
         
     // tuck the node into the end of the active div
     PRUint32 listLen;
-    res = mEditor->GetLengthOfDOMNode(curDiv, listLen);
+    res = mHTMLEditor->GetLengthOfDOMNode(curDiv, listLen);
     if (NS_FAILED(res)) return res;
-    res = mEditor->MoveNode(curNode, curDiv, listLen);
+    res = mHTMLEditor->MoveNode(curNode, curDiv, listLen);
     if (NS_FAILED(res)) return res;
   }
 
@@ -2899,9 +2937,9 @@ nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType
   nsresult res;
   nsCOMPtr  firstChild, lastChild, divNode;
   
-  res = mEditor->GetFirstEditableChild(aNode, address_of(firstChild));
+  res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(firstChild));
   if (NS_FAILED(res)) return res;
-  res = mEditor->GetLastEditableChild(aNode, address_of(lastChild));
+  res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
   if (NS_FAILED(res)) return res;
   if (!firstChild)
   {
@@ -2913,26 +2951,26 @@ nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType
     // act on this div.
     nsCOMPtr divElem = do_QueryInterface(firstChild);
     nsAutoString attr; attr.AssignWithConversion("align");
-    res = mEditor->SetAttribute(divElem, attr, *alignType);
+    res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
     if (NS_FAILED(res)) return res;
   }
   else
   {
     // else we need to put in a div, set the alignment, and toss in all the children
     nsAutoString divType; divType.AssignWithConversion("div");
-    res = mEditor->CreateNode(divType, aNode, 0, getter_AddRefs(divNode));
+    res = mHTMLEditor->CreateNode(divType, aNode, 0, getter_AddRefs(divNode));
     if (NS_FAILED(res)) return res;
     // set up the alignment on the div
     nsCOMPtr divElem = do_QueryInterface(divNode);
     nsAutoString attr; attr.AssignWithConversion("align");
-    res = mEditor->SetAttribute(divElem, attr, *alignType);
+    res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
     if (NS_FAILED(res)) return res;
     // tuck the children into the end of the active div
     while (lastChild && (lastChild != divNode))
     {
-      res = mEditor->MoveNode(lastChild, divNode, 0);
+      res = mHTMLEditor->MoveNode(lastChild, divNode, 0);
       if (NS_FAILED(res)) return res;
-      res = mEditor->GetLastEditableChild(aNode, address_of(lastChild));
+      res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2955,7 +2993,7 @@ nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOf
   nsCOMPtr node;
   nsCOMPtr isupports;
   
-  nsresult res = mEditor->GetFirstEditableChild(aNode, address_of(node));
+  nsresult res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(node));
   while (NS_SUCCEEDED(res) && node)
   {
     if (  ( aList && (nsHTMLEditUtils::IsList(node)     || 
@@ -3010,7 +3048,7 @@ nsHTMLEditRules::IsFirstNode(nsIDOMNode *aNode)
   while (j < offset)
   {
     childList->Item(j, getter_AddRefs(child));
-    if (mEditor->IsEditable(child)) 
+    if (mHTMLEditor->IsEditable(child)) 
       return PR_FALSE;
     j++;
   }
@@ -3052,7 +3090,7 @@ nsHTMLEditRules::IsLastNode(nsIDOMNode *aNode)
   while (j < (PRInt32)numChildren)
   {
     childList->Item(j, getter_AddRefs(child));
-    if (mEditor->IsEditable(child)) 
+    if (mHTMLEditor->IsEditable(child)) 
       return PR_FALSE;
     j++;
   }
@@ -3070,10 +3108,10 @@ nsHTMLEditRules::AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *
   if (nodeAsText && aOffset) return PR_FALSE;  // there are chars in front of us
   
   nsCOMPtr priorNode;
-  nsresult  res = mEditor->GetPriorHTMLNode(aNode, aOffset, address_of(priorNode));
+  nsresult  res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(priorNode));
   if (NS_FAILED(res)) return PR_TRUE;
   if (!priorNode) return PR_TRUE;
-  nsCOMPtr blockParent = mEditor->GetBlockNodeParent(priorNode);
+  nsCOMPtr blockParent = mHTMLEditor->GetBlockNodeParent(priorNode);
   if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
   return PR_TRUE;
 }
@@ -3093,10 +3131,10 @@ nsHTMLEditRules::AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aB
     if ((PRInt32)strLength > aOffset) return PR_FALSE;  // there are chars in after us
   }
   nsCOMPtr nextNode;
-  nsresult  res = mEditor->GetNextHTMLNode(aNode, aOffset, address_of(nextNode));
+  nsresult  res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nextNode));
   if (NS_FAILED(res)) return PR_TRUE;
   if (!nextNode) return PR_TRUE;
-  nsCOMPtr blockParent = mEditor->GetBlockNodeParent(nextNode);
+  nsCOMPtr blockParent = mHTMLEditor->GetBlockNodeParent(nextNode);
   if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
   return PR_TRUE;
 }
@@ -3113,11 +3151,11 @@ nsHTMLEditRules::CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtrCreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
+  nsresult res = mHTMLEditor->CreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
   if (NS_FAILED(res)) return res;
   // give it special moz attr
   nsCOMPtr mozDivElem = do_QueryInterface(*outDiv);
-  res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
+  res = mHTMLEditor->SetAttribute(mozDivElem, "type", "_moz");
   if (NS_FAILED(res)) return res;
   res = AddTrailerBR(*outDiv);
   return res;
@@ -3153,7 +3191,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       do
       {
-        res = mEditor->IsPrevCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
+        res = mHTMLEditor->IsPrevCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
         if (NS_FAILED(res)) return res;
         if (isSpace || isNBSP) node = temp;
         else break;
@@ -3166,7 +3204,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       do
       {
-        res = mEditor->IsNextCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
+        res = mHTMLEditor->IsNextCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
         if (NS_FAILED(res)) return res;
         if (isSpace || isNBSP) node = temp;
         else break;
@@ -3203,7 +3241,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       nsCOMPtr block = nsEditor::GetBlockNodeParent(node);
       nsCOMPtr prevNode, prevNodeBlock;
-      res = mEditor->GetPriorHTMLNode(node, address_of(prevNode));
+      res = mHTMLEditor->GetPriorHTMLNode(node, address_of(prevNode));
       
       while (prevNode && NS_SUCCEEDED(res))
       {
@@ -3215,7 +3253,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
         if (nsEditor::IsBlockNode(prevNode))
           break;
         node = prevNode;
-        res = mEditor->GetPriorHTMLNode(node, address_of(prevNode));
+        res = mHTMLEditor->GetPriorHTMLNode(node, address_of(prevNode));
       }
     }
     
@@ -3269,7 +3307,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       nsCOMPtr block = nsEditor::GetBlockNodeParent(node);
       nsCOMPtr nextNode, nextNodeBlock;
-      res = mEditor->GetNextHTMLNode(node, address_of(nextNode));
+      res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode));
       
       while (nextNode && NS_SUCCEEDED(res))
       {
@@ -3284,7 +3322,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
         if (nsEditor::IsBlockNode(nextNode))
           break;
         node = nextNode;
-        res = mEditor->GetNextHTMLNode(node, address_of(nextNode));
+        res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode));
       }
     }
     
@@ -3436,7 +3474,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
       nsRangeStore *item = new nsRangeStore();
       if (!item) return NS_ERROR_NULL_POINTER;
       item->StoreRange(opRange);
-      mEditor->mRangeUpdater.RegisterRangeItem(item);
+      mHTMLEditor->mRangeUpdater.RegisterRangeItem(item);
       rangeItemArray.AppendElement((void*)item);
       inArrayOfRanges->RemoveElementAt(0);
     }    
@@ -3453,7 +3491,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
       nsRangeStore *item = (nsRangeStore*)rangeItemArray.ElementAt(0);
       if (!item) return NS_ERROR_NULL_POINTER;
       rangeItemArray.RemoveElementAt(0);
-      mEditor->mRangeUpdater.DropRangeItem(item);
+      mHTMLEditor->mRangeUpdater.DropRangeItem(item);
       res = item->GetRange(address_of(opRange));
       if (NS_FAILED(res)) return res;
       delete item;
@@ -3530,8 +3568,8 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
     {
       isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i));
       nsCOMPtr node( do_QueryInterface(isupports) );
-      if (!aDontTouchContent && mEditor->IsInlineNode(node) 
-           && mEditor->IsContainer(node) && !mEditor->IsTextNode(node))
+      if (!aDontTouchContent && mHTMLEditor->IsInlineNode(node) 
+           && mHTMLEditor->IsContainer(node) && !mHTMLEditor->IsTextNode(node))
       {
         nsCOMPtr arrayOfInlines;
         res = BustUpInlinesAtBRs(node, address_of(arrayOfInlines));
@@ -3605,7 +3643,7 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes,
   nsresult res = NS_OK;
   
   nsCOMPtrselection;
-  res = mEditor->GetSelection(getter_AddRefs(selection));
+  res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
   nsCOMPtr selPriv(do_QueryInterface(selection));
   if (!selPriv)
@@ -3675,7 +3713,7 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes,
     nsCOMPtr testNode( do_QueryInterface(isupports ) );
 
     // Remove all non-editable nodes.  Leave them be.
-    if (!mEditor->IsEditable(testNode))
+    if (!mHTMLEditor->IsEditable(testNode))
     {
       (*outArrayOfNodes)->RemoveElementAt(i);
     }
@@ -3706,8 +3744,8 @@ nsHTMLEditRules::GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBo
   res = aNode->GetFirstChild(getter_AddRefs(child));
   while (child && NS_SUCCEEDED(res))
   {
-    if (mEditor->NodeIsType(child,nsIEditProperty::dt)) aDT = PR_TRUE;
-    else if (mEditor->NodeIsType(child,nsIEditProperty::dd)) aDD = PR_TRUE;
+    if (mHTMLEditor->NodeIsType(child,nsIEditProperty::dt)) aDT = PR_TRUE;
+    else if (mHTMLEditor->NodeIsType(child,nsIEditProperty::dd)) aDD = PR_TRUE;
     res = child->GetNextSibling(getter_AddRefs(temp));
     child = temp;
   }
@@ -3724,7 +3762,7 @@ nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfN
   if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER;
   
   nsCOMPtrselection;
-  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
 
   nsCOMPtr arrayOfRanges;
@@ -3745,7 +3783,7 @@ nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfN
     nsCOMPtr testNode( do_QueryInterface(isupports ) );
 
     // Remove all non-editable nodes.  Leave them be.
-    if (!mEditor->IsEditable(testNode))
+    if (!mHTMLEditor->IsEditable(testNode))
     {
       (*outArrayOfNodes)->RemoveElementAt(i);
     }
@@ -3782,7 +3820,7 @@ nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
     nsCOMPtr resultEndNode;
     PRInt32 resultEndOffset;
     item.endNode->GetParentNode(getter_AddRefs(resultEndNode));
-    res = mEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
+    res = mHTMLEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
                           &resultEndOffset, PR_TRUE);
     if (NS_FAILED(res)) return res;
     // reset range
@@ -3796,7 +3834,7 @@ nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
     nsCOMPtr resultStartNode;
     PRInt32 resultStartOffset;
     item.startNode->GetParentNode(getter_AddRefs(resultStartNode));
-    res = mEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
+    res = mHTMLEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
                           &resultStartOffset, PR_TRUE);
     if (NS_FAILED(res)) return res;
     // reset range
@@ -3860,7 +3898,7 @@ nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode,
       if (!splitDeepNode) return NS_ERROR_NULL_POINTER;
       res = nsEditor::GetNodeLocation(breakNode, address_of(splitParentNode), &splitOffset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
+      res = mHTMLEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
                           &resultOffset, PR_FALSE, address_of(leftNode), address_of(rightNode));
       if (NS_FAILED(res)) return res;
       // put left node in node list
@@ -3874,7 +3912,7 @@ nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode,
         if (NS_FAILED(res)) return res;
       }
       // move break outside of container and also put in node list
-      res = mEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
+      res = mHTMLEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
       if (NS_FAILED(res)) return res;
       isupports = do_QueryInterface(breakNode);
       (*outArrayOfNodes)->AppendElement(isupports);
@@ -3901,7 +3939,7 @@ nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode)
   if (nsEditor::IsBlockNode(aNode)) return nsnull;
   nsCOMPtr inlineNode, node=aNode;
 
-  while (node && mEditor->IsInlineNode(node))
+  while (node && mHTMLEditor->IsInlineNode(node))
   {
     inlineNode = node;
     inlineNode->GetParentNode(getter_AddRefs(node));
@@ -3964,12 +4002,12 @@ nsHTMLEditRules::InsertTab(nsISelection *aSelection,
   PRInt32 offset;
   PRBool isPRE;
   
-  nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parentNode), &offset);
+  nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parentNode), &offset);
   if (NS_FAILED(res)) return res;
   
   if (!parentNode) return NS_ERROR_FAILURE;
     
-  res = mEditor->IsPreformatted(parentNode, &isPRE);
+  res = mHTMLEditor->IsPreformatted(parentNode, &isPRE);
   if (NS_FAILED(res)) return res;
     
   if (isPRE)
@@ -4007,16 +4045,16 @@ nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
 
   // split the header
   PRInt32 newOffset;
-  res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
+  res = mHTMLEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
   if (NS_FAILED(res)) return res;
 
   // if the leftand heading is empty, put a mozbr in it
   nsCOMPtr prevItem;
-  mEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
+  mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
   if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
   {
     PRBool bIsEmptyNode;
-    res = mEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
+    res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
     if (NS_FAILED(res)) return res;
     if (bIsEmptyNode)
     {
@@ -4032,12 +4070,12 @@ nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
   if (NS_FAILED(res)) return res;
   if (isEmpty)
   {
-    res = mEditor->DeleteNode(aHeader);
+    res = mHTMLEditor->DeleteNode(aHeader);
     if (NS_FAILED(res)) return res;
     // layout tells the caret to blink in a weird place
     // if we dont place a break after the header.
     nsCOMPtr sibling;
-    res = mEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
+    res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
     if (NS_FAILED(res)) return res;
     if (!sibling || !nsHTMLEditUtils::IsBreak(sibling))
     {
@@ -4078,7 +4116,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
   nsresult res = NS_OK;
 
   // easy case, in a text node:
-  if (mEditor->IsTextNode(aNode))
+  if (mHTMLEditor->IsTextNode(aNode))
   {
     nsCOMPtr textNode = do_QueryInterface(aNode);
     PRUint32 strLength;
@@ -4089,7 +4127,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     if (!aOffset)
     {
       // is there a BR prior to it?
-      mEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
+      mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
       if (!sibling) 
       {
         // no previous sib, so
@@ -4102,15 +4140,15 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
         PRInt32 newOffset;
         *aCancel = PR_TRUE;
         // split the paragraph
-        res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
+        res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
         if (NS_FAILED(res)) return res;
         // get rid of the break
-        res = mEditor->DeleteNode(sibling);  
+        res = mHTMLEditor->DeleteNode(sibling);  
         if (NS_FAILED(res)) return res;
         // check both halves of para to see if we need mozBR
         res = InsertMozBRIfNeeded(aPara);
         if (NS_FAILED(res)) return res;
-        res = mEditor->GetPriorHTMLSibling(aPara, address_of(sibling));
+        res = mHTMLEditor->GetPriorHTMLSibling(aPara, address_of(sibling));
         if (NS_FAILED(res)) return res;
         if (sibling && nsHTMLEditUtils::IsParagraph(sibling))
         {
@@ -4127,7 +4165,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     if (aOffset == (PRInt32)strLength)
     {
       // is there a BR after to it?
-      res = mEditor->GetNextHTMLSibling(aNode, address_of(sibling));
+      res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
       if (!sibling) 
       {
         // no next sib, so
@@ -4140,10 +4178,10 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
         PRInt32 newOffset;
         *aCancel = PR_TRUE;
         // split the paragraph
-        res = mEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
+        res = mHTMLEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
         if (NS_FAILED(res)) return res;
         // get rid of the break
-        res = mEditor->DeleteNode(sibling);  
+        res = mHTMLEditor->DeleteNode(sibling);  
         if (NS_FAILED(res)) return res;
         // position selection inside right hand para
         res = aSelection->Collapse(aPara,0);
@@ -4160,13 +4198,13 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     // not in a text node.  
     // is there a BR prior to it?
     nsCOMPtr nearNode;
-    res = mEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
+    res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
     if (NS_FAILED(res)) return res;
     if (!nearNode || !nsHTMLEditUtils::IsBreak(nearNode)
         || nsHTMLEditUtils::HasMozAttr(nearNode)) 
     {
       // is there a BR after to it?
-      res = mEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
+      res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
       if (NS_FAILED(res)) return res;
       if (!nearNode || !nsHTMLEditUtils::IsBreak(nearNode)
           || nsHTMLEditUtils::HasMozAttr(nearNode)) 
@@ -4179,10 +4217,10 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     PRInt32 newOffset;
     *aCancel = PR_TRUE;
     // split the paragraph
-    res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
+    res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
     if (NS_FAILED(res)) return res;
     // get rid of the break
-    res = mEditor->DeleteNode(nearNode);  
+    res = mHTMLEditor->DeleteNode(nearNode);  
     if (NS_FAILED(res)) return res;
     // selection to beginning of right hand para
     aSelection->Collapse(aPara,0);
@@ -4228,27 +4266,27 @@ nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
     
     // are we the last list item in the list?
     PRBool bIsLast;
-    res = mEditor->IsLastEditableChild(aListItem, &bIsLast);
+    res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
     if (NS_FAILED(res)) return res;
     if (!bIsLast)
     {
       // we need to split the list!
       nsCOMPtr tempNode;
-      res = mEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
+      res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
       if (NS_FAILED(res)) return res;
     }
     // are we in a sublist?
     if (nsHTMLEditUtils::IsList(listparent))  //in a sublist
     {
       // if so, move this list item out of this list and into the grandparent list
-      res = mEditor->MoveNode(aListItem,listparent,offset+1);
+      res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
       if (NS_FAILED(res)) return res;
       res = aSelection->Collapse(aListItem,0);
     }
     else
     {
       // otherwise kill this listitem
-      res = mEditor->DeleteNode(aListItem);
+      res = mHTMLEditor->DeleteNode(aListItem);
       if (NS_FAILED(res)) return res;
       
       // time to insert a break
@@ -4265,17 +4303,17 @@ nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
   
   // else we want a new list item at the same list level
   PRInt32 newOffset;
-  res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
+  res = mHTMLEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
   if (NS_FAILED(res)) return res;
   // hack: until I can change the damaged doc range code back to being
   // extra inclusive, I have to manually detect certain list items that
   // may be left empty.
   nsCOMPtr prevItem;
-  mEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
+  mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
   if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
   {
     PRBool bIsEmptyNode;
-    res = mEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
+    res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
     if (NS_FAILED(res)) return res;
     if (bIsEmptyNode)
     {
@@ -4355,14 +4393,14 @@ nsHTMLEditRules::MakeBlockquote(nsISupportsArray *arrayOfNodes)
       nsAutoString quoteType; quoteType.AssignWithConversion("blockquote");
       res = SplitAsNeeded("eType, address_of(curParent), &offset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
+      res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
       if (NS_FAILED(res)) return res;
     }
       
     PRUint32 blockLen;
-    res = mEditor->GetLengthOfDOMNode(curBlock, blockLen);
+    res = mHTMLEditor->GetLengthOfDOMNode(curBlock, blockLen);
     if (NS_FAILED(res)) return res;
-    res = mEditor->MoveNode(curNode, curBlock, blockLen);
+    res = mHTMLEditor->MoveNode(curNode, curBlock, blockLen);
     if (NS_FAILED(res)) return res;
   }
   return res;
@@ -4434,11 +4472,11 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
         // make sure we have a normal br at end of block
         res = AddTerminatingBR(curNode);
         if (NS_FAILED(res)) return res;
-        res = mEditor->RemoveContainer(curNode); 
+        res = mHTMLEditor->RemoveContainer(curNode); 
       }
       else
       {
-        res = mEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
+        res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
       }
       if (NS_FAILED(res)) return res;
     }
@@ -4467,7 +4505,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       curBlock = 0;  // forget any previous block used for previous inline nodes
       if (!bNoParent)
       {
-        res = mEditor->DeleteNode(curNode);
+        res = mHTMLEditor->DeleteNode(curNode);
         if (NS_FAILED(res)) return res;
       }
     }
@@ -4483,7 +4521,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
     else if (nsEditor::IsInlineNode(curNode) && !bNoParent)
     {
       // if curNode is a non editable, drop it if we are going to 
-      if ((aBlockTag->EqualsWithConversion("pre")) && (!mEditor->IsEditable(curNode)))
+      if ((aBlockTag->EqualsWithConversion("pre")) && (!mHTMLEditor->IsEditable(curNode)))
         continue; // do nothing to this block
       
       // if no curBlock, make one
@@ -4491,7 +4529,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       {
         res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
         if (NS_FAILED(res)) return res;
-        res = mEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
+        res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
         if (NS_FAILED(res)) return res;
       }
       
@@ -4501,9 +4539,9 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       // this is a continuation of some inline nodes that belong together in
       // the same block item.  use curBlock
       PRUint32 blockLen;
-      res = mEditor->GetLengthOfDOMNode(curBlock, blockLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curBlock, blockLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(curNode, curBlock, blockLen);
+      res = mHTMLEditor->MoveNode(curNode, curBlock, blockLen);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -4531,7 +4569,7 @@ nsHTMLEditRules::SplitAsNeeded(const nsString *aTag,
     // sniffing up the parent tree until we find 
     // a legal place for the block
     if (!parent) break;
-    if (mEditor->CanContainTag(parent, *aTag))
+    if (mHTMLEditor->CanContainTag(parent, *aTag))
     {
       tagParent = parent;
       break;
@@ -4548,7 +4586,7 @@ nsHTMLEditRules::SplitAsNeeded(const nsString *aTag,
   if (splitNode)
   {
     // we found a place for block, but above inOutParent.  We need to split nodes.
-    res = mEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
+    res = mHTMLEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
     if (NS_FAILED(res)) return res;
     *inOutParent = tagParent;
   }
@@ -4564,14 +4602,14 @@ nsHTMLEditRules::AddTerminatingBR(nsIDOMNode *aBlock)
 {
   if (!aBlock) return NS_ERROR_NULL_POINTER;
   nsCOMPtr last;
-  nsresult res = mEditor->GetLastEditableLeaf(aBlock, address_of(last));
+  nsresult res = mHTMLEditor->GetLastEditableLeaf(aBlock, address_of(last));
   if (last && nsHTMLEditUtils::IsBreak(last))
   {
     if (nsHTMLEditUtils::IsMozBR(last))
     {
       // need to convert a br
       nsCOMPtr elem = do_QueryInterface(last);
-      res = mEditor->RemoveAttribute(elem, NS_ConvertASCIItoUCS2("type")); 
+      res = mHTMLEditor->RemoveAttribute(elem, NS_ConvertASCIItoUCS2("type")); 
       if (NS_FAILED(res)) return res;
     }
     else // we have what we want, we're done
@@ -4583,9 +4621,9 @@ nsHTMLEditRules::AddTerminatingBR(nsIDOMNode *aBlock)
   {
     PRUint32 len;
     nsCOMPtr brNode;
-    res = mEditor->GetLengthOfDOMNode(aBlock, len);
+    res = mHTMLEditor->GetLengthOfDOMNode(aBlock, len);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateBR(aBlock, len, address_of(brNode));
+    res = mHTMLEditor->CreateBR(aBlock, len, address_of(brNode));
     if (NS_FAILED(res)) return res;
   }
   return res;
@@ -4622,24 +4660,24 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
   // to after the 'left' one
   if (parent != rightParent)
   {
-    res = mEditor->MoveNode(aNodeRight, parent, parOffset);
+    res = mHTMLEditor->MoveNode(aNodeRight, parent, parOffset);
     if (NS_FAILED(res)) return res;
   }
   
   // defaults for outParams
   *aOutMergeParent = aNodeRight;
-  res = mEditor->GetLengthOfDOMNode(aNodeLeft, *((PRUint32*)aOutMergeOffset));
+  res = mHTMLEditor->GetLengthOfDOMNode(aNodeLeft, *((PRUint32*)aOutMergeOffset));
   if (NS_FAILED(res)) return res;
 
   // seperate join rules for differing blocks
   if (nsHTMLEditUtils::IsParagraph(aNodeLeft))
   {
     // for para's, merge deep & add a 
after merging - res = mEditor->JoinNodeDeep(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset); + res = mHTMLEditor->JoinNodeDeep(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset); if (NS_FAILED(res)) return res; // now we need to insert a br. nsCOMPtr brNode; - res = mEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, address_of(brNode)); + res = mHTMLEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, address_of(brNode)); if (NS_FAILED(res)) return res; res = nsEditor::GetNodeLocation(brNode, aOutMergeParent, aOutMergeOffset); if (NS_FAILED(res)) return res; @@ -4647,10 +4685,10 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, return res; } else if (nsHTMLEditUtils::IsList(aNodeLeft) - || mEditor->IsTextNode(aNodeLeft)) + || mHTMLEditor->IsTextNode(aNodeLeft)) { // for list's, merge shallow (wouldn't want to combine list items) - res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent); + res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent); if (NS_FAILED(res)) return res; return res; } @@ -4658,16 +4696,16 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, { // remember the last left child, and firt right child nsCOMPtr lastLeft, firstRight; - res = mEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft)); + res = mHTMLEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft)); if (NS_FAILED(res)) return res; - res = mEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight)); + res = mHTMLEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight)); if (NS_FAILED(res)) return res; // for list items, divs, etc, merge smart - res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent); + res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent); if (NS_FAILED(res)) return res; - if (lastLeft && firstRight && mEditor->NodesSameType(lastLeft, firstRight)) + if (lastLeft && firstRight && mHTMLEditor->NodesSameType(lastLeft, firstRight)) { return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset); } @@ -4677,7 +4715,9 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, nsresult -nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode) +nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, + nsCOMPtr *aOutCiteNode, + PRBool aPlainText) { // check parms if (!aNode || !aOutCiteNode) @@ -4689,7 +4729,9 @@ nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr while (node) { - if (nsHTMLEditUtils::IsMailCite(node)) *aOutCiteNode = node; + if ( (aPlainText && nsHTMLEditUtils::IsPre(node)) || + (!aPlainText && nsHTMLEditUtils::IsMailCite(node)) ) + *aOutCiteNode = node; if (nsHTMLEditUtils::IsBody(node)) break; res = node->GetParentNode(getter_AddRefs(parentNode)); @@ -4709,7 +4751,7 @@ nsHTMLEditRules::AdjustSpecialBreaks(PRBool aSafeToAskFrames) PRUint32 nodeCount,j; // gather list of empty nodes - nsEmptyFunctor functor(mEditor); + nsEmptyFunctor functor(mHTMLEditor); nsDOMIterator iter; nsresult res = iter.Init(mDocChangeRange); if (NS_FAILED(res)) return res; @@ -4740,7 +4782,7 @@ nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection) PRUint32 nodeCount,j; nsresult res; - nsAutoSelectionReset selectionResetter(aSelection, mEditor); + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); // special case for mDocChangeRange entirely in one text node. // This is an efficiency hack for normal typing in the editor. @@ -4766,7 +4808,7 @@ nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection) } // gather up a list of text nodes - nsEditableTextFunctor functor(mEditor); + nsEditableTextFunctor functor(mHTMLEditor); nsDOMIterator iter; res = iter.Init(mDocChangeRange); if (NS_FAILED(res)) return res; @@ -4807,12 +4849,12 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection // get the (collapsed) selection location nsCOMPtr selNode, temp; PRInt32 selOffset; - res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; temp = selNode; // are we in an editable node? - while (!mEditor->IsEditable(selNode)) + while (!mHTMLEditor->IsEditable(selNode)) { // scan up the tree until we find an editable place to be res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset); @@ -4833,22 +4875,22 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection // 4) that br is the last editable node in it's block nsCOMPtr nearNode; - res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; if (!nearNode) return res; // is nearNode also a descendant of same block? nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); - nearBlock = mEditor->GetBlockNodeParent(nearNode); + if (mHTMLEditor->IsBlockNode(selNode)) block = selNode; + else block = mHTMLEditor->GetBlockNodeParent(selNode); + nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode); if (block == nearBlock) { if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) && !nsHTMLEditUtils::IsMozBR(nearNode)) { PRBool bIsLast; - res = mEditor->IsLastEditableChild(nearNode, &bIsLast); + res = mHTMLEditor->IsLastEditableChild(nearNode, &bIsLast); if (NS_FAILED(res)) return res; if (bIsLast) { @@ -4872,11 +4914,11 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection // text
  1. list item
) // in this case we also need moz-br. nsCOMPtr nextNode; - res = mEditor->GetNextHTMLNode(nearNode, address_of(nextNode)); + res = mHTMLEditor->GetNextHTMLNode(nearNode, address_of(nextNode)); if (NS_FAILED(res)) return res; - res = mEditor->GetNextHTMLSibling(nearNode, address_of(nextNode)); + res = mHTMLEditor->GetNextHTMLSibling(nearNode, address_of(nextNode)); if (NS_FAILED(res)) return res; - if (nextNode && mEditor->IsBlockNode(nextNode)) + if (nextNode && mHTMLEditor->IsBlockNode(nextNode)) { // need to insert special moz BR. Why? Because if we don't // the user will see no new line for the break. @@ -4895,12 +4937,12 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection } // we aren't in a textnode: are we adjacent to a break or an image? - res = mEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; if (nearNode && (nsHTMLEditUtils::IsBreak(nearNode) || nsHTMLEditUtils::IsImage(nearNode))) return NS_OK; // this is a good place for the caret to be - res = mEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nearNode)); + res = mHTMLEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; if (nearNode && (nsHTMLEditUtils::IsBreak(nearNode) || nsHTMLEditUtils::IsImage(nearNode))) @@ -4946,22 +4988,22 @@ nsHTMLEditRules::FindNearSelectableNode(nsIDOMNode *aSelNode, nsCOMPtr nearNode, curNode; if (aDirection == nsIEditor::ePrevious) - res = mEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); else - res = mEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); + res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; // scan in the right direction until we find an eligible text node, // but dont cross any breaks, images, or table elements. - while (nearNode && !(mEditor->IsTextNode(nearNode) + while (nearNode && !(mHTMLEditor->IsTextNode(nearNode) || nsHTMLEditUtils::IsBreak(nearNode) || nsHTMLEditUtils::IsImage(nearNode))) { curNode = nearNode; if (aDirection == nsIEditor::ePrevious) - res = mEditor->GetPriorHTMLNode(curNode, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode)); else - res = mEditor->GetNextHTMLNode(curNode, address_of(nearNode)); + res = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode)); if (NS_FAILED(res)) return res; } @@ -5042,7 +5084,7 @@ nsHTMLEditRules::RemoveEmptyNodes() if (!node) return NS_ERROR_FAILURE; PRBool bIsEmptyNode; - res = mEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE); + res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE); if (NS_FAILED(res)) return res; if (bIsEmptyNode && !nsHTMLEditUtils::IsBody(node)) { @@ -5085,7 +5127,7 @@ nsHTMLEditRules::RemoveEmptyNodes() isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); nsCOMPtr delNode( do_QueryInterface(isupports ) ); arrayOfNodes->RemoveElementAt(0); - res = mEditor->DeleteNode(delNode); + res = mHTMLEditor->DeleteNode(delNode); if (NS_FAILED(res)) return res; } @@ -5102,7 +5144,7 @@ nsHTMLEditRules::SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult) *aResult = PR_FALSE; nsCOMPtrselection; - nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; nsCOMPtrselPriv(do_QueryInterface(selection)); @@ -5170,7 +5212,7 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS PRBool isPRE; nsCOMPtr node = do_QueryInterface(aTextNode); - res = mEditor->IsPreformatted(node,&isPRE); + res = mHTMLEditor->IsPreformatted(node,&isPRE); if (NS_FAILED(res)) return res; if (isPRE) @@ -5222,19 +5264,19 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS EditTxn *txn; // note 1: we are not telling edit listeners about these because they don't care // note 2: we are not wrapping these in a placeholder because we know they already are - res = mEditor->CreateTxnForDeleteText(aTextNode, aStart+runStart, runEnd-runStart, (DeleteTextTxn**)&txn); + res = mHTMLEditor->CreateTxnForDeleteText(aTextNode, aStart+runStart, runEnd-runStart, (DeleteTextTxn**)&txn); if (NS_FAILED(res)) return res; if (!txn) return NS_ERROR_OUT_OF_MEMORY; - res = mEditor->Do(txn); + res = mHTMLEditor->Do(txn); if (NS_FAILED(res)) return res; // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); // insert the new run - res = mEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); + res = mHTMLEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); if (NS_FAILED(res)) return res; if (!txn) return NS_ERROR_OUT_OF_MEMORY; - res = mEditor->Do(txn); + res = mHTMLEditor->Do(txn); // The transaction system (if any) has taken ownwership of txns. NS_IF_RELEASE(txn); } @@ -5322,24 +5364,24 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) if (NS_FAILED(res)) return res; PRBool bIsFirstListItem; - res = mEditor->IsFirstEditableChild(curNode, &bIsFirstListItem); + res = mHTMLEditor->IsFirstEditableChild(curNode, &bIsFirstListItem); if (NS_FAILED(res)) return res; PRBool bIsLastListItem; - res = mEditor->IsLastEditableChild(curNode, &bIsLastListItem); + res = mHTMLEditor->IsLastEditableChild(curNode, &bIsLastListItem); if (NS_FAILED(res)) return res; if (!bIsFirstListItem && !bIsLastListItem) { // split the list nsCOMPtr newBlock; - res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock)); + res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock)); if (NS_FAILED(res)) return res; } if (!bIsFirstListItem) parOffset++; - res = mEditor->MoveNode(curNode, curParPar, parOffset); + res = mHTMLEditor->MoveNode(curNode, curParPar, parOffset); if (NS_FAILED(res)) return res; // unwrap list item contents if they are no longer in a list @@ -5348,7 +5390,7 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) { res = AddTerminatingBR(curNode); if (NS_FAILED(res)) return res; - res = mEditor->RemoveContainer(curNode); + res = mHTMLEditor->RemoveContainer(curNode); if (NS_FAILED(res)) return res; *aOutOfList = PR_TRUE; } @@ -5364,20 +5406,20 @@ nsHTMLEditRules::ConfirmSelectionInBody() nsCOMPtr bodyNode; // get the body - res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); + res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement)); if (NS_FAILED(res)) return res; if (!bodyElement) return NS_ERROR_UNEXPECTED; bodyNode = do_QueryInterface(bodyElement); // get the selection nsCOMPtrselection; - res = mEditor->GetSelection(getter_AddRefs(selection)); + res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; // get the selection start location nsCOMPtr selNode, temp, parent; PRInt32 selOffset; - res = mEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; temp = selNode; @@ -5397,7 +5439,7 @@ nsHTMLEditRules::ConfirmSelectionInBody() } // get the selection end location - res = mEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; temp = selNode; @@ -5472,11 +5514,11 @@ nsresult nsHTMLEditRules::InsertMozBRIfNeeded(nsIDOMNode *aNode) { if (!aNode) return NS_ERROR_NULL_POINTER; - if (!mEditor->IsBlockNode(aNode)) return NS_OK; + if (!mHTMLEditor->IsBlockNode(aNode)) return NS_OK; PRBool isEmpty; nsCOMPtr brNode; - nsresult res = mEditor->IsEmptyNode(aNode, &isEmpty); + nsresult res = mHTMLEditor->IsEmptyNode(aNode, &isEmpty); if (NS_FAILED(res)) return res; if (isEmpty) { @@ -5658,11 +5700,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection) nsCOMPtr selNode; PRInt32 selOffset; - nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; res = mUtilRange->SetStart(selNode, selOffset); if (NS_FAILED(res)) return res; - res = mEditor->GetEndNodeAndOffset(aSelection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; res = mUtilRange->SetEnd(selNode, selOffset); if (NS_FAILED(res)) return res; diff --git a/mozilla/editor/base/nsHTMLEditRules.h b/mozilla/editor/base/nsHTMLEditRules.h index d827487b3c5..88fe713cd8d 100644 --- a/mozilla/editor/base/nsHTMLEditRules.h +++ b/mozilla/editor/base/nsHTMLEditRules.h @@ -34,6 +34,7 @@ class nsISupportsArray; class nsVoidArray; class nsIDOMElement; class nsIEditor; +class nsHTMLEditor; class nsHTMLEditRules : public nsIHTMLEditRules, public nsTextEditRules, public nsIEditActionListener { @@ -46,7 +47,7 @@ public: // nsIEditRules methods - NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); + NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags); NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); @@ -98,10 +99,10 @@ protected: nsString *outString, PRInt32 aMaxLength); nsresult WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled); + nsresult DidInsertBreak(nsISelection *aSelection, nsresult aResult); nsresult WillDeleteSelection(nsISelection *aSelection, nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled); nsresult DeleteNonTableElements(nsIDOMNode *aNode); - nsresult WillMakeList(nsISelection *aSelection, const nsString *aListType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull); nsresult WillRemoveList(nsISelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); nsresult WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled); @@ -110,17 +111,13 @@ protected: nsresult WillMakeDefListItem(nsISelection *aSelection, const nsString *aBlockType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled); nsresult WillMakeBasicBlock(nsISelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled); nsresult DidMakeBasicBlock(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); - nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsString *alignType); nsresult AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType); nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE); - nsresult InsertTab(nsISelection *aSelection, nsString *outString); - nsresult ReturnInHeader(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset); nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled); nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset); - nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection); nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr *outList, const nsString& aListType, const nsString& aItemType); nsresult CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc); @@ -132,7 +129,6 @@ protected: PRBool IsLastNode(nsIDOMNode *aNode); PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock); PRBool AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock); - nsresult GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset, PRInt32 actionID, nsCOMPtr *outNode, PRInt32 *outOffset); nsresult GetPromotedRanges(nsISelection *inSelection, @@ -154,20 +150,16 @@ protected: nsCOMPtr GetHighestInlineParent(nsIDOMNode* aNode); nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, nsVoidArray *inTransitionArray); - nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag); nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes); nsresult SplitAsNeeded(const nsString *aTag, nsCOMPtr *inOutParent, PRInt32 *inOutOffset); nsresult AddTerminatingBR(nsIDOMNode *aBlock); - nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft, nsIDOMNode *aNodeRight, nsCOMPtr *aOutMergeParent, PRInt32 *aOutMergeOffset); - - nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode); + nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode, PRBool aPlaintext); nsresult PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList); - nsresult AdjustSpecialBreaks(PRBool aSafeToAskFrames = PR_FALSE); nsresult AdjustWhitespace(nsISelection *aSelection); nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction); @@ -180,14 +172,13 @@ protected: nsresult SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult); nsresult DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aStart, PRInt32 aEnd); nsresult UpdateDocChangeRange(nsIDOMRange *aRange); - nsresult ConvertWhitespace(const nsString & inString, nsString & outString); nsresult ConfirmSelectionInBody(); - nsresult InsertMozBRIfNeeded(nsIDOMNode *aNode); // data members protected: + nsHTMLEditor *mHTMLEditor; nsCOMPtr mDocChangeRange; PRBool mListenerEnabled; PRBool mReturnInEmptyLIKillsList; diff --git a/mozilla/editor/base/nsHTMLEditUtils.cpp b/mozilla/editor/base/nsHTMLEditUtils.cpp index e7057287acd..787aa3044b2 100644 --- a/mozilla/editor/base/nsHTMLEditUtils.cpp +++ b/mozilla/editor/base/nsHTMLEditUtils.cpp @@ -26,6 +26,7 @@ #include "nsEditor.h" #include "nsIDOMNode.h" #include "nsIDOMNodeList.h" +#include "nsIDOMHTMLAnchorElement.h" /******************************************************** * helper methods from nsTextEditRules @@ -487,6 +488,34 @@ nsHTMLEditUtils::IsImage(nsIDOMNode *node) return PR_FALSE; } +PRBool +nsHTMLEditUtils::IsLink(nsIDOMNode *aNode) +{ + if (!aNode) return PR_FALSE; + nsCOMPtr anchor = do_QueryInterface(aNode); + if (anchor) + { + nsAutoString tmpText; + if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) + return PR_TRUE; + } + return PR_FALSE; +} + +PRBool +nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode) +{ + if (!aNode) return PR_FALSE; + nsCOMPtr anchor = do_QueryInterface(aNode); + if (anchor) + { + nsAutoString tmpText; + if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) + return PR_TRUE; + } + return PR_FALSE; +} + /////////////////////////////////////////////////////////////////////////// // IsDiv: true if node an html div node diff --git a/mozilla/editor/base/nsHTMLEditUtils.h b/mozilla/editor/base/nsHTMLEditUtils.h index 4904f271157..da1228289b6 100644 --- a/mozilla/editor/base/nsHTMLEditUtils.h +++ b/mozilla/editor/base/nsHTMLEditUtils.h @@ -58,6 +58,8 @@ public: static PRBool IsAddress(nsIDOMNode *aNode); static PRBool IsAnchor(nsIDOMNode *aNode); static PRBool IsImage(nsIDOMNode *aNode); + static PRBool IsLink(nsIDOMNode *aNode); + static PRBool IsNamedAnchor(nsIDOMNode *aNode); static PRBool IsDiv(nsIDOMNode *aNode); static PRBool IsNormalDiv(nsIDOMNode *aNode); static PRBool IsMozDiv(nsIDOMNode *aNode); diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index 505c5656869..b841561a8ee 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -119,16 +119,6 @@ static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); -// Drag & Drop, Clipboard Support -static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); -static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); -static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); -static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); -// private clipboard data flavors for html copy/paste -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" - - #if defined(NS_DEBUG) && defined(DEBUG_buster) static PRBool gNoisy = PR_FALSE; #else @@ -139,105 +129,18 @@ static const PRBool gNoisy = PR_FALSE; static char hrefText[] = "href"; static char anchorTxt[] = "anchor"; static char namedanchorText[] = "namedanchor"; -nsIAtom *nsHTMLEditor::gTypingTxnName; -nsIAtom *nsHTMLEditor::gIMETxnName; -nsIAtom *nsHTMLEditor::gDeleteTxnName; // some prototypes for rules creation shortcuts nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); -#define IsLink(s) (s.EqualsIgnoreCase(hrefText)) -#define IsNamedAnchor(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) - -static PRBool IsLinkNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtr anchor = do_QueryInterface(aNode); - if (anchor) - { - nsAutoString tmpText; - if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) - return PR_TRUE; - } - } - return PR_FALSE; -} - -static PRBool IsNamedAnchorNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtr anchor = do_QueryInterface(aNode); - if (anchor) - { - nsAutoString tmpText; - if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) - return PR_TRUE; - } - } - return PR_FALSE; -} - -static PRBool IsListNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtrelement; - element = do_QueryInterface(aNode); - if (element) - { - nsAutoString tagName; - if (NS_SUCCEEDED(element->GetTagName(tagName))) - { - tagName.ToLowerCase(); - // With only 3 tests, it doesn't - // seem worth using nsAtoms - if (tagName.EqualsWithConversion("ol") || - tagName.EqualsWithConversion("ul") || - tagName.EqualsWithConversion("dl")) - { - return PR_TRUE; - } - } - } - } - return PR_FALSE; -} - -static PRBool IsCellNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtrelement; - element = do_QueryInterface(aNode); - if (element) - { - nsAutoString tagName; - if (NS_SUCCEEDED(element->GetTagName(tagName))) - { - // With only 2 tests, it doesn't - // seem worth using nsAtoms - if (tagName.EqualsIgnoreCase("td") || - tagName.EqualsIgnoreCase("th")) - { - return PR_TRUE; - } - } - } - } - return PR_FALSE; -} - +#define IsLinkTag(s) (s.EqualsIgnoreCase(hrefText)) +#define IsNamedAnchorTag(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) nsHTMLEditor::nsHTMLEditor() -: nsEditor() +: nsPlaintextEditor() , mIgnoreSpuriousDragEvent(PR_FALSE) , mTypeInState(nsnull) -, mRules(nsnull) -, mIsComposing(PR_FALSE) -, mMaxTextLength(-1) , mSelectedCellIndex(0) { // Done in nsEditor @@ -247,58 +150,10 @@ nsHTMLEditor::nsHTMLEditor() mUnderlineAtom = getter_AddRefs(NS_NewAtom("u")); mFontAtom = getter_AddRefs(NS_NewAtom("font")); mLinkAtom = getter_AddRefs(NS_NewAtom("a")); - - if (!gTypingTxnName) - gTypingTxnName = NS_NewAtom("Typing"); - else - NS_ADDREF(gTypingTxnName); - if (!gIMETxnName) - gIMETxnName = NS_NewAtom("IME"); - else - NS_ADDREF(gIMETxnName); - if (!gDeleteTxnName) - gDeleteTxnName = NS_NewAtom("Deleting"); - else - NS_ADDREF(gDeleteTxnName); } nsHTMLEditor::~nsHTMLEditor() { - /* first, delete the transaction manager if there is one. - this will release any remaining transactions. - this is important because transactions can hold onto the atoms (gTypingTxnName, ...) - and to make the optimization (holding refcounted statics) work correctly, - the editor instance needs to hold the last refcount. - If you get this wrong, expect to deref a garbage gTypingTxnName pointer if you bring up a second editor. - */ - if (mTxnMgr) { - mTxnMgr = 0; - } - nsrefcnt refCount=0; - if (gTypingTxnName) // we addref'd in the constructor - { // want to release it without nulling out the pointer. - refCount = gTypingTxnName->Release(); - if (0==refCount) { - gTypingTxnName = nsnull; - } - } - - if (gIMETxnName) // we addref'd in the constructor - { // want to release it without nulling out the pointer. - refCount = gIMETxnName->Release(); - if (0==refCount) { - gIMETxnName = nsnull; - } - } - - if (gDeleteTxnName) // we addref'd in the constructor - { // want to release it without nulling out the pointer. - refCount = gDeleteTxnName->Release(); - if (0==refCount) { - gDeleteTxnName = nsnull; - } - } - // remove the rules as an action listener. Else we get a bad ownership loop later on. // it's ok if the rules aren't a listener; we ignore the error. nsCOMPtr mListener = do_QueryInterface(mRules); @@ -318,31 +173,6 @@ nsHTMLEditor::~nsHTMLEditor() selPriv->RemoveSelectionListener(listener); } } - nsCOMPtr erP; - result = GetDOMEventReceiver(getter_AddRefs(erP)); - if (NS_SUCCEEDED(result) && erP) - { - if (mKeyListenerP) { - erP->RemoveEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener)); - } - if (mMouseListenerP) { - erP->RemoveEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener)); - } - if (mTextListenerP) { - erP->RemoveEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener)); - } - if (mCompositionListenerP) { - erP->RemoveEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener)); - } - if (mFocusListenerP) { - erP->RemoveEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener)); - } - if (mDragListenerP) { - erP->RemoveEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener)); - } - } - else - NS_NOTREACHED("~nsTextEditor"); NS_IF_RELEASE(mTypeInState); } @@ -400,148 +230,56 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, if (!aDoc || !aPresShell) return NS_ERROR_NULL_POINTER; - nsresult result = NS_ERROR_NULL_POINTER; - // Init the base editor - result = nsEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags); - if (NS_FAILED(result)) { return result; } - - // disable links - nsCOMPtr context; - aPresShell->GetPresContext(getter_AddRefs(context)); - if (!context) return NS_ERROR_NULL_POINTER; - if (!(mFlags & eEditorPlaintextMask)) - context->SetLinkHandler(0); - - nsCOMPtr bodyElement; - result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(result)) { return result; } - if (!bodyElement) { return NS_ERROR_NULL_POINTER; } - - // init the type-in state - mTypeInState = new TypeInState(); - if (!mTypeInState) {return NS_ERROR_NULL_POINTER;} - NS_ADDREF(mTypeInState); - - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) { return result; } - if (selection) + nsresult result = NS_OK, rulesRes = NS_OK; + + if (1) { - nsCOMPtr selPriv(do_QueryInterface(selection)); - nsCOMPtrlistener; - listener = do_QueryInterface(mTypeInState); - if (listener) { - selPriv->AddSelectionListener(listener); + // block to scope nsAutoEditInitRulesTrigger + nsAutoEditInitRulesTrigger rulesTrigger(NS_STATIC_CAST(nsPlaintextEditor*,this), rulesRes); + + // Init the plaintext editor + result = nsPlaintextEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags); + if (NS_FAILED(result)) { return result; } + + // disable links + nsCOMPtr context; + aPresShell->GetPresContext(getter_AddRefs(context)); + if (!context) return NS_ERROR_NULL_POINTER; + if (!(mFlags & eEditorPlaintextMask)) + context->SetLinkHandler(0); + + nsCOMPtr bodyElement; + result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(result)) { return result; } + if (!bodyElement) { return NS_ERROR_NULL_POINTER; } + + // init the type-in state + mTypeInState = new TypeInState(); + if (!mTypeInState) {return NS_ERROR_NULL_POINTER;} + NS_ADDREF(mTypeInState); + + nsCOMPtrselection; + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { return result; } + if (selection) + { + nsCOMPtr selPriv(do_QueryInterface(selection)); + nsCOMPtrlistener; + listener = do_QueryInterface(mTypeInState); + if (listener) { + selPriv->AddSelectionListener(listener); + } } + + // Set up a DTD + mDTD = do_CreateInstance(kCTransitionalDTDCID); + if (!mDTD) result = NS_ERROR_FAILURE; } - // Set up a DTD XXX XXX - // HACK: This should have happened in a document specific way - // in nsEditor::Init(), but we dont' have a way to do that yet - result = nsComponentManager::CreateInstance(kCTransitionalDTDCID, nsnull, - NS_GET_IID(nsIDTD), getter_AddRefs(mDTD)); - if (!mDTD) result = NS_ERROR_FAILURE; - if (NS_FAILED(result)) return result; - - // Init the rules system - result = InitRules(); - if (NS_FAILED(result)) return result; - - EnableUndo(PR_TRUE); - + if (NS_FAILED(rulesRes)) return rulesRes; return result; } -NS_IMETHODIMP -nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) -{ - nsresult result; - - result = nsEditor::SetDocumentCharacterSet(characterSet); - - // update META charset tag - if (NS_SUCCEEDED(result)) { - nsCOMPtrdomdoc; - result = GetDocument(getter_AddRefs(domdoc)); - if (NS_SUCCEEDED(result) && domdoc) { - nsAutoString newMetaString; - nsCOMPtrmetaList; - nsCOMPtrmetaNode; - nsCOMPtrmetaElement; - PRBool newMetaCharset = PR_TRUE; - - // get a list of META tags - result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("meta"), getter_AddRefs(metaList)); - if (NS_SUCCEEDED(result) && metaList) { - PRUint32 listLength = 0; - (void) metaList->GetLength(&listLength); - - for (PRUint32 i = 0; i < listLength; i++) { - metaList->Item(i, getter_AddRefs(metaNode)); - if (!metaNode) continue; - metaElement = do_QueryInterface(metaNode); - if (!metaElement) continue; - - const NS_ConvertASCIItoUCS2 content("charset="); - nsString currentValue; - - if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("http-equiv"), currentValue))) continue; - - if (kNotFound != currentValue.Find("content-type", PR_TRUE)) { - if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("content"), currentValue))) continue; - - PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE); - if (kNotFound != offset) { - currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html) - newMetaString.Append(content); - newMetaString.Append(characterSet); - result = nsEditor::SetAttribute(metaElement, NS_ConvertASCIItoUCS2("content"), newMetaString); - if (NS_SUCCEEDED(result)) - newMetaCharset = PR_FALSE; - break; - } - } - } - } - - if (newMetaCharset) { - nsCOMPtrheadList; - nsCOMPtrheadNode; - nsCOMPtrresultNode; - - result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"),getter_AddRefs(headList)); - if (NS_SUCCEEDED(result) && headList) { - headList->Item(0, getter_AddRefs(headNode)); - if (headNode) { - // Create a new meta charset tag - // can't use |NS_LITERAL_STRING| here until |CreateNode| is fixed to accept readables - result = CreateNode(NS_ConvertASCIItoUCS2("meta"), headNode, 0, getter_AddRefs(resultNode)); - if (NS_FAILED(result)) - return NS_ERROR_FAILURE; - - // Set attributes to the created element - if (resultNode && nsCRT::strlen(characterSet) > 0) { - metaElement = do_QueryInterface(resultNode); - if (metaElement) { - // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_LITERAL_STRING("http-equiv"), NS_LITERAL_STRING("Content-Type")); - if (NS_SUCCEEDED(result)) { - newMetaString.AssignWithConversion("text/html;charset="); - newMetaString.Append(characterSet); - // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_LITERAL_STRING("content"), newMetaString); - } - } - } - } - } - } - } - } - - return result; -} - NS_IMETHODIMP nsHTMLEditor::PostCreate() { @@ -668,17 +406,11 @@ nsHTMLEditor::SetFlags(PRUint32 aFlags) NS_IMETHODIMP nsHTMLEditor::InitRules() { -// instantiate the rules for this text editor -// XXX: we should be told which set of rules to instantiate - nsresult res = NS_ERROR_FAILURE; - if (mFlags & eEditorPlaintextMask) - res = NS_NewTextEditRules(getter_AddRefs(mRules)); - else - res = NS_NewHTMLEditRules(getter_AddRefs(mRules)); - + // instantiate the rules for the html editor + nsresult res = NS_NewHTMLEditRules(getter_AddRefs(mRules)); if (NS_FAILED(res)) return res; if (!mRules) return NS_ERROR_UNEXPECTED; - res = mRules->Init(this, mFlags); + res = mRules->Init(NS_STATIC_CAST(nsPlaintextEditor*,this), mFlags); return res; } @@ -797,18 +529,15 @@ NS_IMETHODIMP nsHTMLEditor::TypedText(const PRUnichar* aString, switch (aAction) { case eTypedText: + case eTypedBreak: { - return InsertText(aString); + return nsPlaintextEditor::TypedText(aString, aAction); } case eTypedBR: { nsCOMPtr brNode; return InsertBR(address_of(brNode)); // only inserts a br node } - case eTypedBreak: - { - return InsertLineBreak(); // uses rules to figure out what to insert - } } return NS_ERROR_FAILURE; } @@ -1020,1179 +749,6 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) return res; } -NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aProperty) { return NS_ERROR_NULL_POINTER; } - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - ForceCompositionEnd(); - - nsresult res; - nsCOMPtrselection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - if (!selection) return NS_ERROR_NULL_POINTER; - nsCOMPtr selPriv(do_QueryInterface(selection)); - - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - if (isCollapsed) - { - // manipulating text attributes on a collapsed selection only sets state for the next text insertion - return mTypeInState->SetProp(aProperty, *aAttribute, *aValue); - } - - nsAutoEditBatch batchIt(this); - nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); - nsAutoSelectionReset selectionResetter(selection, this); - nsAutoTxnsConserveSelection dontSpazMySelection(this); - - PRBool cancel, handled; - nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (!cancel && !handled) - { - // get selection range enumerator - nsCOMPtr enumerator; - res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(res)) return res; - if (!enumerator) return NS_ERROR_FAILURE; - - // loop thru the ranges in the selection - enumerator->First(); - nsCOMPtr currentItem; - while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) - { - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(res)) return res; - if (!currentItem) return NS_ERROR_FAILURE; - - nsCOMPtr range( do_QueryInterface(currentItem) ); - - // adjust range to include any ancestors who's children are entirely selected - res = PromoteInlineRange(range); - if (NS_FAILED(res)) return res; - - // check for easy case: both range endpoints in same text node - nsCOMPtr startNode, endNode; - res = range->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = range->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - if ((startNode == endNode) && IsTextNode(startNode)) - { - PRInt32 startOffset, endOffset; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - nsCOMPtr nodeAsText = do_QueryInterface(startNode); - res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - } - else - { - // not the easy case. range not contained in single text node. - // there are up to three phases here. There are all the nodes - // reported by the subtree iterator to be processed. And there - // are potentially a starting textnode and an ending textnode - // which are only partially contained by the range. - - // lets handle the nodes reported by the iterator. These nodes - // are entirely contained in the selection range. We build up - // a list of them (since doing operations on the document during - // iteration would perturb the iterator). - - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; - if (!iter) return NS_ERROR_FAILURE; - - nsCOMPtr arrayOfNodes; - nsCOMPtr content; - nsCOMPtr node; - nsCOMPtr isupports; - - // make a array - res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); - if (NS_FAILED(res)) return res; - - // iterate range and build up array - res = iter->Init(range); - // init returns an error if no nodes in range. - // this can easily happen with the subtree - // iterator if the selection doesn't contain - // any *whole* nodes. - if (NS_SUCCEEDED(res)) - { - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - res = iter->CurrentNode(getter_AddRefs(content)); - if (NS_FAILED(res)) return res; - node = do_QueryInterface(content); - if (!node) return NS_ERROR_FAILURE; - if (IsEditable(node)) - { - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); - } - res = iter->Next(); - if (NS_FAILED(res)) return res; - } - } - // first check the start parent of the range to see if it needs to - // be seperately handled (it does if it's a text node, due to how the - // subtree iterator works - it will not have reported it). - if (IsTextNode(startNode) && IsEditable(startNode)) - { - nsCOMPtr nodeAsText = do_QueryInterface(startNode); - PRInt32 startOffset; - PRUint32 textLen; - range->GetStartOffset(&startOffset); - nodeAsText->GetLength(&textLen); - res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - } - - // then loop through the list, set the property on each node - PRUint32 listCount; - PRUint32 j; - arrayOfNodes->Count(&listCount); - for (j = 0; j < listCount; j++) - { - isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); - node = do_QueryInterface(isupports); - res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - arrayOfNodes->RemoveElementAt(0); - } - - // last check the end parent of the range to see if it needs to - // be seperately handled (it does if it's a text node, due to how the - // subtree iterator works - it will not have reported it). - if (IsTextNode(endNode) && IsEditable(endNode)) - { - nsCOMPtr nodeAsText = do_QueryInterface(endNode); - PRInt32 endOffset; - range->GetEndOffset(&endOffset); - res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - } - } - enumerator->Next(); - } - } - if (!cancel) - { - // post-process - res = mRules->DidDoAction(selection, &ruleInfo, res); - } - return res; -} - - - -nsresult -nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, - PRInt32 aStartOffset, - PRInt32 aEndOffset, - nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aTextNode) return NS_ERROR_NULL_POINTER; - - // dont need to do anything if no characters actually selected - if (aStartOffset == aEndOffset) return NS_OK; - - nsresult res = NS_OK; - nsCOMPtr tmp, node = do_QueryInterface(aTextNode); - - // dont need to do anything if property already set on node - PRBool bHasProp; - nsCOMPtr styleNode; - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); - if (bHasProp) return NS_OK; - - // do we need to split the text node? - PRUint32 textLen; - aTextNode->GetLength(&textLen); - - if ( (PRUint32)aEndOffset != textLen ) - { - // we need to split off back of text node - res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); - if (NS_FAILED(res)) return res; - node = tmp; // remember left node - } - if ( aStartOffset ) - { - // we need to split off front of text node - res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); - if (NS_FAILED(res)) return res; - } - - // reparent the node inside inline node with appropriate {attribute,value} - res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); - return res; -} - - -nsresult -nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode, - nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; - - nsresult res = NS_OK; - nsCOMPtr tmp; - nsAutoString tag; - aProperty->ToString(tag); - tag.ToLowerCase(); - - // dont need to do anything if property already set on node - PRBool bHasProp; - nsCOMPtr styleNode; - IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); - if (bHasProp) return NS_OK; - - // is it already the right kind of node, but with wrong attribute? - if (NodeIsType(aNode, aProperty)) - { - // just set the attribute on it. - // but first remove any contrary style in it's children. - res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE); - if (NS_FAILED(res)) return res; - nsCOMPtr elem = do_QueryInterface(aNode); - return SetAttribute(elem, *aAttribute, *aValue); - } - - // can it be put inside inline node? - if (TagCanContain(tag, aNode)) - { - nsCOMPtr priorNode, nextNode; - // is either of it's neighbors the right kind of node? - GetPriorHTMLSibling(aNode, address_of(priorNode)); - GetNextHTMLSibling(aNode, address_of(nextNode)); - if (priorNode && NodeIsType(priorNode, aProperty) && - HasAttrVal(priorNode, aAttribute, aValue) && - IsOnlyAttribute(priorNode, aAttribute) ) - { - // previous sib is already right kind of inline node; slide this over into it - res = MoveNode(aNode, priorNode, -1); - } - else if (nextNode && NodeIsType(nextNode, aProperty) && - HasAttrVal(nextNode, aAttribute, aValue) && - IsOnlyAttribute(priorNode, aAttribute) ) - { - // following sib is already right kind of inline node; slide this over into it - res = MoveNode(aNode, nextNode, 0); - } - else - { - // ok, chuck it in it's very own container - res = InsertContainerAbove(aNode, address_of(tmp), tag, aAttribute, aValue); - } - if (NS_FAILED(res)) return res; - return RemoveStyleInside(aNode, aProperty, aAttribute); - } - // none of the above? then cycle through the children. - nsCOMPtr childNodes; - res = aNode->GetChildNodes(getter_AddRefs(childNodes)); - if (NS_FAILED(res)) return res; - if (childNodes) - { - PRInt32 j; - PRUint32 childCount; - childNodes->GetLength(&childCount); - if (childCount) - { - nsCOMPtr arrayOfNodes; - nsCOMPtr node; - nsCOMPtr isupports; - - // make a array - res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); - if (NS_FAILED(res)) return res; - - // populate the list - for (j=0 ; j < (PRInt32)childCount; j++) - { - nsCOMPtr childNode; - res = childNodes->Item(j, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode)) - { - isupports = do_QueryInterface(childNode); - arrayOfNodes->AppendElement(isupports); - } - } - - // then loop through the list, set the property on each node - PRUint32 listCount; - arrayOfNodes->Count(&listCount); - for (j = 0; j < (PRInt32)listCount; j++) - { - isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); - node = do_QueryInterface(isupports); - res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - arrayOfNodes->RemoveElementAt(0); - } - } - } - return res; -} - - -nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, - nsIAtom *aProperty, - const nsString *aAttribute) -{ - if (!inRange) return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr startNode, endNode, origStartNode; - PRInt32 startOffset, endOffset, origStartOffset; - - res = inRange->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetStartOffset(&startOffset); - if (NS_FAILED(res)) return res; - res = inRange->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetEndOffset(&endOffset); - if (NS_FAILED(res)) return res; - - origStartNode = startNode; - origStartOffset = startOffset; - PRBool sameNode = (startNode==endNode); - - // split any matching style nodes above the start of range - res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - - if (sameNode && (startNode != origStartNode)) - { - // our startNode got split. This changes the offset of the end of our range. - endOffset -= origStartOffset; - } - - // second verse, same as the first... - res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - - // reset the range - res = inRange->SetStart(startNode, startOffset); - if (NS_FAILED(res)) return res; - res = inRange->SetEnd(endNode, endOffset); - return res; -} - -nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr *aNode, - PRInt32 *aOffset, - nsIAtom *aProperty, // null here means we split all properties - const nsString *aAttribute, - nsCOMPtr *outLeftNode, - nsCOMPtr *outRightNode) -{ - if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER; - if (outLeftNode) *outLeftNode = nsnull; - if (outRightNode) *outRightNode = nsnull; - // split any matching style nodes above the node/offset - nsCOMPtr parent, tmp = *aNode; - PRInt32 offset; - while (tmp && !nsHTMLEditUtils::IsBody(tmp)) - { - if ( (aProperty && NodeIsType(tmp, aProperty)) || // node is the correct inline prop - (aProperty == nsIEditProperty::href && IsLinkNode(tmp)) || // node is href - test if really GetParentNode(getter_AddRefs(*aNode)); - *aOffset = offset; - } - tmp->GetParentNode(getter_AddRefs(parent)); - tmp = parent; - } - return NS_OK; -} - -PRBool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode) -{ - if (!aNode) return PR_FALSE; - if (!IsContainer(aNode)) return PR_FALSE; - if (!IsEditable(aNode)) return PR_FALSE; - if (!IsInlineNode(aNode)) return PR_FALSE; - if (NodeIsType(aNode, nsIEditProperty::a)) return PR_FALSE; - return PR_TRUE; -} - -nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, - nsIAtom *aProperty, // null here means remove all properties - const nsString *aAttribute, - PRBool aChildrenOnly) -{ - if (!aNode) return NS_ERROR_NULL_POINTER; - if (IsTextNode(aNode)) return NS_OK; - nsresult res = NS_OK; - - // first process the children - nsCOMPtr child, tmp; - aNode->GetFirstChild(getter_AddRefs(child)); - while (child) - { - // cache next sibling since we might remove child - child->GetNextSibling(getter_AddRefs(tmp)); - res = RemoveStyleInside(child, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - child = tmp; - } - - // then process the node itself - if ( !aChildrenOnly && - ((aProperty && NodeIsType(aNode, aProperty)) || // node is prop we asked for - (aProperty == nsIEditProperty::href && IsLinkNode(aNode))) || // but check for link (IsEmpty()) - { - res = RemoveContainer(aNode); - } - // otherwise we just want to eliminate the attribute - else - { - if (HasAttr(aNode, aAttribute)) - { - // if this matching attribute is the ONLY one on the node, - // then remove the whole node. Otherwise just nix the attribute. - if (IsOnlyAttribute(aNode, aAttribute)) - { - res = RemoveContainer(aNode); - } - else - { - nsCOMPtr elem = do_QueryInterface(aNode); - if (!elem) return NS_ERROR_NULL_POINTER; - res = RemoveAttribute(elem, *aAttribute); - } - } - } - } - return res; -} - -PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, - const nsString *aAttribute) -{ - if (!aNode || !aAttribute) return PR_FALSE; // ooops - nsCOMPtr content = do_QueryInterface(aNode); - if (!content) return PR_FALSE; // ooops - - PRInt32 attrCount, i, nameSpaceID; - nsCOMPtr attrName, prefix; - content->GetAttributeCount(attrCount); - - for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), - *getter_AddRefs(prefix)); - nsAutoString attrString, tmp; - if (!attrName) continue; // ooops - attrName->ToString(attrString); - // if it's the attribute we know about, keep looking - if (attrString.EqualsIgnoreCase(*aAttribute)) continue; - // if it's a special _moz... attribute, keep looking - attrString.Left(tmp,4); - if (tmp.EqualsWithConversion("_moz")) continue; - // otherwise, it's another attribute, so return false - return PR_FALSE; - } - // if we made it through all of them without finding a real attribute - // other than aAttribute, then return PR_TRUE - return PR_TRUE; -} - -PRBool -nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1, - nsIDOMNode *aNode2) -{ - if (!aNode1 || !aNode2) return PR_FALSE; // ooops - nsCOMPtr content1 = do_QueryInterface(aNode1); - if (!content1) return PR_FALSE; // ooops - nsCOMPtr content2 = do_QueryInterface(aNode2); - if (!content2) return PR_FALSE; // ooops - - PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0; - nsCOMPtr attrName, prefix; - nsresult res, res2; - content1->GetAttributeCount(attrCount); - nsAutoString attrString, tmp, attrVal1, attrVal2; - - for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), - *getter_AddRefs(prefix)); - if (!attrName) continue; // ooops - attrName->ToString(attrString); - // if it's a special _moz... attribute, keep going - attrString.Left(tmp,4); - if (tmp.EqualsWithConversion("_moz")) continue; - // otherwise, it's another attribute, so count it - realCount1++; - // and compare it to element2's attributes - res = content1->GetAttribute(nameSpaceID, attrName, attrVal1); - res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2); - if (res != res2) return PR_FALSE; - if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE; - } - - content2->GetAttributeCount(attrCount); - for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), - *getter_AddRefs(prefix)); - if (!attrName) continue; // ooops - attrName->ToString(attrString); - // if it's a special _moz... attribute, keep going - attrString.Left(tmp,4); - if (tmp.EqualsWithConversion("_moz")) continue; - // otherwise, it's another attribute, so count it - realCount2++; - } - - if (realCount1 != realCount2) return PR_FALSE; - // otherwise, attribute counts match, and we already compared them - // when going through the first list, so we're done. - return PR_TRUE; -} - -PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, - const nsString *aAttribute) -{ - if (!aNode) return PR_FALSE; - if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute - - // get element - nsCOMPtr elem = do_QueryInterface(aNode); - if (!elem) return PR_FALSE; - - // get attribute node - nsCOMPtr attNode; - nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); - if ((NS_FAILED(res)) || !attNode) return PR_FALSE; - return PR_TRUE; -} - - -PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aNode) return PR_FALSE; - if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute - - // get element - nsCOMPtr elem = do_QueryInterface(aNode); - if (!elem) return PR_FALSE; - - // get attribute node - nsCOMPtr attNode; - nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); - if ((NS_FAILED(res)) || !attNode) return PR_FALSE; - - // check if attribute has a value - PRBool isSet; - attNode->GetSpecified(&isSet); - // if no value, and that's what we wanted, then return true - if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; - - // get attribute value - nsAutoString attrVal; - attNode->GetValue(attrVal); - - // do values match? - if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE; - return PR_FALSE; -} - - -nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange) -{ - if (!inRange) return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr startNode, endNode, parent; - PRInt32 startOffset, endOffset; - - res = inRange->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetStartOffset(&startOffset); - if (NS_FAILED(res)) return res; - res = inRange->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetEndOffset(&endOffset); - if (NS_FAILED(res)) return res; - - while ( startNode && - !nsHTMLEditUtils::IsBody(startNode) && - IsAtFrontOfNode(startNode, startOffset) ) - { - res = GetNodeLocation(startNode, address_of(parent), &startOffset); - if (NS_FAILED(res)) return res; - startNode = parent; - } - if (!startNode) return NS_ERROR_NULL_POINTER; - - while ( endNode && - !nsHTMLEditUtils::IsBody(endNode) && - IsAtEndOfNode(endNode, endOffset) ) - { - res = GetNodeLocation(endNode, address_of(parent), &endOffset); - if (NS_FAILED(res)) return res; - endNode = parent; - endOffset++; // we are AFTER this node - } - if (!endNode) return NS_ERROR_NULL_POINTER; - - res = inRange->SetStart(startNode, startOffset); - if (NS_FAILED(res)) return res; - res = inRange->SetEnd(endNode, endOffset); - return res; -} - -PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset) -{ - if (!aNode) return PR_FALSE; // oops - if (!aOffset) return PR_TRUE; - - if (IsTextNode(aNode)) - { - return PR_FALSE; - } - else - { - nsCOMPtr firstNode; - GetFirstEditableChild(aNode, address_of(firstNode)); - if (!firstNode) return PR_TRUE; - PRInt32 offset; - nsEditor::GetChildOffset(firstNode, aNode, offset); - if (offset < aOffset) return PR_FALSE; - return PR_TRUE; - } -} - -PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) -{ - if (!aNode) return PR_FALSE; // oops - PRUint32 len; - GetLengthOfDOMNode(aNode, len); - if (aOffset == (PRInt32)len) return PR_TRUE; - - if (IsTextNode(aNode)) - { - return PR_FALSE; - } - else - { - nsCOMPtr lastNode; - GetLastEditableChild(aNode, address_of(lastNode)); - if (!lastNode) return PR_TRUE; - PRInt32 offset; - nsEditor::GetChildOffset(lastNode, aNode, offset); - if (offset < aOffset) return PR_TRUE; - return PR_FALSE; - } -} - -NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue, - PRBool &aFirst, - PRBool &aAny, - PRBool &aAll) -{ - return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull); -} - -NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue, - PRBool &aFirst, - PRBool &aAny, - PRBool &aAll, - nsString *outValue) -{ - if (!aProperty) - return NS_ERROR_NULL_POINTER; -/* - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); } - nsCRT::free(propCString); - } -*/ - nsresult result; - aAny=PR_FALSE; - aAll=PR_TRUE; - aFirst=PR_FALSE; - PRBool first=PR_TRUE; - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - nsCOMPtr selPriv(do_QueryInterface(selection)); - - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - nsCOMPtr collapsedNode; - nsCOMPtr enumerator; - result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; - - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - // XXX: should be a while loop, to get each separate range - // XXX: ERROR_HANDLING can currentItem be null? - if ((NS_SUCCEEDED(result)) && currentItem) - { - PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag - nsCOMPtr range(do_QueryInterface(currentItem)); - - if (isCollapsed) - { - // efficiency hack. we cache prior results for being collapsed in a given text node. - // this speeds up typing. Note that other parts of the editor code have to clear out - // this cache after certain actions. - range->GetStartContainer(getter_AddRefs(collapsedNode)); - if (!collapsedNode) return NS_ERROR_FAILURE; - // refresh the cache if we need to - if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode); - // cache now current, use it! But override it with typeInState results if any... - PRBool isSet, theSetting; - if (aAttribute) - mTypeInState->GetTypingState(isSet, theSetting, aProperty, *aAttribute, outValue); - else - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - return NS_OK; - } - /* - if (aProperty == mBoldAtom.get()) - { - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - } - else - { - aFirst = aAny = aAll = mCachedBoldStyle; - } - return NS_OK; - } - else if (aProperty == mItalicAtom.get()) - { - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - } - else - { - aFirst = aAny = aAll = mCachedItalicStyle; - } - return NS_OK; - } - else if (aProperty == mUnderlineAtom.get()) - { - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - } - else - { - aFirst = aAny = aAll = mCachedUnderlineStyle; - } - return NS_OK; - } */ - } - - // either non-collapsed selection or no cached value: do it the hard way - nsCOMPtr iter; - result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(result)) return result; - if (!iter) return NS_ERROR_NULL_POINTER; - - iter->Init(range); - nsCOMPtr content; - nsAutoString firstValue, theValue; - iter->CurrentNode(getter_AddRefs(content)); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - //if (gNoisy) { printf(" checking node %p\n", content.get()); } - nsCOMPtrtext; - text = do_QueryInterface(content); - PRBool skipNode = PR_FALSE; - if (text) - { - if (!isCollapsed && first && firstNodeInRange) - { - firstNodeInRange = PR_FALSE; - PRInt32 startOffset; - range->GetStartOffset(&startOffset); - PRUint32 count; - text->GetLength(&count); - if (startOffset==(PRInt32)count) - { - //if (gNoisy) { printf(" skipping node %p\n", content.get()); } - skipNode = PR_TRUE; - } - } - } - else - { // handle non-text leaf nodes here - PRBool canContainChildren; - content->CanContainChildren(canContainChildren); - if (canContainChildren) - { - //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } - skipNode = PR_TRUE; - } - else { - //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } - } - } - if (!skipNode) - { - nsCOMPtrnode; - node = do_QueryInterface(content); - if (node) - { - PRBool isSet; - nsCOMPtrresultNode; - if (first) - { - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue); - aFirst = isSet; - first = PR_FALSE; - if (outValue) *outValue = firstValue; - } - else - { - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue); - if (firstValue != theValue) - aAll = PR_FALSE; - } - - if (isSet) { - aAny = PR_TRUE; - } - else { - aAll = PR_FALSE; - } - } - } - result = iter->Next(); - if (NS_FAILED(result)) - break; - iter->CurrentNode(getter_AddRefs(content)); - } - } - if (!aAny) - { // make sure that if none of the selection is set, we don't report all is set - aAll = PR_FALSE; - } - //if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); } - return result; -} - - -NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties() -{ - return RemoveInlinePropertyImpl(nsnull, nsnull); -} - -NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) -{ - return RemoveInlinePropertyImpl(aProperty, aAttribute); -} - -nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsString *aAttribute) -{ - if (!mRules) return NS_ERROR_NOT_INITIALIZED; - ForceCompositionEnd(); - - nsresult res; - nsCOMPtrselection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - if (!selection) return NS_ERROR_NULL_POINTER; - nsCOMPtr selPriv(do_QueryInterface(selection)); - - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - if (isCollapsed) - { - // manipulating text attributes on a collapsed selection only sets state for the next text insertion - - // For links, aProperty uses "href", use "a" instead - if (aProperty == nsIEditProperty::href) - aProperty = nsIEditProperty::a; - - if (aProperty) return mTypeInState->ClearProp(aProperty, *aAttribute); - else return mTypeInState->ClearAllProps(); - } - nsAutoEditBatch batchIt(this); - nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); - nsAutoSelectionReset selectionResetter(selection, this); - nsAutoTxnsConserveSelection dontSpazMySelection(this); - - PRBool cancel, handled; - nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (!cancel && !handled) - { - // get selection range enumerator - nsCOMPtr enumerator; - res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(res)) return res; - if (!enumerator) return NS_ERROR_FAILURE; - - // loop thru the ranges in the selection - enumerator->First(); - nsCOMPtr currentItem; - while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) - { - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(res)) return res; - if (!currentItem) return NS_ERROR_FAILURE; - - nsCOMPtr range( do_QueryInterface(currentItem) ); - - // adjust range to include any ancestors who's children are entirely selected - res = PromoteInlineRange(range); - if (NS_FAILED(res)) return res; - - // remove this style from ancestors of our range endpoints, - // splitting them as appropriate - res = SplitStyleAboveRange(range, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - - // check for easy case: both range endpoints in same text node - nsCOMPtr startNode, endNode; - res = range->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = range->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - if ((startNode == endNode) && IsTextNode(startNode)) - { - // we're done with this range! - } - else - { - // not the easy case. range not contained in single text node. - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; - if (!iter) return NS_ERROR_FAILURE; - - nsCOMPtr arrayOfNodes; - nsCOMPtr content; - nsCOMPtr node; - nsCOMPtr isupports; - - // make a array - res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); - if (NS_FAILED(res)) return res; - - // iterate range and build up array - iter->Init(range); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - res = iter->CurrentNode(getter_AddRefs(content)); - if (NS_FAILED(res)) return res; - node = do_QueryInterface(content); - if (!node) return NS_ERROR_FAILURE; - if (IsEditable(node)) - { - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); - } - res = iter->Next(); - if (NS_FAILED(res)) return res; - } - - // loop through the list, remove the property on each node - PRUint32 listCount; - PRUint32 j; - arrayOfNodes->Count(&listCount); - for (j = 0; j < listCount; j++) - { - isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); - node = do_QueryInterface(isupports); - res = RemoveStyleInside(node, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - arrayOfNodes->RemoveElementAt(0); - } - } - enumerator->Next(); - } - } - if (!cancel) - { - // post-process - res = mRules->DidDoAction(selection, &ruleInfo, res); - } - return res; -} - -NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize() -{ - return RelativeFontChange(1); -} - -NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize() -{ - return RelativeFontChange(-1); -} - -nsresult nsHTMLEditor::GetTextSelectionOffsets(nsISelection *aSelection, - PRInt32 &aOutStartOffset, - PRInt32 &aOutEndOffset) -{ - if(!aSelection) { return NS_ERROR_NULL_POINTER; } - nsresult result; - // initialize out params - aOutStartOffset = 0; // default to first char in selection - aOutEndOffset = -1; // default to total length of text in selection - - nsCOMPtr startNode, endNode, parentNode; - PRInt32 startOffset, endOffset; - aSelection->GetAnchorNode(getter_AddRefs(startNode)); - aSelection->GetAnchorOffset(&startOffset); - aSelection->GetFocusNode(getter_AddRefs(endNode)); - aSelection->GetFocusOffset(&endOffset); - - nsCOMPtr enumerator; - nsCOMPtr selection(aSelection); - nsCOMPtr selPriv(do_QueryInterface(selection)); - result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; - - // don't use "result" in this block - enumerator->First(); - nsCOMPtr currentItem; - nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(findParentResult)) && (currentItem)) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - range->GetCommonAncestorContainer(getter_AddRefs(parentNode)); - } - else - { - parentNode = do_QueryInterface(startNode); - } - - - return GetAbsoluteOffsetsForPoints(startNode, startOffset, - endNode, endOffset, - parentNode, - aOutStartOffset, aOutEndOffset); -} - -nsresult -nsHTMLEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, - PRInt32 aInStartOffset, - nsIDOMNode *aInEndNode, - PRInt32 aInEndOffset, - nsIDOMNode *aInCommonParentNode, - PRInt32 &aOutStartOffset, - PRInt32 &aOutEndOffset) -{ - if(!aInStartNode || !aInEndNode || !aInCommonParentNode) - return NS_ERROR_NULL_POINTER; - - nsresult result; - // initialize out params - aOutStartOffset = 0; // default to first char in selection - aOutEndOffset = -1; // default to total length of text in selection - - nsCOMPtr iter; - result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(result)) return result; - if (!iter) return NS_ERROR_NULL_POINTER; - - PRUint32 totalLength=0; - nsCOMPtrtextNode; - nsCOMPtrblockParentContent = do_QueryInterface(aInCommonParentNode); - iter->Init(blockParentContent); - // loop through the content iterator for each content node - nsCOMPtr content; - result = iter->CurrentNode(getter_AddRefs(content)); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - textNode = do_QueryInterface(content); - if (textNode) - { - nsCOMPtrcurrentNode = do_QueryInterface(textNode); - if (!currentNode) {return NS_ERROR_NO_INTERFACE;} - if (IsEditable(currentNode)) - { - if (currentNode.get() == aInStartNode) - { - aOutStartOffset = totalLength + aInStartOffset; - } - if (currentNode.get() == aInEndNode) - { - aOutEndOffset = totalLength + aInEndOffset; - break; - } - PRUint32 length; - textNode->GetLength(&length); - totalLength += length; - } - } - iter->Next(); - iter->CurrentNode(getter_AddRefs(content)); - } - if (-1==aOutEndOffset) { - aOutEndOffset = totalLength; - } - - // guarantee that aOutStartOffset <= aOutEndOffset - if (aOutEndOffset end"); - return result; -} - nsresult nsHTMLEditor::GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver) { @@ -2299,692 +855,6 @@ nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsISelection *aSelect return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::EDirection aAction) -{ - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - PRBool cancel, handled; - nsresult result; - - // delete placeholder txns merge. - nsAutoPlaceHolderBatch batch(this, gDeleteTxnName); - nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); - - // If it's one of these modes, - // we have to extend the selection first. - // This needs to happen inside selection batching, - // otherwise the deleted text is autocopied to the clipboard. - if (aAction == eNextWord || aAction == ePreviousWord - || aAction == eToBeginningOfLine || aAction == eToEndOfLine) - { - if (!mSelConWeak) return NS_ERROR_NOT_INITIALIZED; - nsCOMPtr selCont (do_QueryReferent(mSelConWeak)); - if (!selCont) - return NS_ERROR_NO_INTERFACE; - - switch (aAction) - { - case eNextWord: - result = selCont->WordMove(PR_TRUE, PR_TRUE); - // DeleteSelectionImpl doesn't handle these actions - // because it's inside batching, so don't confuse it: - aAction = eNone; - break; - case ePreviousWord: - result = selCont->WordMove(PR_FALSE, PR_TRUE); - aAction = eNone; - break; - case eToBeginningOfLine: - selCont->IntraLineMove(PR_TRUE, PR_FALSE); // try to move to end - result = selCont->IntraLineMove(PR_FALSE, PR_TRUE); // select to beginning - aAction = eNone; - break; - case eToEndOfLine: - result = selCont->IntraLineMove(PR_TRUE, PR_TRUE); - // Bugs 54449/54452: the selection jumps to the wrong place - // when deleting past a
and action is eNext or ePrev, - // so setting action to eNone makes delete-to-end marginally usable. - // aAction should really be set to eNext - aAction = eNone; - break; - default: // avoid several compiler warnings - result = NS_OK; - break; - } - if (NS_FAILED(result)) - { -#ifdef DEBUG - printf("Selection controller interface didn't work!\n"); -#endif - return result; - } - } - - // pre-process - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - - nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection); - ruleInfo.collapsedAction = aAction; - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; - if (!cancel && !handled) - { - result = DeleteSelectionImpl(aAction); - } - if (!cancel) - { - // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); - } - - return result; -} - -NS_IMETHODIMP nsHTMLEditor::InsertText(const PRUnichar* aStringToInsert) -{ - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - PRBool cancel, handled; - PRInt32 theAction = nsTextEditRules::kInsertText; - PRInt32 opID = kOpInsertText; - if (mInIMEMode) - { - theAction = nsTextEditRules::kInsertTextIME; - opID = kOpInsertIMEText; - } - nsAutoPlaceHolderBatch batch(this, nsnull); - nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); - - // pre-process - nsresult result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoString resultString; - // XXX can we trust instring to outlive ruleInfo, - // XXX and ruleInfo not to refer to instring in its dtor? - nsAutoString instring(aStringToInsert); - nsTextRulesInfo ruleInfo(theAction); - ruleInfo.inString = &instring; - ruleInfo.outString = &resultString; - ruleInfo.maxLength = mMaxTextLength; - - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; - if (!cancel && !handled) - { - // we rely on rules code for now - no default implementation - } - if (!cancel) - { - // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); - } - return result; -} - - -static nsCOMPtr GetListParent(nsIDOMNode* aNode) -{ - if (!aNode) return nsnull; - nsCOMPtr parent, tmp; - aNode->GetParentNode(getter_AddRefs(parent)); - while (parent) - { - if (nsHTMLEditUtils::IsList(parent)) return parent; - parent->GetParentNode(getter_AddRefs(tmp)); - parent = tmp; - } - return nsnull; -} - -static nsCOMPtr GetTableParent(nsIDOMNode* aNode) -{ - if (!aNode) return nsnull; - nsCOMPtr parent, tmp; - aNode->GetParentNode(getter_AddRefs(parent)); - while (parent) - { - if (nsHTMLEditUtils::IsTable(parent)) return parent; - parent->GetParentNode(getter_AddRefs(tmp)); - parent = tmp; - } - return nsnull; -} - - -NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) -{ - nsAutoString charset; - return InsertHTMLWithCharset(aInputString, charset); -} - -nsresult nsHTMLEditor::InsertHTMLWithContext(const nsString& aInputString, const nsString& aContextStr, const nsString& aInfoStr) -{ - nsAutoString charset; - return InsertHTMLWithCharsetAndContext(aInputString, charset, aContextStr, aInfoStr); -} - - -NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset) -{ - return InsertHTMLWithCharsetAndContext(aInputString, aCharset, nsAutoString(), nsAutoString()); -} - -nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *nsrange, - const nsString& aInputString, - const nsString& aContextStr, - const nsString& aInfoStr, - nsCOMPtr *outFragNode, - PRInt32 *outRangeStartHint, - PRInt32 *outRangeEndHint) -{ - if (!outFragNode || !outRangeStartHint || !outRangeEndHint) - return NS_ERROR_NULL_POINTER; - nsCOMPtr docfrag; - nsresult res = nsrange->CreateContextualFragment(aInputString, getter_AddRefs(docfrag)); - NS_ENSURE_SUCCESS(res, res); - *outFragNode = do_QueryInterface(docfrag); - - res = StripFormattingNodes(*outFragNode); - NS_ENSURE_SUCCESS(res, res); - - // if we have context info, create a fragment for that too - nsCOMPtr contextfrag; - nsCOMPtr contextLeaf; - PRInt32 contextDepth = 0; - if (aContextStr.Length()) - { - res = nsrange->CreateContextualFragment(aContextStr, getter_AddRefs(contextfrag)); - NS_ENSURE_SUCCESS(res, res); - nsCOMPtr contextAsNode (do_QueryInterface(contextfrag)); - res = StripFormattingNodes(contextAsNode); - NS_ENSURE_SUCCESS(res, res); - // cache the deepest leaf in the context - nsCOMPtr junk, child, tmp = contextAsNode; - while (tmp) - { - contextDepth++; - contextLeaf = tmp; - contextLeaf->GetFirstChild(getter_AddRefs(tmp)); - } - // unite the two trees - contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk)); - *outFragNode = contextAsNode; - // no longer have fragmentAsNode in tree - contextDepth--; - } - - // get the infoString contents - nsAutoString numstr1, numstr2; - if (aInfoStr.Length()) - { - PRInt32 err, sep; - sep = aInfoStr.FindChar((PRUnichar)','); - aInfoStr.Left(numstr1, sep); - aInfoStr.Mid(numstr2, sep+1, -1); - *outRangeStartHint = numstr1.ToInteger(&err) + contextDepth; - *outRangeEndHint = numstr2.ToInteger(&err) + contextDepth; - } - else - { - *outRangeStartHint = contextDepth; - *outRangeEndHint = contextDepth; - } - return res; -} - -nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, - nsCOMPtr *outNodeList, - PRInt32 aRangeStartHint, - PRInt32 aRangeEndHint) -{ - if (!outNodeList || !aFragmentAsNode) - return NS_ERROR_NULL_POINTER; - - // First off create a range over the portion of docFrag indicated by - // the range hints. - nsCOMPtr docFragRange; - docFragRange = do_CreateInstance(kCRangeCID); - nsCOMPtr startParent, endParent, tmp; - PRInt32 endOffset; - startParent = aFragmentAsNode; - while (aRangeStartHint > 0) - { - startParent->GetFirstChild(getter_AddRefs(tmp)); - startParent = tmp; - aRangeStartHint--; - NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); - } - endParent = aFragmentAsNode; - while (aRangeEndHint > 0) - { - endParent->GetLastChild(getter_AddRefs(tmp)); - endParent = tmp; - aRangeEndHint--; - NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE); - } - nsresult res = GetLengthOfDOMNode(endParent, (PRUint32&)endOffset); - NS_ENSURE_SUCCESS(res, res); - - res = docFragRange->SetStart(startParent, 0); - NS_ENSURE_SUCCESS(res, res); - res = docFragRange->SetEnd(endParent, endOffset); - NS_ENSURE_SUCCESS(res, res); - - // now use a subtree iterator over the range to create a list of nodes - nsTrivialFunctor functor; - nsDOMSubtreeIterator iter; - res = NS_NewISupportsArray(getter_AddRefs(*outNodeList)); - NS_ENSURE_SUCCESS(res, res); - res = iter.Init(docFragRange); - NS_ENSURE_SUCCESS(res, res); - res = iter.AppendList(functor, *outNodeList); - - return res; -} - - -nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsString& aInputString, - const nsString& aCharset, - const nsString& aContextStr, - const nsString& aInfoStr) -{ - if (!mRules) return NS_ERROR_NOT_INITIALIZED; - - // First, make sure there are no return chars in the document. - // Bad things happen if you insert returns (instead of dom newlines, \n) - // into an editor document. - nsAutoString inputString (aInputString); // hope this does copy-on-write - - // Windows linebreaks: Map CRLF to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r\n"), - NS_ConvertASCIItoUCS2("\n")); - - // Mac linebreaks: Map any remaining CR to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r"), - NS_ConvertASCIItoUCS2("\n")); - - // force IME commit; set up rules sniffing and batching - ForceCompositionEnd(); - nsAutoEditBatch beginBatching(this); - nsAutoRules beginRulesSniffing(this, kOpHTMLPaste, nsIEditor::eNext); - - // Get selection - nsresult res; - nsCOMPtrselection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - - // Get the first range in the selection, for context: - nsCOMPtr range; - res = selection->GetRangeAt(0, getter_AddRefs(range)); - if (NS_FAILED(res)) - return res; - - nsCOMPtr nsrange (do_QueryInterface(range)); - if (!nsrange) - return NS_ERROR_NO_INTERFACE; - - // create a dom document fragment that represents the structure to paste - nsCOMPtr fragmentAsNode; - PRInt32 rangeStartHint, rangeEndHint; - res = CreateDOMFragmentFromPaste(nsrange, inputString, aContextStr, aInfoStr, - address_of(fragmentAsNode), - &rangeStartHint, &rangeEndHint); - NS_ENSURE_SUCCESS(res, res); - - - // make a list of what nodes in docFrag we need to move - nsCOMPtr nodeList; - res = CreateListOfNodesToPaste(fragmentAsNode, address_of(nodeList), rangeStartHint, rangeEndHint); - NS_ENSURE_SUCCESS(res, res); - - // are there any table elements in the list? - // node and offset for insertion - nsCOMPtr parentNode; - PRInt32 offsetOfNewNode; - - // check for table cell selection mode - PRBool cellSelectionMode = PR_FALSE; - nsCOMPtr cell; - res = GetFirstSelectedCell(getter_AddRefs(cell), nsnull); - if (NS_SUCCEEDED(res) && cell) - { - cellSelectionMode = PR_TRUE; - } - - if (cellSelectionMode) - { - // do we have table content to paste? If so, we want to delete - // the selected table cells and replace with new table elements; - // but if not we want to delete _contents_ of cells and replace - // with non-table elements. Use cellSelectionMode bool to - // indicate results. - nsCOMPtr isupports = nodeList->ElementAt(0); - nsCOMPtr firstNode( do_QueryInterface(isupports) ); - if (!nsHTMLEditUtils::IsTableElement(firstNode)) - cellSelectionMode = PR_FALSE; - } - - if (!cellSelectionMode) - { - res = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode); - NS_ENSURE_SUCCESS(res, res); - - // pasting does not inherit local inline styles - res = RemoveAllInlineProperties(); - NS_ENSURE_SUCCESS(res, res); - } - else - { - // delete whole cells: we will replace with new table content - if (1) - { - // Save current selection since DeleteTableCell perturbs it - nsAutoSelectionReset selectionResetter(selection, this); - res = DeleteTableCell(1); - NS_ENSURE_SUCCESS(res, res); - } - // colapse selection to beginning of deleted table content - selection->CollapseToStart(); - } - - // give rules a chance to handle or cancel - nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement); - PRBool cancel, handled; - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (cancel) return NS_OK; // rules canceled the operation - if (!handled) - { - // The rules code (WillDoAction above) might have changed the selection. - // refresh our memory... - res = GetStartNodeAndOffset(selection, address_of(parentNode), &offsetOfNewNode); - if (!parentNode) res = NS_ERROR_FAILURE; - if (NS_FAILED(res)) return res; - - // are we in a text node? If so, split it. - if (IsTextNode(parentNode)) - { - nsCOMPtr temp; - res = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode); - if (NS_FAILED(res)) return res; - res = parentNode->GetParentNode(getter_AddRefs(temp)); - if (NS_FAILED(res)) return res; - parentNode = temp; - } - - // build up list of parents of first node in lst that are either: - // lists, or tables. - nsCOMPtr isup = nodeList->ElementAt(0); - nsCOMPtr pNode( do_QueryInterface(isup) ); - nsCOMPtr listAndTableArray; - res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); - NS_ENSURE_SUCCESS(res, res); - while (pNode) - { - if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) - { - isup = do_QueryInterface(pNode); - listAndTableArray->AppendElement(isup); - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - - // remember number of lists and tables above us - PRUint32 listAndTableParents; - PRInt32 highWaterMark = -1; - listAndTableArray->Count(&listAndTableParents); - - PRUint32 listCount, j; - if (listAndTableParents) - { - // scan insertion list for table elements (other than table). - nodeList->Count(&listCount); - for (j=0; j isupports = nodeList->ElementAt(j); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); - if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) - { - nsCOMPtr theTable = GetTableParent(curNode); - if (theTable) - { - nsCOMPtr isupTable(do_QueryInterface(theTable)); - PRInt32 indexT = listAndTableArray->IndexOf(isupTable); - if (indexT >= 0) - { - highWaterMark = indexT; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - if (nsHTMLEditUtils::IsListItem(curNode)) - { - nsCOMPtr theList = GetListParent(curNode); - if (theList) - { - nsCOMPtr isupList(do_QueryInterface(theList)); - PRInt32 indexL = listAndTableArray->IndexOf(isupList); - if (indexL >= 0) - { - highWaterMark = indexL; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - } - } - // if we have pieces of tables or lists to be inserted, let's force the paste - // to deal with table elements right away, so that it doesn't orphan some - // table or list contents outside the table or list. - if (highWaterMark >= 0) - { - nsCOMPtr isupports = listAndTableArray->ElementAt(highWaterMark); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - nsCOMPtr replaceNode, tmp; - if (nsHTMLEditUtils::IsTable(curNode)) - { - // look upward from curNode for a piece of this table - isup = nodeList->ElementAt(0); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode)) - { - nsCOMPtr tableP = GetTableParent(pNode); - if (tableP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - else // list case - { - // look upward from curNode for a piece of this list - isup = nodeList->ElementAt(0); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsListItem(pNode)) - { - nsCOMPtr listP = GetListParent(pNode); - if (listP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - - if (replaceNode) - { - isupports = do_QueryInterface(replaceNode); - nodeList->ReplaceElementAt(isupports, 0); - // postprocess list to remove any descendants of this node - // so that we dont insert them twice. - do - { - isupports = nodeList->ElementAt(1); - tmp = do_QueryInterface(isupports); - if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) - nodeList->RemoveElementAt(1); - else - break; - } while(tmp); - } - } - - // Loop over the node list and paste the nodes: - PRBool bDidInsert = PR_FALSE; - nsCOMPtr lastInsertNode, insertedContextParent; - nodeList->Count(&listCount); - for (j=0; j isupports = nodeList->ElementAt(j); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); - NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE); - NS_ENSURE_TRUE(!nsHTMLEditUtils::IsBody(curNode), NS_ERROR_FAILURE); - - if (insertedContextParent) - { - // if we had to insert something higher up in the paste heirarchy, we want to - // skip any further paste nodes that descend from that. Else we will paste twice. - if (nsHTMLEditUtils::IsDescendantOf(curNode, insertedContextParent)) - continue; - } - - // give the user a hand on table element insertion. if they have - // a table or table row on the clipboard, and are trying to insert - // into a table or table row, insert the appropriate children instead. - if ( (nsHTMLEditUtils::IsTableRow(curNode) && nsHTMLEditUtils::IsTableRow(parentNode)) - && (nsHTMLEditUtils::IsTable(curNode) || nsHTMLEditUtils::IsTable(parentNode)) ) - { - nsCOMPtr child; - curNode->GetFirstChild(getter_AddRefs(child)); - while (child) - { - InsertNodeAtPoint(child, parentNode, offsetOfNewNode, PR_TRUE); - if (NS_SUCCEEDED(res)) - { - bDidInsert = PR_TRUE; - lastInsertNode = curNode; - offsetOfNewNode++; - } - curNode->GetFirstChild(getter_AddRefs(child)); - } - } - else - { - // try to insert - res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); - if (NS_SUCCEEDED(res)) - { - bDidInsert = PR_TRUE; - lastInsertNode = curNode; - } - - // assume failure means no legal parent in the document heirarchy. - // try again with the parent of curNode in the paste heirarchy. - nsCOMPtr parent; - while (NS_FAILED(res) && curNode) - { - curNode->GetParentNode(getter_AddRefs(parent)); - if (parent && !nsHTMLEditUtils::IsBody(parent)) - { - res = InsertNodeAtPoint(parent, parentNode, offsetOfNewNode, PR_TRUE); - if (NS_SUCCEEDED(res)) - { - bDidInsert = PR_TRUE; - insertedContextParent = parent; - lastInsertNode = parent; - } - } - curNode = parent; - } - } - if (bDidInsert) - { - res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode); - NS_ENSURE_SUCCESS(res, res); - offsetOfNewNode++; - } - } - - // Now collapse the selection to the end of what we just inserted: - if (lastInsertNode) - { - res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode); - NS_ENSURE_SUCCESS(res, res); - selection->Collapse(parentNode, offsetOfNewNode+1); - } - } - - res = mRules->DidDoAction(selection, &ruleInfo, res); - return res; -} - -nsresult -nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode) -{ - NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); - - nsresult res = NS_OK; - nsCOMPtr content = do_QueryInterface(aNode); - if (IsEmptyTextContent(content)) - { - nsCOMPtr parent, ignored; - aNode->GetParentNode(getter_AddRefs(parent)); - if (parent) - { - res = parent->RemoveChild(aNode, getter_AddRefs(ignored)); - return res; - } - } - - if (!nsHTMLEditUtils::IsPre(aNode)) - { - nsCOMPtr child; - aNode->GetLastChild(getter_AddRefs(child)); - - while (child) - { - nsCOMPtr tmp; - child->GetPreviousSibling(getter_AddRefs(tmp)); - res = StripFormattingNodes(child); - NS_ENSURE_SUCCESS(res, res); - child = tmp; - } - } - return res; -} // This is mostly like InsertHTMLWithCharset, // but we can't use that because it is selection-based and @@ -3177,92 +1047,6 @@ nsHTMLEditor::RebuildDocumentFromSource(const nsString& aSourceString) return CloneAttributes(bodyElement, child); } -NS_IMETHODIMP nsHTMLEditor::InsertLineBreak() -{ - nsresult res; - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsAutoEditBatch beginBatching(this); - nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext); - nsCOMPtr selection; - PRBool cancel, handled; - - // pre-process - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - if (!selection) return NS_ERROR_NULL_POINTER; - - nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertBreak); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (!cancel && !handled) - { - // create the new BR node - nsCOMPtr newNode; - nsAutoString tag; tag.AssignWithConversion("BR"); - res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (!newNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called - if (NS_SUCCEEDED(res)) - { - // set the selection to the new node - nsCOMPtrparent; - res = newNode->GetParentNode(getter_AddRefs(parent)); - if (!parent) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called - if (NS_SUCCEEDED(res)) - { - PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not - nsCOMPtrnextNode; - newNode->GetNextSibling(getter_AddRefs(nextNode)); - if (nextNode) - { - nsCOMPtrnextTextNode; - nextTextNode = do_QueryInterface(nextNode); - if (!nextTextNode) { - nextNode = do_QueryInterface(newNode); - } - else { - offsetInParent=0; - } - } - else { - nextNode = do_QueryInterface(newNode); - } - res = GetSelection(getter_AddRefs(selection)); - if (!selection) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called - if (NS_SUCCEEDED(res)) - { - nsCOMPtr selPriv(do_QueryInterface(selection)); - if (-1==offsetInParent) - { - nextNode->GetParentNode(getter_AddRefs(parent)); - res = GetChildOffset(nextNode, parent, offsetInParent); - if (NS_SUCCEEDED(res)) { - // SetInterlinePosition(PR_TRUE) means we want the caret to stick to the content on the "right". - // We want the caret to stick to whatever is past the break. This is - // because the break is on the same line we were on, but the next content - // will be on the following line. - selPriv->SetInterlinePosition(PR_TRUE); - res = selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break - } - } - else - { - res = selection->Collapse(nextNode, offsetInParent); - } - } - } - } - } - if (!cancel) - { - // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE - res = mRules->DidDoAction(selection, &ruleInfo, res); - } - - return res; -} - - NS_IMETHODIMP nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection) { @@ -3307,7 +1091,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe // Named Anchor is a special case, // We collapse to insert element BEFORE the selection // For all other tags, we insert AFTER the selection - if (IsNamedAnchorNode(node)) + if (nsHTMLEditUtils::IsNamedAnchor(node)) { selection->CollapseToStart(); } else { @@ -3387,35 +1171,6 @@ nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, return res; } -// XXX: error handling in this routine needs to be cleaned up! -NS_IMETHODIMP -nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag, - nsIDOMNode ** aNewNode) -{ - nsCOMPtr parentSelectedNode; - PRInt32 offsetOfNewNode; - nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, - offsetOfNewNode); - if (!NS_SUCCEEDED(result)) - return result; - - nsCOMPtr newNode; - result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, - getter_AddRefs(newNode)); - // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases - *aNewNode = newNode; - NS_IF_ADDREF(*aNewNode); - - // we want the selection to be just after the new node - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1); - - return result; -} - NS_IMETHODIMP nsHTMLEditor::SelectElement(nsIDOMElement* aElement) { @@ -3626,71 +1381,6 @@ nsHTMLEditor::GetParagraphState(PRBool &aMixed, nsString &outFormat) return htmlRules->GetParagraphState(aMixed, outFormat); } -NS_IMETHODIMP -nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace) -{ - aMixed = PR_TRUE; - outFace.AssignWithConversion(""); - - nsresult res; - nsAutoString faceStr; faceStr.AssignWithConversion("face"); - PRBool first, any, all; - - res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace); - if (NS_FAILED(res)) return res; - if (any && !all) return res; // mixed - if (all) - { - aMixed = PR_FALSE; - return res; - } - - res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all); - if (NS_FAILED(res)) return res; - if (any && !all) return res; // mixed - if (all) - { - aMixed = PR_FALSE; - nsIEditProperty::tt->ToString(outFace); - } - - if (!any) - { - // there was no font face attrs of any kind. We are in normal font. - outFace.AssignWithConversion(""); - aMixed = PR_FALSE; - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditor::GetFontColorState(PRBool &aMixed, nsString &aOutColor) -{ - aMixed = PR_TRUE; - aOutColor.AssignWithConversion(""); - - nsresult res; - nsAutoString colorStr; colorStr.AssignWithConversion("color"); - PRBool first, any, all; - - res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &colorStr, nsnull, first, any, all, &aOutColor); - if (NS_FAILED(res)) return res; - if (any && !all) return res; // mixed - if (all) - { - aMixed = PR_FALSE; - return res; - } - - if (!any) - { - // there was no font color attrs of any kind.. - aOutColor.AssignWithConversion(""); - aMixed = PR_FALSE; - } - return res; -} - NS_IMETHODIMP nsHTMLEditor::GetBackgroundColorState(PRBool &aMixed, nsString &aOutColor) { @@ -4142,8 +1832,8 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * nsAutoString TagName(aTagName); TagName.ToLowerCase(); - PRBool getLink = IsLink(TagName); - PRBool getNamedAnchor = IsNamedAnchor(TagName); + PRBool getLink = IsLinkTag(TagName); + PRBool getNamedAnchor = IsNamedAnchorTag(TagName); if ( getLink || getNamedAnchor) { TagName.AssignWithConversion("a"); @@ -4161,8 +1851,8 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * { nsAutoString currentTagName; // Test if we have a link (an anchor with href set) - if ( (getLink && IsLinkNode(currentNode)) || - (getNamedAnchor && IsNamedAnchorNode(currentNode)) ) + if ( (getLink && nsHTMLEditUtils::IsLink(currentNode)) || + (getNamedAnchor && nsHTMLEditUtils::IsNamedAnchor(currentNode)) ) { bNodeFound = PR_TRUE; break; @@ -4170,14 +1860,14 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * if (findList) { // Match "ol", "ul", or "dl" for lists - if (IsListNode(currentNode)) + if (nsHTMLEditUtils::IsList(currentNode)) goto NODE_FOUND; } else if (findTableCell) { // Table cells are another special case: // Match either "td" or "th" for them - if (IsCellNode(currentNode)) + if (nsHTMLEditUtils::IsTableCell(currentNode)) goto NODE_FOUND; } else { @@ -4249,8 +1939,8 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu TagName.ToLowerCase(); // Empty string indicates we should match any element tag PRBool anyTag = (TagName.IsEmpty()); - PRBool isLinkTag = IsLink(TagName); - PRBool isNamedAnchorTag = IsNamedAnchor(TagName); + PRBool isLinkTag = IsLinkTag(TagName); + PRBool isNamedAnchorTag = IsNamedAnchorTag(TagName); nsCOMPtr selectedElement; nsCOMPtr range; @@ -4282,8 +1972,8 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu // Test for appropriate node type requested if (anyTag || (TagName == domTagName) || - (isLinkTag && IsLinkNode(selectedNode)) || - (isNamedAnchorTag && IsNamedAnchorNode(selectedNode))) + (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) || + (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode))) { bNodeFound = PR_TRUE; selectedElement = do_QueryInterface(selectedNode); @@ -4357,7 +2047,7 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu { nsCOMPtr anchorChild; anchorChild = GetChildAt(anchorNode,anchorOffset); - if (anchorChild && IsLinkNode(anchorChild) && + if (anchorChild && nsHTMLEditUtils::IsLink(anchorChild) && (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) { selectedElement = do_QueryInterface(anchorChild); @@ -4427,8 +2117,8 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu // The "A" tag is a pain, // used for both link(href is set) and "Named Anchor" nsCOMPtr selectedNode = do_QueryInterface(selectedElement); - if ( (isLinkTag && IsLinkNode(selectedNode)) || - (isNamedAnchorTag && IsNamedAnchorNode(selectedNode)) ) + if ( (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) || + (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode)) ) { bNodeFound = PR_TRUE; } else if (TagName == domTagName) { // All other tag names are handled here @@ -4482,7 +2172,7 @@ nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement* TagName.ToLowerCase(); nsAutoString realTagName; - if (IsLink(TagName) || IsNamedAnchor(TagName)) + if (IsLinkTag(TagName) || IsNamedAnchorTag(TagName)) { realTagName.AssignWithConversion("a"); } else { @@ -4657,86 +2347,6 @@ NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const n return res; } -NS_IMETHODIMP -nsHTMLEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty) -{ - if (!aDocumentIsEmpty) - return NS_ERROR_NULL_POINTER; - - if (!mRules) - return NS_ERROR_NOT_INITIALIZED; - - return mRules->DocumentIsEmpty(aDocumentIsEmpty); -} - -NS_IMETHODIMP -nsHTMLEditor::GetTextLength(PRInt32 *aCount) -{ - if (!aCount) { return NS_ERROR_NULL_POINTER; } - nsresult result; - // initialize out params - *aCount = 0; - - // special-case for empty document, to account for the bogus text node - PRBool docEmpty; - result = GetDocumentIsEmpty(&docEmpty); - if (NS_FAILED(result)) return result; - if (docEmpty) - { - *aCount = 0; - return NS_OK; - } - - // get the body node - nsCOMPtr bodyElement; - result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(result)) { return result; } - if (!bodyElement) { return NS_ERROR_NULL_POINTER; } - - // get the offsets of the first and last children of the body node - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); - if (!bodyNode) { return NS_ERROR_NULL_POINTER; } - PRInt32 numBodyChildren=0; - nsCOMPtrlastChild; - result = bodyNode->GetLastChild(getter_AddRefs(lastChild)); - if (NS_FAILED(result)) { return result; } - if (!lastChild) { return NS_ERROR_NULL_POINTER; } - result = GetChildOffset(lastChild, bodyNode, numBodyChildren); - if (NS_FAILED(result)) { return result; } - - // count - PRInt32 start, end; - result = GetAbsoluteOffsetsForPoints(bodyNode, 0, - bodyNode, numBodyChildren, - bodyNode, start, end); - if (NS_SUCCEEDED(result)) - { - NS_ASSERTION(0==start, "GetAbsoluteOffsetsForPoints failed to set start correctly."); - NS_ASSERTION(0<=end, "GetAbsoluteOffsetsForPoints failed to set end correctly."); - if (0<=end) { - *aCount = end; - } - } - return result; -} - -NS_IMETHODIMP -nsHTMLEditor::SetMaxTextLength(PRInt32 aMaxTextLength) -{ - mMaxTextLength = aMaxTextLength; - return NS_OK; -} - -NS_IMETHODIMP -nsHTMLEditor::GetMaxTextLength(PRInt32* aMaxTextLength) -{ - if (!aMaxTextLength) - return NS_ERROR_INVALID_POINTER; - *aMaxTextLength = mMaxTextLength; - return NS_OK; -} - - #ifdef XP_MAC #pragma mark - #pragma mark nsIEditorStyleSheets methods @@ -4913,167 +2523,6 @@ nsHTMLEditor::ApplyDocumentOrOverrideStyleSheet(const nsString& aURL, PRBool aOv #pragma mark - #endif -NS_IMETHODIMP -nsHTMLEditor::GetBodyStyleContext(nsIStyleContext** aStyleContext) -{ - nsCOMPtr body; - nsresult res = GetRootElement(getter_AddRefs(body)); - if (NS_FAILED(res)) return res; - nsCOMPtr content = do_QueryInterface(body); - - nsIFrame *frame; - if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; - res = ps->GetPrimaryFrameFor(content, &frame); - if (NS_FAILED(res)) return res; - - return ps->GetStyleContextFor(frame, aStyleContext); -} - -// -// Get an appropriate wrap width for saving this document. -// -NS_IMETHODIMP -nsHTMLEditor::GetWrapWidth(PRInt32 *aWrapColumn) -{ - nsresult res; - - if (! aWrapColumn) - return NS_ERROR_NULL_POINTER; - - *aWrapColumn = -1; // default: no wrap - - // Don't wrap output of text fields - if (mFlags & eEditorSingleLineMask) - return NS_OK; - - if (mFlags & eEditorPlaintextMask) - { - nsCOMPtr styleContext; - res = GetBodyStyleContext(getter_AddRefs(styleContext)); - if (NS_SUCCEEDED(res) && styleContext) - { - const nsStyleText* styleText = - (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text); - - if (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) - { - *aWrapColumn = 0; // wrap to window width - return NS_OK; - } - else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace) - { - const nsStylePosition* stylePosition = - (const nsStylePosition*)styleContext->GetStyleData(eStyleStruct_Position); - if (stylePosition->mWidth.GetUnit() == eStyleUnit_Chars) - { - *aWrapColumn = stylePosition->mWidth.GetIntValue(); - return NS_OK; - } -#ifdef DEBUG_akkana - else - printf("Can't get wrap column: style unit is %d\n", - stylePosition->mWidth.GetUnit()); -#endif - } - } - } - - // If we get here, we weren't able to get the wrap column from style, - // or we aren't plaintext so we don't expect to. - // So fall back on nsEditor (which uses a pref). - return nsEditor::GetWrapWidth(aWrapColumn); -} - -// -// See if the style value includes this attribute, and if it does, -// cut out everything from the attribute to the next semicolon. -// -static void CutStyle(const char* stylename, nsString& styleValue) -{ - // Find the current wrapping type: - PRInt32 styleStart = styleValue.Find(stylename, PR_TRUE); - if (styleStart >= 0) - { - PRInt32 styleEnd = styleValue.Find(";", PR_FALSE, styleStart); - if (styleEnd > styleStart) - styleValue.Cut(styleStart, styleEnd - styleStart + 1); - else - styleValue.Cut(styleStart, styleValue.Length() - styleStart); - } -} - -// -// Change the wrap width on the first
 tag in this document.
-// (Eventually want to search for more than one in case there are
-// interspersed quoted text blocks.)
-// Alternately: Change the wrap width on the editor style sheet.
-// 
-NS_IMETHODIMP nsHTMLEditor::SetWrapWidth(PRInt32 aWrapColumn)
-{
-  nsresult res;
-
-  // Ought to set a style sheet here ...
-  // Probably should keep around an mPlaintextStyleSheet for this purpose.
-  nsCOMPtr bodyElement;
-  res = GetRootElement(getter_AddRefs(bodyElement));
-  if (NS_FAILED(res)) return res;
-  if (!bodyElement) return NS_ERROR_NULL_POINTER;
-
-  // Get the current style for this body element:
-  nsAutoString styleName; styleName.AssignWithConversion("style");
-  nsAutoString styleValue;
-  res = bodyElement->GetAttribute(styleName, styleValue);
-  if (NS_FAILED(res)) return res;
-
-  // We'll replace styles for these values:
-  CutStyle("white-space", styleValue);
-  CutStyle("width", styleValue);
-  CutStyle("font-family", styleValue);
-
-  // If we have other style left, trim off any existing semicolons
-  // or whitespace, then add a known semicolon-space:
-  if (styleValue.Length() > 0)
-  {
-    styleValue.Trim("; \t", PR_FALSE, PR_TRUE);
-    styleValue.AppendWithConversion("; ");
-  }
-
-  // Make sure we have fixed-width font.  This should be done for us,
-  // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;".
-  // Only do this if we're wrapping.
-  PRUint32 flags = 0;
-  GetFlags(&flags);
-  if ((flags & eEditorEnableWrapHackMask) && aWrapColumn >= 0)
-    styleValue.AppendWithConversion("font-family: -moz-fixed; ");
-
-  // and now we're ready to set the new whitespace/wrapping style.
-  if (aWrapColumn > 0)        // Wrap to a fixed column
-  {
-    styleValue.AppendWithConversion("white-space: -moz-pre-wrap; width: ");
-    styleValue.AppendInt(aWrapColumn);
-    styleValue.AppendWithConversion("ch;");
-  }
-  else if (aWrapColumn == 0)
-    styleValue.AppendWithConversion("white-space: -moz-pre-wrap;");
-  else
-    styleValue.AppendWithConversion("white-space: pre;");
-
-  res = bodyElement->SetAttribute(styleName, styleValue);
-
-#ifdef DEBUG_wrapstyle
-  char* curstyle = styleValue.ToNewCString();
-  printf("Setting style: [%s]\nNow body looks like:\n", curstyle);
-  Recycle(curstyle);
-  //nsCOMPtr nodec (do_QueryInterface(bodyElement));
-  //if (nodec) nodec->List(stdout);
-  //printf("-----\n");
-#endif /* DEBUG_akkana */
-
-  return res;
-}
-
 NS_IMETHODIMP
 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
 {
@@ -5257,1075 +2706,6 @@ NS_IMETHODIMP nsHTMLEditor::CanCopy(PRBool &aCanCopy)
   return NS_OK;
 }
 
-NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable)
-{
-  // Create generic Transferable for getting the data
-  nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
-                                          NS_GET_IID(nsITransferable), 
-                                          (void**)transferable);
-  if (NS_FAILED(rv))
-    return rv;
-
-  // Get the nsITransferable interface for getting the data from the clipboard
-  if (transferable)
-  {
-    // Create the desired DataFlavor for the type of data
-    // we want to get out of the transferable
-    if ((mFlags & eEditorPlaintextMask) == 0)  // This should only happen in html editors, not plaintext
-    {
-      (*transferable)->AddDataFlavor(kJPEGImageMime);
-      (*transferable)->AddDataFlavor(kHTMLMime);
-      (*transferable)->AddDataFlavor(kFileMime);
-    }
-    (*transferable)->AddDataFlavor(kUnicodeMime);
-  }
-  
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, 
-                                                   const nsString& aContextStr,
-                                                   const nsString& aInfoStr)
-{
-  nsresult rv = NS_OK;
-  char* bestFlavor = nsnull;
-  nsCOMPtr genericDataObj;
-  PRUint32 len = 0;
-  if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
-  {
-    nsAutoTxnsConserveSelection dontSpazMySelection(this);
-    nsAutoString flavor, stuffToPaste;
-    flavor.AssignWithConversion( bestFlavor );   // just so we can use flavor.Equals()
-#ifdef DEBUG_akkana
-    printf("Got flavor [%s]\n", bestFlavor);
-#endif
-    if (flavor.EqualsWithConversion(kHTMLMime))
-    {
-      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
-      if (textDataObj && len > 0)
-      {
-        PRUnichar* text = nsnull;
-
-        textDataObj->ToString ( &text );
-        stuffToPaste.Assign ( text, len / 2 );
-        nsAutoEditBatch beginBatching(this);
-        rv = InsertHTMLWithContext(stuffToPaste, aContextStr, aInfoStr);
-        if (text)
-          nsMemory::Free(text);
-      }
-    }
-    else if (flavor.EqualsWithConversion(kUnicodeMime))
-    {
-      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
-      if (textDataObj && len > 0)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj->ToString ( &text );
-        stuffToPaste.Assign ( text, len / 2 );
-        nsAutoEditBatch beginBatching(this);
-        // pasting does not inherit local inline styles
-        RemoveAllInlineProperties();
-        rv = InsertText(stuffToPaste.GetUnicode());
-        if (text)
-          nsMemory::Free(text);
-      }
-    }
-    else if (flavor.EqualsWithConversion(kFileMime))
-    {
-      nsCOMPtr fileObj ( do_QueryInterface(genericDataObj) );
-      if (fileObj && len > 0)
-      {
-        nsCOMPtr fileURL;
-        rv = nsComponentManager::CreateInstance("@mozilla.org/network/standard-url;1", nsnull, 
-                                     NS_GET_IID(nsIURL), getter_AddRefs(fileURL));
-        if (NS_FAILED(rv))
-          return rv;
-        
-        if ( fileURL )
-        {
-          rv = fileURL->SetFile( fileObj );
-          if (NS_FAILED(rv))
-            return rv;
-          
-          PRBool insertAsImage = PR_FALSE;
-          char *fileextension = nsnull;
-          rv = fileURL->GetFileExtension( &fileextension );
-          if ( NS_SUCCEEDED(rv) && fileextension )
-          {
-            if ( (nsCRT::strcasecmp( fileextension, "jpg" ) == 0 )
-              || (nsCRT::strcasecmp( fileextension, "jpeg" ) == 0 )
-              || (nsCRT::strcasecmp( fileextension, "gif" ) == 0 )
-              || (nsCRT::strcasecmp( fileextension, "png" ) == 0 ) )
-            {
-              insertAsImage = PR_TRUE;
-            }
-          }
-          if (fileextension) nsCRT::free(fileextension);
-          
-          char *urltext = nsnull;
-          rv = fileURL->GetSpec( &urltext );
-          if ( NS_SUCCEEDED(rv) && urltext && urltext[0] != 0)
-          {
-            len = strlen(urltext);
-            if ( insertAsImage )
-            {
-              stuffToPaste.AssignWithConversion ( "" );
-            }
-            else /* insert as link */
-            {
-              stuffToPaste.AssignWithConversion ( "" );
-              stuffToPaste.AppendWithConversion ( urltext, len );
-              stuffToPaste.AppendWithConversion ( "" );
-            }
-            nsAutoEditBatch beginBatching(this);
-            rv = InsertHTML(stuffToPaste);
-          }
-          if (urltext) nsCRT::free(urltext);
-        }
-      }
-    }
-    else if (flavor.EqualsWithConversion(kJPEGImageMime))
-    {
-      // Insert Image code here
-      printf("Don't know how to insert an image yet!\n");
-      //nsIImage* image = (nsIImage *)data;
-      //NS_RELEASE(image);
-      rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code
-    }
-  }
-  nsCRT::free(bestFlavor);
-      
-  // Try to scroll the selection into view if the paste/drop succeeded
-  if (NS_SUCCEEDED(rv))
-  {
-    nsCOMPtr selCon;
-    if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
-      selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
-{
-  ForceCompositionEnd();
-  
-  nsresult rv;
-  NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  nsCOMPtr dragSession(do_QueryInterface(dragService));
-  
-  if (!dragSession) return NS_OK;
-
-  // Get the nsITransferable interface for getting the data from the drop
-  nsCOMPtr trans;
-  rv = PrepareTransferable(getter_AddRefs(trans));
-  if (NS_FAILED(rv)) return rv;
-  if (!trans) return NS_OK;  // NS_ERROR_FAILURE; SHOULD WE FAIL?
-
-  PRUint32 numItems = 0; 
-  rv = dragSession->GetNumDropItems(&numItems);
-  if (NS_FAILED(rv)) return rv;
-
-  // Combine any deletion and drop insertion into one transaction
-  nsAutoEditBatch beginBatching(this);
-
-  PRUint32 i; 
-  PRBool doPlaceCaret = PR_TRUE;
-  for (i = 0; i < numItems; ++i)
-  {
-    rv = dragSession->GetData(trans, i);
-    if (NS_FAILED(rv)) return rv;
-    if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
-
-    if ( doPlaceCaret )
-    {
-      // check if the user pressed the key to force a copy rather than a move
-      // if we run into problems here, we'll just assume the user doesn't want a copy
-      PRBool userWantsCopy = PR_FALSE;
-
-      nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent));
-      if (!nsuiEvent) return NS_ERROR_FAILURE;
-
-      nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) );
-      if (mouseEvent)
-
-#ifdef XP_MAC
-        mouseEvent->GetAltKey(&userWantsCopy);
-#else
-        mouseEvent->GetCtrlKey(&userWantsCopy);
-#endif
-      // Source doc is null if source is *not* the current editor document
-      nsCOMPtr srcdomdoc;
-      rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
-      if (NS_FAILED(rv)) return rv;
-
-      // Current doc is destination
-      nsCOMPtrdestdomdoc; 
-      rv = GetDocument(getter_AddRefs(destdomdoc)); 
-      if (NS_FAILED(rv)) return rv;
-
-      nsCOMPtr selection;
-      rv = GetSelection(getter_AddRefs(selection));
-      if (NS_FAILED(rv)) return rv;
-      if (!selection) return NS_ERROR_FAILURE;
-
-      PRBool isCollapsed;
-      rv = selection->GetIsCollapsed(&isCollapsed);
-      if (NS_FAILED(rv)) return rv;
-      
-      // Parent and offset under the mouse cursor
-      nsCOMPtr newSelectionParent;
-      PRInt32 newSelectionOffset = 0;
-      rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
-      if (NS_FAILED(rv)) return rv;
-      if (!newSelectionParent) return NS_ERROR_FAILURE;
-
-      rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
-      if (NS_FAILED(rv)) return rv;
-      /* Creating a range to store insert position because when
-         we delete the selection, range gravity will make sure the insertion
-         point is in the correct place */
-      nsCOMPtr destinationRange;
-      rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange));
-      if (NS_FAILED(rv))
-        return rv;
-      if(!destinationRange)
-        return NS_ERROR_FAILURE;
-
-      // We never have to delete if selection is already collapsed
-      PRBool deleteSelection = PR_FALSE;
-      PRBool cursorIsInSelection = PR_FALSE;
-
-      // Check if mouse is in the selection
-      if (!isCollapsed)
-      {
-        PRInt32 rangeCount;
-        rv = selection->GetRangeCount(&rangeCount);
-        if (NS_FAILED(rv)) 
-          return rv?rv:NS_ERROR_FAILURE;
-
-        for (PRInt32 j = 0; j < rangeCount; j++)
-        {
-          nsCOMPtr range;
-
-          rv = selection->GetRangeAt(j, getter_AddRefs(range));
-          if (NS_FAILED(rv) || !range) 
-            continue;//dont bail yet, iterate through them all
-
-          nsCOMPtr nsrange(do_QueryInterface(range));
-          if (NS_FAILED(rv) || !nsrange) 
-            continue;//dont bail yet, iterate through them all
-
-          rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
-          if(cursorIsInSelection)
-            break;
-        }
-        if (cursorIsInSelection)
-        {
-          // Dragging within same doc can't drop on itself -- leave!
-          // (We shouldn't get here - drag event shouldn't have started if over selection)
-          if (srcdomdoc == destdomdoc)
-            return NS_OK;
-          
-          // Dragging from another window onto a selection
-          // XXX Decision made to NOT do this,
-          //     note that 4.x does replace if dropped on
-          //deleteSelection = PR_TRUE;
-        }
-        else 
-        {
-          // We are NOT over the selection
-          if (srcdomdoc == destdomdoc)
-          {
-            // Within the same doc: delete if user doesn't want to copy
-            deleteSelection = !userWantsCopy;
-          }
-          else
-          {
-            // Different source doc: Don't delete
-            deleteSelection = PR_FALSE;
-          }
-        }
-      }
-
-      if (deleteSelection)
-      {
-        rv = DeleteSelection(eNone);
-        if (NS_FAILED(rv)) return rv;
-      }
-
-      // If we deleted the selection because we dropped from another doc,
-      //  then we don't have to relocate the caret (insert at the deletion point)
-      if (!(deleteSelection && srcdomdoc != destdomdoc))
-      {
-        // Move the selection to the point under the mouse cursor
-        rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent));
-        if (NS_FAILED(rv))
-          return rv;
-        if(!newSelectionParent)
-          return NS_ERROR_FAILURE;
-       
-        rv = destinationRange->GetStartOffset(&newSelectionOffset);
-        if (NS_FAILED(rv))
-          return rv;
-        selection->Collapse(newSelectionParent, newSelectionOffset);
-      }      
-      // We have to figure out whether to delete and relocate caret only once
-      doPlaceCaret = PR_FALSE;
-    }
-    
-    rv = InsertFromTransferable(trans, nsAutoString(), nsAutoString());
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag)
-{
-  /* we really should be checking the XY coordinates of the mouseevent and ensure that
-   * that particular point is actually within the selection (not just that there is a selection)
-   */
-  aCanDrag = PR_FALSE;
- 
-  // KLUDGE to work around bug 50703
-  // After double click and object property editing, 
-  //  we get a spurious drag event
-  if (mIgnoreSpuriousDragEvent)
-  {
-#ifdef DEBUG_cmanske
-    printf(" *** IGNORING SPURIOUS DRAG EVENT!\n");
-#endif
-    mIgnoreSpuriousDragEvent = PR_FALSE;
-    return NS_OK;
-  }
-   
-  nsCOMPtr selection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-    
-  PRBool isCollapsed;
-  res = selection->GetIsCollapsed(&isCollapsed);
-  if (NS_FAILED(res)) return res;
-  
-  // if we are collapsed, we have no selection so nothing to drag
-  if ( isCollapsed )
-    return NS_OK;
-
-  nsCOMPtr eventTarget;
-  res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget));
-  if (NS_FAILED(res)) return res;
-  if ( eventTarget )
-  {
-    nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget);
-    if ( eventTargetDomNode )
-    {
-      PRBool amTargettedCorrectly = PR_FALSE;
-      res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly);
-      if (NS_FAILED(res)) return res;
-
-    	aCanDrag = amTargettedCorrectly;
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent)
-{
-  nsresult rv;
-
-  nsCOMPtr eventTarget;
-  rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
-  if (NS_FAILED(rv)) return rv;
-  nsCOMPtr domnode = do_QueryInterface(eventTarget);
-
-  /* get the selection to be dragged */
-  nsCOMPtr selection;
-  rv = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(rv)) return rv;
-
-  /* create an array of transferables */
-  nsCOMPtr transferableArray;
-  NS_NewISupportsArray(getter_AddRefs(transferableArray));
-  if (transferableArray == nsnull)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  /* get the drag service */
-  NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  /* create html flavor transferable */
-  nsCOMPtr trans;
-  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
-                                        NS_GET_IID(nsITransferable), 
-                                        getter_AddRefs(trans));
-  if (NS_FAILED(rv)) return rv;
-  if ( !trans ) return NS_ERROR_OUT_OF_MEMORY;
-
-  nsCOMPtr domdoc;
-  rv = GetDocument(getter_AddRefs(domdoc));
-  if (NS_FAILED(rv)) return rv;
-	
-  nsCOMPtr doc = do_QueryInterface(domdoc);
-  if (doc)
-  {
-    nsCOMPtr docEncoder;
-
-    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html");
-    NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
-
-    docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0);
-    docEncoder->SetSelection(selection);
-
-    nsAutoString buffer;
-
-    rv = docEncoder->EncodeToString(buffer);
-  
-    if (NS_FAILED(rv))
-      return rv;
-
-    if ( !buffer.IsEmpty() )
-    {
-      nsCOMPtr htmlConverter;
-      rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter),
-                                              getter_AddRefs(htmlConverter));
-      if (NS_FAILED(rv)) return rv;
-      if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY;
-
-      nsCOMPtr dataWrapper;
-      rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull,
-                                              NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper));
-      if (NS_FAILED(rv)) return rv;
-      if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY;
-
-      rv = trans->AddDataFlavor(kHTMLMime);
-      if (NS_FAILED(rv)) return rv;
-      rv = trans->SetConverter(htmlConverter);
-      if (NS_FAILED(rv)) return rv;
-
-      rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) );
-      if (NS_FAILED(rv)) return rv;
-
-      // QI the data object an |nsISupports| so that when the transferable holds
-      // onto it, it will addref the correct interface.
-      nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) );
-      rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2);
-      if (NS_FAILED(rv)) return rv;
-
-      /* add the transferable to the array */
-      rv = transferableArray->AppendElement(trans);
-      if (NS_FAILED(rv)) return rv;
-
-      /* invoke drag */
-      unsigned int flags;
-      // in some cases we'll want to cut rather than copy... hmmmmm...
-      // if ( wantToCut )
-      //   flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE;
-      // else
-        flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
-      
-      rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags);
-      if (NS_FAILED(rv)) return rv;
-
-      aDragEvent->PreventBubble();
-    }
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType)
-{
-  ForceCompositionEnd();
-
-  // Get Clipboard Service
-  nsresult rv;
-  NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv );
-  if ( NS_FAILED(rv) )
-    return rv;
-    
-  // Get the nsITransferable interface for getting the data from the clipboard
-  nsCOMPtr trans;
-  rv = PrepareTransferable(getter_AddRefs(trans));
-  if (NS_SUCCEEDED(rv) && trans)
-  {
-    // Get the Data from the clipboard  
-    if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
-    {
-      // also get additional html copy hints, if present
-      nsAutoString contextStr, infoStr;
-      nsCOMPtr contextDataObj, infoDataObj;
-      PRUint32 contextLen, infoLen;
-      nsCOMPtr textDataObj;
-      
-      nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID);
-      NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER);
-      contextTrans->AddDataFlavor(kHTMLContext);
-      clipboard->GetData(contextTrans, aSelectionType);
-      contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen);
-
-      nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID);
-      NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER);
-      infoTrans->AddDataFlavor(kHTMLInfo);
-      clipboard->GetData(infoTrans, aSelectionType);
-      infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen);
-      
-      if (contextDataObj)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj = do_QueryInterface(contextDataObj);
-        textDataObj->ToString ( &text );
-        contextStr.Assign ( text, contextLen / 2 );
-        if (text)
-          nsMemory::Free(text);
-      }
-      
-      if (infoDataObj)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj = do_QueryInterface(infoDataObj);
-        textDataObj->ToString ( &text );
-        infoStr.Assign ( text, infoLen / 2 );
-        if (text)
-          nsMemory::Free(text);
-      }
-      rv = InsertFromTransferable(trans, contextStr, infoStr);
-    }
-  }
-
-  return rv;
-}
-
-
-NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste)
-{
-  aCanPaste = PR_FALSE;
-
-  // can't paste if readonly
-  if (!IsModifiable())
-    return NS_OK;
-
-  nsresult rv;
-  NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
-  if (NS_FAILED(rv)) return rv;
-  
-  // the flavors that we can deal with
-  char* textEditorFlavors[] = { kUnicodeMime, nsnull };
-  char* htmlEditorFlavors[] = { kJPEGImageMime, kHTMLMime, nsnull };
-
-  nsCOMPtr flavorsList;
-  rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, 
-         NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
-  if (NS_FAILED(rv)) return rv;
-  
-  PRUint32 editorFlags;
-  GetFlags(&editorFlags);
-  
-  // add the flavors for all editors
-  for (char** flavor = textEditorFlavors; *flavor; flavor++)
-  {
-    nsCOMPtr flavorString;            
-    nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, 
-         NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
-    if (flavorString)
-    {
-      flavorString->SetData(*flavor);
-      flavorsList->AppendElement(flavorString);
-    }
-  }
-  
-  // add the HTML-editor only flavors
-  if ((editorFlags & eEditorPlaintextMask) == 0)
-  {
-    for (char** htmlFlavor = htmlEditorFlavors; *htmlFlavor; htmlFlavor++)
-    {
-      nsCOMPtr flavorString;            
-      nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, 
-           NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
-      if (flavorString)
-      {
-        flavorString->SetData(*htmlFlavor);
-        flavorsList->AppendElement(flavorString);
-      }
-    }
-  }
-  
-  PRBool haveFlavors;
-  rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
-  if (NS_FAILED(rv)) return rv;
-  
-  aCanPaste = haveFlavors;
-  return NS_OK;
-}
-
-
-// 
-// HTML PasteAsQuotation: Paste in a blockquote type=cite
-//
-NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation(PRInt32 aSelectionType)
-{
-  if (mFlags & eEditorPlaintextMask)
-    return PasteAsPlaintextQuotation(aSelectionType);
-
-  nsAutoString citation;
-  return PasteAsCitedQuotation(citation, aSelectionType);
-}
-
-NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation,
-                                                  PRInt32 aSelectionType)
-{
-  nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
-
-  // get selection
-  nsCOMPtr selection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-
-  // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
-  PRBool cancel, handled;
-  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  if (NS_FAILED(res)) return res;
-  if (cancel) return NS_OK; // rules canceled the operation
-  if (!handled)
-  {
-    nsCOMPtr newNode;
-    nsAutoString tag; tag.AssignWithConversion("blockquote");
-    res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
-    if (NS_FAILED(res)) return res;
-    if (!newNode) return NS_ERROR_NULL_POINTER;
-
-    // Try to set type=cite.  Ignore it if this fails.
-    nsCOMPtr newElement (do_QueryInterface(newNode));
-    if (newElement)
-    {
-      nsAutoString type; type.AssignWithConversion("type");
-      nsAutoString cite; cite.AssignWithConversion("cite");
-      newElement->SetAttribute(type, cite);
-    }
-
-    // Set the selection to the underneath the node we just inserted:
-    res = selection->Collapse(newNode, 0);
-    if (NS_FAILED(res))
-    {
-#ifdef DEBUG_akkana
-      printf("Couldn't collapse");
-#endif
-      // XXX: error result:  should res be returned here?
-    }
-
-    res = Paste(aSelectionType);
-  }
-  return res;
-}
-
-//
-// Paste a plaintext quotation
-//
-NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(PRInt32 aSelectionType)
-{
-  // Get Clipboard Service
-  nsresult rv;
-  NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  // Create generic Transferable for getting the data
-  nsCOMPtr trans;
-  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
-                                          NS_GET_IID(nsITransferable), 
-                                          (void**) getter_AddRefs(trans));
-  if (NS_SUCCEEDED(rv) && trans)
-  {
-    // We only handle plaintext pastes here
-    trans->AddDataFlavor(kUnicodeMime);
-
-    // Get the Data from the clipboard
-    clipboard->GetData(trans, aSelectionType);
-
-    // Now we ask the transferable for the data
-    // it still owns the data, we just have a pointer to it.
-    // If it can't support a "text" output of the data the call will fail
-    nsCOMPtr genericDataObj;
-    PRUint32 len = 0;
-    char* flav = 0;
-    rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj),
-                                   &len);
-    if (NS_FAILED(rv))
-    {
-#ifdef DEBUG_akkana
-      printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv);
-#endif
-      return rv;
-    }
-#ifdef DEBUG_akkana
-    printf("Got flavor [%s]\n", flav);
-#endif
-    nsAutoString flavor; flavor.AssignWithConversion(flav);
-    nsAutoString stuffToPaste;
-    if (flavor.EqualsWithConversion(kUnicodeMime))
-    {
-      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
-      if (textDataObj && len > 0)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj->ToString ( &text );
-        stuffToPaste.Assign ( text, len / 2 );
-        nsAutoEditBatch beginBatching(this);
-        rv = InsertAsPlaintextQuotation(stuffToPaste, 0);
-        if (text)
-          nsMemory::Free(text);
-      }
-    }
-    nsCRT::free(flav);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText,
-                                              nsIDOMNode **aNodeInserted)
-{
-  if (mFlags & eEditorPlaintextMask)
-    return InsertAsPlaintextQuotation(aQuotedText, aNodeInserted);
-
-  nsAutoString citation;
-  nsAutoString charset;
-  return InsertAsCitedQuotation(aQuotedText, citation, PR_FALSE,
-                                charset, aNodeInserted);
-}
-
-// text insert.
-NS_IMETHODIMP
-nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText,
-                                         nsIDOMNode **aNodeInserted)
-{
-  // We have the text.  Cite it appropriately:
-  nsCOMPtr citer;
-  nsresult rv;
-  NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  char *citationType = 0;
-  rv = prefs->CopyCharPref("mail.compose.citationType", &citationType);
-                          
-  if (NS_SUCCEEDED(rv) && citationType[0])
-  {
-    if (!strncmp(citationType, "aol", 3))
-      citer = new nsAOLCiter;
-    else
-      citer = new nsInternetCiter;
-    PL_strfree(citationType);
-  }
-  else
-    citer = new nsInternetCiter;
-  
-  // Let the citer quote it for us:
-  nsString quotedStuff;
-  rv = citer->GetCiteString(aQuotedText, quotedStuff);
-  if (!NS_SUCCEEDED(rv))
-    return rv;
-
-  // It's best to put a blank line after the quoted text so that mails
-  // written without thinking won't be so ugly.
-  quotedStuff.Append(PRUnichar('\n'));
-
-  nsCOMPtr preNode;
-  // get selection
-  nsCOMPtr selection;
-  rv = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(rv)) return rv;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-  else
-  {
-    nsAutoEditBatch beginBatching(this);
-    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
-
-    // give rules a chance to handle or cancel
-    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
-    PRBool cancel, handled;
-    rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-    if (NS_FAILED(rv)) return rv;
-    if (cancel) return NS_OK; // rules canceled the operation
-    if (!handled)
-    {
-      // Wrap the inserted quote in a 
 so it won't be wrapped:
-      nsAutoString tag; tag.AssignWithConversion("pre");
-      rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
-      
-      // If this succeeded, then set selection inside the pre
-      // so the inserted text will end up there.
-      // If it failed, we don't care what the return value was,
-      // but we'll fall through and try to insert the text anyway.
-      if (NS_SUCCEEDED(rv) && preNode)
-      {
-        // Add an attribute on the pre node so we'll know it's a quotation.
-        // Do this after the insertion, so that 
-        nsCOMPtr preElement (do_QueryInterface(preNode));
-        if (preElement)
-        {
-          preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"),
-                                   NS_LITERAL_STRING("true"));
-          // set style to not have unwanted vertical margins
-          preElement->SetAttribute(NS_LITERAL_STRING("style"),
-                                   NS_LITERAL_STRING("margin: 0 0 0 0px;"));
-        }
-
-        // and set the selection inside it:
-        selection->Collapse(preNode, 0);
-      }
-
-      rv = InsertText(quotedStuff.GetUnicode());
-
-      if (aNodeInserted && NS_SUCCEEDED(rv))
-      {
-        *aNodeInserted = preNode;
-        NS_IF_ADDREF(*aNodeInserted);
-      }
-    }
-  }
-    
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(rv) && preNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(preNode, address_of(parent), &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
-                                     const nsString& aCitation,
-                                     PRBool aInsertHTML,
-                                     const nsString& aCharset,
-                                     nsIDOMNode **aNodeInserted)
-{
-  nsCOMPtr newNode;
-  nsresult res = NS_OK;
-
-  // get selection
-  nsCOMPtr selection;
-  res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-  else
-  {
-    nsAutoEditBatch beginBatching(this);
-    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
-
-    // give rules a chance to handle or cancel
-    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
-    PRBool cancel, handled;
-    res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-    if (NS_FAILED(res)) return res;
-    if (cancel) return NS_OK; // rules canceled the operation
-    if (!handled)
-    {
-      nsAutoString tag; tag.AssignWithConversion("blockquote");
-      res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
-      if (NS_FAILED(res)) return res;
-      if (!newNode) return NS_ERROR_NULL_POINTER;
-
-      // Try to set type=cite.  Ignore it if this fails.
-      nsCOMPtr newElement (do_QueryInterface(newNode));
-      if (newElement)
-      {
-        nsAutoString type; type.AssignWithConversion("type");
-        nsAutoString cite; cite.AssignWithConversion("cite");
-        newElement->SetAttribute(type, cite);
-
-        if (aCitation.Length() > 0)
-          newElement->SetAttribute(cite, aCitation);
-
-        // Set the selection inside the blockquote so aQuotedText will go there:
-        selection->Collapse(newNode, 0);
-      }
-
-      if (aInsertHTML)
-        res = InsertHTMLWithCharset(aQuotedText, aCharset);
-
-      else
-        res = InsertText(aQuotedText.GetUnicode());  // XXX ignore charset
-
-      if (aNodeInserted)
-      {
-        if (NS_SUCCEEDED(res))
-        {
-          *aNodeInserted = newNode;
-          NS_IF_ADDREF(*aNodeInserted);
-        }
-      }
-    }
-  }
-
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(res) && newNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(newNode, address_of(parent), &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-  return res;
-}
-
-// Shared between OutputToString and OutputToStream
-NS_IMETHODIMP
-nsHTMLEditor::GetAndInitDocEncoder(const nsAReadableString& aFormatType,
-                                   PRUint32 aFlags,
-                                   const nsAReadableString* aCharset,
-                                   nsIDocumentEncoder** encoder)
-{
-  nsCOMPtr presShell;
-  nsresult rv = GetPresShell(getter_AddRefs(presShell));
-  if (NS_FAILED(rv)) return rv;
-  if (!presShell) return NS_ERROR_FAILURE;
-
-  nsCAutoString formatType(NS_DOC_ENCODER_CONTRACTID_BASE);
-  formatType.AppendWithConversion(aFormatType);
-  nsCOMPtr docEncoder (do_CreateInstance(formatType, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr doc;
-  rv = presShell->GetDocument(getter_AddRefs(doc));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = docEncoder->Init(doc, aFormatType, aFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (aCharset && aCharset->Length() != 0
-      && !(aCharset->Equals(NS_LITERAL_STRING("null"))))
-    docEncoder->SetCharset(*aCharset);
-
-  PRInt32 wc;
-  (void) GetWrapWidth(&wc);
-  if (wc >= 0)
-    (void) docEncoder->SetWrapColumn(wc);
-
-  // Set the selection, if appropriate.
-  // We do this either if the OutputSelectionOnly flag is set,
-  // in which case we use our existing selection ...
-  if (aFlags & nsIDocumentEncoder::OutputSelectionOnly)
-  {
-    nsCOMPtr selection;
-    rv = GetSelection(getter_AddRefs(selection));
-    if (NS_SUCCEEDED(rv) && selection)
-      rv = docEncoder->SetSelection(selection);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  // ... or if the root element is not a body,
-  // in which case we set the selection to encompass the root.
-  else
-  {
-    nsCOMPtr rootElement;
-    GetRootElement(getter_AddRefs(rootElement));
-    NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
-    if (!nsHTMLEditUtils::IsBody(rootElement))
-    {
-      // XXX Why does this use range rather than selection collapse/extend?
-      nsCOMPtr range (do_CreateInstance(kCRangeCID, &rv));
-      if (NS_FAILED(rv)) return rv;
-      if (!range) return NS_ERROR_FAILURE;
-      nsCOMPtr selection (do_CreateInstance(kCDOMSelectionCID,
-                                                          &rv));
-      if (NS_FAILED(rv)) return rv;
-      if (!selection) return NS_ERROR_FAILURE;
-
-      // get the independent selection interface
-      nsCOMPtr indSel = do_QueryInterface(selection);
-      if (indSel)
-        indSel->SetPresShell(presShell);
-
-      nsCOMPtr content(do_QueryInterface(rootElement));
-      if (content)
-      {
-        range->SetStart(rootElement,0);
-        PRInt32 children;
-        if (NS_SUCCEEDED(content->ChildCount(children)))
-          range->SetEnd(rootElement,children);
-        // XXX else, should we return the error code?
-
-        if (NS_FAILED(selection->AddRange(range)))
-          return NS_ERROR_FAILURE;
-      }
-      rv = docEncoder->SetSelection(selection);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  }
-
-  NS_ADDREF(*encoder = docEncoder);
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLEditor::OutputToString(nsAWritableString& aOutputString,
-                             const nsAReadableString& aFormatType,
-                             PRUint32 aFlags)
-{
-  // XXX Why isn't this rules stuff also in OutputToStream?
-  PRBool cancel, handled;
-  nsString resultString;
-  nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText);
-  ruleInfo.outString = &resultString;
-  // XXX Struct should store a nsAReadable*
-  nsAutoString str(aFormatType);
-  ruleInfo.outputFormat = &str;
-  nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel, &handled);
-  if (cancel || NS_FAILED(rv)) { return rv; }
-  if (handled)
-  { // this case will get triggered by password fields
-    aOutputString.Assign(*(ruleInfo.outString));
-    return rv;
-  }
-
-  nsCOMPtr encoder;
-  rv = GetAndInitDocEncoder(aFormatType, aFlags, 0, getter_AddRefs(encoder));
-  if (NS_FAILED(rv))
-    return rv;
-  rv = encoder->EncodeToString(aOutputString);
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream,
-                             const nsAReadableString& aFormatType,
-                             const nsAReadableString* aCharset,
-                             PRUint32 aFlags)
-{
-  nsresult rv;
-
-  // special-case for empty document when requesting plain text,
-  // to account for the bogus text node.
-  // XXX Should there be a similar test in OutputToString?
-  if (aFormatType == NS_LITERAL_STRING("text/plain"))
-  {
-    PRBool docEmpty;
-    rv = GetDocumentIsEmpty(&docEmpty);
-    if (NS_FAILED(rv)) return rv;
-    
-    if (docEmpty)
-       return NS_OK;    // output nothing
-  }
-
-  nsCOMPtr encoder;
-  rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset,
-                            getter_AddRefs(encoder));
-
-  if (NS_FAILED(rv))
-    return rv;
-
-  return encoder->EncodeToStream(aOutputStream);
-}
 
 static nsresult SetSelectionAroundHeadChildren(nsCOMPtr aSelection, nsWeakPtr aDocWeak)
 {
@@ -6926,96 +3306,6 @@ nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode)
   return tbl;
 }
 
-NS_IMETHODIMP
-nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode)
-{
-  nsresult result=NS_ERROR_NOT_INITIALIZED;
-  nsCOMPtr selection;
-  result = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(result)) return result;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-
-  PRBool collapsed;
-  result = selection->GetIsCollapsed(&collapsed);
-  if (NS_SUCCEEDED(result) && !collapsed) 
-  {
-    result = DeleteSelection(nsIEditor::eNone);
-    if (NS_FAILED(result)) {
-      return result;
-    }
-    // get the new selection
-    result = GetSelection(getter_AddRefs(selection));
-    if (NS_FAILED(result)) {
-      return result;
-    }
-#ifdef NS_DEBUG
-    nsCOMPtrtestSelectedNode;
-    nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
-    // no selection is ok.
-    // if there is a selection, it must be collapsed
-    if (testSelectedNode)
-    {
-      PRBool testCollapsed;
-      debugResult = selection->GetIsCollapsed(&testCollapsed);
-      NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
-      NS_ASSERTION(testCollapsed, "selection not reset after deletion");
-    }
-#endif
-  }
-  // split the selected node
-  PRInt32 offsetOfSelectedNode;
-  result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
-  if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
-  {
-    nsCOMPtr selectedNode;
-    PRUint32 selectedNodeContentCount=0;
-    nsCOMPtrselectedParentNodeAsText;
-    selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
-
-    offsetOfNewNode = offsetOfSelectedNode;
-    
-    /* if the selection is a text node, split the text node if necesary
-       and compute where to put the new node
-    */
-    if (selectedParentNodeAsText) 
-    { 
-      PRInt32 indexOfTextNodeInParent;
-      selectedNode = do_QueryInterface(parentSelectedNode);
-      selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
-      selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
-      GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
-
-      if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
-      {
-        nsCOMPtr newSiblingNode;
-        result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
-        // now get the node's offset in it's parent, and insert the new tag there
-        if (NS_SUCCEEDED(result)) {
-          result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
-        }
-      }
-      else 
-      { // determine where to insert the new node
-        if (0==offsetOfSelectedNode) {
-          offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
-        }
-        else {                 // insert new node as last child
-          GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
-          offsetOfNewNode++;    // offsets are 0-based, and we need the index of the new node
-        }
-      }
-    }
-    // Here's where the new node was inserted
-  }
-#ifdef DEBUG
-  else {
-    printf("InsertLineBreak into an empty document is not yet supported\n");
-  }
-#endif
-  return result;
-}
-
-
 #ifdef XP_MAC
 #pragma mark -
 #endif
@@ -7211,272 +3501,6 @@ nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
 #pragma mark -
 #endif
 
-nsresult
-nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
-{
-  // Can only change font size by + or - 1
-  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
-    return NS_ERROR_ILLEGAL_VALUE;
-  
-  ForceCompositionEnd();
-
-  // Get the selection 
-  nsCOMPtrselection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-  if (!selection) return NS_ERROR_FAILURE;
-  nsCOMPtr selPriv(do_QueryInterface(selection));  
-  // Is the selection collapsed?
-  PRBool bCollapsed;
-  res = selection->GetIsCollapsed(&bCollapsed);
-  if (NS_FAILED(res)) return res;
-  
-  // if it's collapsed set typing state
-  if (bCollapsed)
-  {
-    nsCOMPtr atom;
-    if (aSizeChange==1) atom = nsIEditProperty::big;
-    else                atom = nsIEditProperty::small;
-    // manipulating text attributes on a collapsed selection only sets state for the next text insertion
-    return mTypeInState->SetProp(atom, nsAutoString(), nsAutoString());
-  }
-  
-  // wrap with txn batching, rules sniffing, and selection preservation code
-  nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext);
-  nsAutoSelectionReset selectionResetter(selection, this);
-  nsAutoTxnsConserveSelection dontSpazMySelection(this);
-
-  // get selection range enumerator
-  nsCOMPtr enumerator;
-  res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
-  if (NS_FAILED(res)) return res;
-  if (!enumerator)    return NS_ERROR_FAILURE;
-
-  // loop thru the ranges in the selection
-  enumerator->First(); 
-  nsCOMPtr currentItem;
-  while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
-  {
-    res = enumerator->CurrentItem(getter_AddRefs(currentItem));
-    if (NS_FAILED(res)) return res;
-    if (!currentItem)   return NS_ERROR_FAILURE;
-    
-    nsCOMPtr range( do_QueryInterface(currentItem) );
-
-    // adjust range to include any ancestors who's children are entirely selected
-    res = PromoteInlineRange(range);
-    if (NS_FAILED(res)) return res;
-    
-    // check for easy case: both range endpoints in same text node
-    nsCOMPtr startNode, endNode;
-    res = range->GetStartContainer(getter_AddRefs(startNode));
-    if (NS_FAILED(res)) return res;
-    res = range->GetEndContainer(getter_AddRefs(endNode));
-    if (NS_FAILED(res)) return res;
-    if ((startNode == endNode) && IsTextNode(startNode))
-    {
-      PRInt32 startOffset, endOffset;
-      range->GetStartOffset(&startOffset);
-      range->GetEndOffset(&endOffset);
-      nsCOMPtr nodeAsText = do_QueryInterface(startNode);
-      res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
-      if (NS_FAILED(res)) return res;
-    }
-    else
-    {
-      // not the easy case.  range not contained in single text node. 
-      // there are up to three phases here.  There are all the nodes
-      // reported by the subtree iterator to be processed.  And there
-      // are potentially a starting textnode and an ending textnode
-      // which are only partially contained by the range.
-      
-      // lets handle the nodes reported by the iterator.  These nodes
-      // are entirely contained in the selection range.  We build up
-      // a list of them (since doing operations on the document during
-      // iteration would perturb the iterator).
-
-      nsCOMPtr iter;
-      res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull,
-                                                NS_GET_IID(nsIContentIterator), 
-                                                getter_AddRefs(iter));
-      if (NS_FAILED(res)) return res;
-      if (!iter)          return NS_ERROR_FAILURE;
-
-      nsCOMPtr arrayOfNodes;
-      nsCOMPtr content;
-      nsCOMPtr node;
-      nsCOMPtr isupports;
-      
-      // make a array
-      res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
-      if (NS_FAILED(res)) return res;
-      
-      // iterate range and build up array
-      res = iter->Init(range);
-      if (NS_SUCCEEDED(res))
-      {
-        while (NS_ENUMERATOR_FALSE == iter->IsDone())
-        {
-          res = iter->CurrentNode(getter_AddRefs(content));
-          if (NS_FAILED(res)) return res;
-          node = do_QueryInterface(content);
-          if (!node) return NS_ERROR_FAILURE;
-          if (IsEditable(node))
-          { 
-            isupports = do_QueryInterface(node);
-            arrayOfNodes->AppendElement(isupports);
-          }
-          iter->Next();
-        }
-        
-        // now that we have the list, do the font size change on each node
-        PRUint32 listCount;
-        PRUint32 j;
-        arrayOfNodes->Count(&listCount);
-        for (j = 0; j < listCount; j++)
-        {
-          isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
-          node = do_QueryInterface(isupports);
-          res = RelativeFontChangeOnNode(aSizeChange, node);
-          if (NS_FAILED(res)) return res;
-          arrayOfNodes->RemoveElementAt(0);
-        }
-      }
-      // now check the start and end parents of the range to see if they need to 
-      // be seperately handled (they do if they are text nodes, due to how the
-      // subtree iterator works - it will not have reported them).
-      if (IsTextNode(startNode) && IsEditable(startNode))
-      {
-        nsCOMPtr nodeAsText = do_QueryInterface(startNode);
-        PRInt32 startOffset;
-        PRUint32 textLen;
-        range->GetStartOffset(&startOffset);
-        nodeAsText->GetLength(&textLen);
-        res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
-        if (NS_FAILED(res)) return res;
-      }
-      if (IsTextNode(endNode) && IsEditable(endNode))
-      {
-        nsCOMPtr nodeAsText = do_QueryInterface(endNode);
-        PRInt32 endOffset;
-        range->GetEndOffset(&endOffset);
-        res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
-        if (NS_FAILED(res)) return res;
-      }
-    }
-    enumerator->Next();
-  }
-  
-  return res;  
-}
-
-nsresult
-nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, 
-                                            nsIDOMCharacterData *aTextNode, 
-                                            PRInt32 aStartOffset,
-                                            PRInt32 aEndOffset)
-{
-  // Can only change font size by + or - 1
-  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
-    return NS_ERROR_ILLEGAL_VALUE;
-  if (!aTextNode) return NS_ERROR_NULL_POINTER;
-  
-  // dont need to do anything if no characters actually selected
-  if (aStartOffset == aEndOffset) return NS_OK;
-  
-  nsresult res = NS_OK;
-  nsCOMPtr tmp, node = do_QueryInterface(aTextNode);
-  
-  // do we need to split the text node?
-  PRUint32 textLen;
-  aTextNode->GetLength(&textLen);
-  
-  // -1 is a magic value meaning to the end of node
-  if (aEndOffset == -1) aEndOffset = textLen;
-  
-  if ( (PRUint32)aEndOffset != textLen )
-  {
-    // we need to split off back of text node
-    res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
-    if (NS_FAILED(res)) return res;
-    node = tmp;  // remember left node
-  }
-  if ( aStartOffset )
-  {
-    // we need to split off front of text node
-    res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
-    if (NS_FAILED(res)) return res;
-  }
-  
-  // reparent the node inside font node with appropriate relative size
-  res = InsertContainerAbove(node, address_of(tmp), NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small"));
-  return res;
-}
-
-
-nsresult
-nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, 
-                                        nsIDOMNode *aNode)
-{
-  // Can only change font size by + or - 1
-  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
-    return NS_ERROR_ILLEGAL_VALUE;
-  if (!aNode) return NS_ERROR_NULL_POINTER;
-
-  nsresult res = NS_OK;
-  nsCOMPtr tmp;
-  nsAutoString tag;
-  if (aSizeChange == 1) tag.AssignWithConversion("big");
-  else tag.AssignWithConversion("small");
-  
-  // is this node a text node?
-  if (IsTextNode(aNode))
-  {
-    res = InsertContainerAbove(aNode, address_of(tmp), tag);
-    return res;
-  }
-  // is it the opposite of what we want?  
-  if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) || 
-       ((aSizeChange == -1) &&  nsHTMLEditUtils::IsBig(aNode)) )
-  {
-    // in that case, just remove this node and pull up the children
-    res = RemoveContainer(aNode);
-    return res;
-  }
-  // can it be put inside a "big" or "small"?
-  if (TagCanContain(tag, aNode))
-  {
-    // ok, chuck it in.
-    res = InsertContainerAbove(aNode, address_of(tmp), tag);
-    return res;
-  }
-  // none of the above?  then cycle through the children.
-  // MOOSE: we should group the children together if possible
-  // into a single "big" or "small".  For the moment they are
-  // each getting their own.  
-  nsCOMPtr childNodes;
-  res = aNode->GetChildNodes(getter_AddRefs(childNodes));
-  if (NS_FAILED(res)) return res;
-  if (childNodes)
-  {
-    PRInt32 j;
-    PRUint32 childCount;
-    childNodes->GetLength(&childCount);
-    for (j=0 ; j < (PRInt32)childCount; j++)
-    {
-      nsCOMPtr childNode;
-      res = childNodes->Item(j, getter_AddRefs(childNode));
-      if ((NS_SUCCEEDED(res)) && (childNode))
-      {
-        res = RelativeFontChangeOnNode(aSizeChange, childNode);
-        if (NS_FAILED(res)) return res;
-      }
-    }
-  }
-  return res;
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
 //                   one within the parent
diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h
index 30a0b1ca42b..212c3d5997f 100644
--- a/mozilla/editor/base/nsHTMLEditor.h
+++ b/mozilla/editor/base/nsHTMLEditor.h
@@ -25,7 +25,8 @@
 
 #include "nsCOMPtr.h"
 
-#include "nsIPlaintextEditor.h"
+#include "nsPlaintextEditor.h"
+#include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsITableEditor.h"
 #include "nsIEditorMailSupport.h"
@@ -51,8 +52,7 @@ class nsIDocumentEncoder;
  * The HTML editor implementation.
* Use to edit HTML document represented as a DOM tree. */ -class nsHTMLEditor : public nsEditor, - public nsIPlaintextEditor, +class nsHTMLEditor : public nsPlaintextEditor, public nsIHTMLEditor, public nsIEditorMailSupport, public nsITableEditor, @@ -74,7 +74,10 @@ public: kOpRemoveList = 3006, kOpMakeDefListItem = 3007, kOpInsertElement = 3008, - kOpInsertQuotation = 3009 + kOpInsertQuotation = 3009, + kOpSetTextProperty = 3010, + kOpRemoveTextProperty = 3011, + kOpHTMLPaste = 3012 }; @@ -89,8 +92,9 @@ public: nsHTMLEditor(); virtual ~nsHTMLEditor(); - /* ------------ nsIPlaintextEditor methods -------------- */ - NS_DECL_NSIPLAINTEXTEDITOR + /* ------------ nsPlaintextEditor overrides -------------- */ + NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent); + NS_IMETHODIMP CollapseSelectionToStart(); /* ------------ nsIHTMLEditor methods -------------- */ NS_IMETHOD SetInlineProperty(nsIAtom *aProperty, @@ -118,7 +122,6 @@ public: NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); - NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); NS_IMETHOD SelectElement(nsIDOMElement* aElement); NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); @@ -240,12 +243,6 @@ public: /** prepare the editor for use */ NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags); - NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty); - - NS_IMETHOD DeleteSelection(EDirection aAction); - - NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet); - /** we override this here to install event listeners */ NS_IMETHOD PostCreate(); @@ -266,15 +263,6 @@ public: NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent); NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent); - NS_IMETHOD OutputToString(nsAWritableString& aOutputString, - const nsAReadableString& aFormatType, - PRUint32 aFlags); - - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsAReadableString& aFormatType, - const nsAReadableString* aCharsetOverride, - PRUint32 aFlags); - NS_IMETHOD GetHeadContentsAsHTML(nsString& aOutputString); NS_IMETHOD ReplaceHeadContentsWithHTML(const nsString &aSourceToInsert); @@ -307,7 +295,6 @@ public: PRInt32 aOffset, PRBool aNoEmptyNodes); - NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); /** returns the absolute position of the end points of aSelection * in the document as a text stream. @@ -356,8 +343,6 @@ protected: */ NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject); - NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode); - /* StyleSheet load callback */ static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData); @@ -376,11 +361,11 @@ protected: // key event helpers NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled); NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, - nsCOMPtr *outBRNode, EDirection aSelect = eNone); + nsCOMPtr *outBRNode, nsIEditor::EDirection aSelect = nsIEditor::eNone); NS_IMETHOD CreateBRImpl(nsCOMPtr *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr *outBRNode, - EDirection aSelect); + nsIEditor::EDirection aSelect); NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); // Table Editing (implemented in nsTableEditor.cpp) @@ -491,12 +476,6 @@ protected: PRInt32 aEndOffset, nsISelection *aSelection); - // Helpers for output routines - NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType, - PRUint32 aFlags, - const nsAReadableString* aCharset, - nsIDocumentEncoder** encoder); - // Methods for handling plaintext quotations NS_IMETHOD PasteAsPlaintextQuotation(PRInt32 aSelectionType); NS_IMETHOD InsertAsPlaintextQuotation(const nsString& aQuotedText, @@ -610,38 +589,24 @@ protected: // Data members protected: - TypeInState* mTypeInState; - nsCOMPtr mRules; - nsCOMPtr mKeyListenerP; - nsCOMPtr mMouseListenerP; - nsCOMPtr mTextListenerP; - nsCOMPtr mCompositionListenerP; - nsCOMPtr mDragListenerP; - nsCOMPtr mFocusListenerP; - PRBool mIsComposing; - - // Used by nsIPlaintextEditor but not html editors -- factor me! - PRInt32 mMaxTextLength; + TypeInState* mTypeInState; - nsCOMPtr mBoldAtom; - nsCOMPtr mItalicAtom; - nsCOMPtr mUnderlineAtom; - nsCOMPtr mFontAtom; - nsCOMPtr mLinkAtom; + nsCOMPtr mBoldAtom; + nsCOMPtr mItalicAtom; + nsCOMPtr mUnderlineAtom; + nsCOMPtr mFontAtom; + nsCOMPtr mLinkAtom; nsCOMPtr mCachedNode; - PRBool mCachedBoldStyle; - PRBool mCachedItalicStyle; - PRBool mCachedUnderlineStyle; + PRBool mCachedBoldStyle; + PRBool mCachedItalicStyle; + PRBool mCachedUnderlineStyle; nsString mCachedFontName; // Used by GetFirstSelectedCell and GetNextSelectedCell PRInt32 mSelectedCellIndex; public: - static nsIAtom *gTypingTxnName; - static nsIAtom *gIMETxnName; - static nsIAtom *gDeleteTxnName; // friends friend class nsHTMLEditRules; diff --git a/mozilla/editor/base/nsHTMLEditorStyle.cpp b/mozilla/editor/base/nsHTMLEditorStyle.cpp new file mode 100644 index 00000000000..0b50ebb6a1a --- /dev/null +++ b/mozilla/editor/base/nsHTMLEditorStyle.cpp @@ -0,0 +1,1486 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ +#include "nsICaret.h" + +#include "nsHTMLEditor.h" +#include "nsHTMLEditRules.h" +#include "nsHTMLEditUtils.h" + +#include "nsEditorEventListeners.h" + +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" + +// Misc +#include "nsEditorUtils.h" + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + + +NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aProperty) { return NS_ERROR_NULL_POINTER; } + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + ForceCompositionEnd(); + + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selPriv(do_QueryInterface(selection)); + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return mTypeInState->SetProp(aProperty, *aAttribute, *aValue); + } + + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + nsAutoTxnsConserveSelection dontSpazMySelection(this); + + PRBool cancel, handled; + nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (!cancel && !handled) + { + // get selection range enumerator + nsCOMPtr enumerator; + res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + + nsCOMPtr range( do_QueryInterface(currentItem) ); + + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + else + { + // not the easy case. range not contained in single text node. + // there are up to three phases here. There are all the nodes + // reported by the subtree iterator to be processed. And there + // are potentially a starting textnode and an ending textnode + // which are only partially contained by the range. + + // lets handle the nodes reported by the iterator. These nodes + // are entirely contained in the selection range. We build up + // a list of them (since doing operations on the document during + // iteration would perturb the iterator). + + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + res = iter->Init(range); + // init returns an error if no nodes in range. + // this can easily happen with the subtree + // iterator if the selection doesn't contain + // any *whole* nodes. + if (NS_SUCCEEDED(res)) + { + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; + } + } + // first check the start parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(startNode) && IsEditable(startNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + PRInt32 startOffset; + PRUint32 textLen; + range->GetStartOffset(&startOffset); + nodeAsText->GetLength(&textLen); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + + // last check the end parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(endNode) && IsEditable(endNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(endNode); + PRInt32 endOffset; + range->GetEndOffset(&endOffset); + res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + } + enumerator->Next(); + } + } + if (!cancel) + { + // post-process + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + return res; +} + + + +nsresult +nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aTextNode) return NS_ERROR_NULL_POINTER; + + // dont need to do anything if no characters actually selected + if (aStartOffset == aEndOffset) return NS_OK; + + nsresult res = NS_OK; + nsCOMPtr tmp, node = do_QueryInterface(aTextNode); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // do we need to split the text node? + PRUint32 textLen; + aTextNode->GetLength(&textLen); + + if ( (PRUint32)aEndOffset != textLen ) + { + // we need to split off back of text node + res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + node = tmp; // remember left node + } + if ( aStartOffset ) + { + // we need to split off front of text node + res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + } + + // reparent the node inside inline node with appropriate {attribute,value} + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + return res; +} + + +nsresult +nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsCOMPtr tmp; + nsAutoString tag; + aProperty->ToString(tag); + tag.ToLowerCase(); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // is it already the right kind of node, but with wrong attribute? + if (NodeIsType(aNode, aProperty)) + { + // just set the attribute on it. + // but first remove any contrary style in it's children. + res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE); + if (NS_FAILED(res)) return res; + nsCOMPtr elem = do_QueryInterface(aNode); + return SetAttribute(elem, *aAttribute, *aValue); + } + + // can it be put inside inline node? + if (TagCanContain(tag, aNode)) + { + nsCOMPtr priorNode, nextNode; + // is either of it's neighbors the right kind of node? + GetPriorHTMLSibling(aNode, address_of(priorNode)); + GetNextHTMLSibling(aNode, address_of(nextNode)); + if (priorNode && NodeIsType(priorNode, aProperty) && + HasAttrVal(priorNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, priorNode, -1); + } + else if (nextNode && NodeIsType(nextNode, aProperty) && + HasAttrVal(nextNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, nextNode, 0); + } + else + { + // ok, chuck it in it's very own container + res = InsertContainerAbove(aNode, address_of(tmp), tag, aAttribute, aValue); + } + if (NS_FAILED(res)) return res; + return RemoveStyleInside(aNode, aProperty, aAttribute); + } + // none of the above? then cycle through the children. + nsCOMPtr childNodes; + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + PRInt32 j; + PRUint32 childCount; + childNodes->GetLength(&childCount); + if (childCount) + { + nsCOMPtr arrayOfNodes; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // populate the list + for (j=0 ; j < (PRInt32)childCount; j++) + { + nsCOMPtr childNode; + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode)) + { + isupports = do_QueryInterface(childNode); + arrayOfNodes->AppendElement(isupports); + } + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + arrayOfNodes->Count(&listCount); + for (j = 0; j < (PRInt32)listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + } + return res; +} + + +nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, + nsIAtom *aProperty, + const nsString *aAttribute) +{ + if (!inRange) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, origStartNode; + PRInt32 startOffset, endOffset, origStartOffset; + + res = inRange->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + origStartNode = startNode; + origStartOffset = startOffset; + PRBool sameNode = (startNode==endNode); + + // split any matching style nodes above the start of range + res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + if (sameNode && (startNode != origStartNode)) + { + // our startNode got split. This changes the offset of the end of our range. + endOffset -= origStartOffset; + } + + // second verse, same as the first... + res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // reset the range + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr *aNode, + PRInt32 *aOffset, + nsIAtom *aProperty, // null here means we split all properties + const nsString *aAttribute, + nsCOMPtr *outLeftNode, + nsCOMPtr *outRightNode) +{ + if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER; + if (outLeftNode) *outLeftNode = nsnull; + if (outRightNode) *outRightNode = nsnull; + // split any matching style nodes above the node/offset + nsCOMPtr parent, tmp = *aNode; + PRInt32 offset; + while (tmp && !nsHTMLEditUtils::IsBody(tmp)) + { + if ( (aProperty && NodeIsType(tmp, aProperty)) || // node is the correct inline prop + (aProperty == nsIEditProperty::href && nsHTMLEditUtils::IsLink(tmp)) || // node is href - test if really GetParentNode(getter_AddRefs(*aNode)); + *aOffset = offset; + } + tmp->GetParentNode(getter_AddRefs(parent)); + tmp = parent; + } + return NS_OK; +} + +PRBool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode) +{ + if (!aNode) return PR_FALSE; + if (!IsContainer(aNode)) return PR_FALSE; + if (!IsEditable(aNode)) return PR_FALSE; + if (!IsInlineNode(aNode)) return PR_FALSE; + if (NodeIsType(aNode, nsIEditProperty::a)) return PR_FALSE; + return PR_TRUE; +} + +nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, + nsIAtom *aProperty, // null here means remove all properties + const nsString *aAttribute, + PRBool aChildrenOnly) +{ + if (!aNode) return NS_ERROR_NULL_POINTER; + if (IsTextNode(aNode)) return NS_OK; + nsresult res = NS_OK; + + // first process the children + nsCOMPtr child, tmp; + aNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + // cache next sibling since we might remove child + child->GetNextSibling(getter_AddRefs(tmp)); + res = RemoveStyleInside(child, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + child = tmp; + } + + // then process the node itself + if ( !aChildrenOnly && + ((aProperty && NodeIsType(aNode, aProperty)) || // node is prop we asked for + (aProperty == nsIEditProperty::href && nsHTMLEditUtils::IsLink(aNode))) || // but check for link (IsEmpty()) + { + res = RemoveContainer(aNode); + } + // otherwise we just want to eliminate the attribute + else + { + if (HasAttr(aNode, aAttribute)) + { + // if this matching attribute is the ONLY one on the node, + // then remove the whole node. Otherwise just nix the attribute. + if (IsOnlyAttribute(aNode, aAttribute)) + { + res = RemoveContainer(aNode); + } + else + { + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return NS_ERROR_NULL_POINTER; + res = RemoveAttribute(elem, *aAttribute); + } + } + } + } + return res; +} + +PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode || !aAttribute) return PR_FALSE; // ooops + nsCOMPtr content = do_QueryInterface(aNode); + if (!content) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID; + nsCOMPtr attrName, prefix; + content->GetAttributeCount(attrCount); + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), + *getter_AddRefs(prefix)); + nsAutoString attrString, tmp; + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's the attribute we know about, keep looking + if (attrString.EqualsIgnoreCase(*aAttribute)) continue; + // if it's a special _moz... attribute, keep looking + attrString.Left(tmp,4); + if (tmp.EqualsWithConversion("_moz")) continue; + // otherwise, it's another attribute, so return false + return PR_FALSE; + } + // if we made it through all of them without finding a real attribute + // other than aAttribute, then return PR_TRUE + return PR_TRUE; +} + +PRBool +nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1, + nsIDOMNode *aNode2) +{ + if (!aNode1 || !aNode2) return PR_FALSE; // ooops + nsCOMPtr content1 = do_QueryInterface(aNode1); + if (!content1) return PR_FALSE; // ooops + nsCOMPtr content2 = do_QueryInterface(aNode2); + if (!content2) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0; + nsCOMPtr attrName, prefix; + nsresult res, res2; + content1->GetAttributeCount(attrCount); + nsAutoString attrString, tmp, attrVal1, attrVal2; + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), + *getter_AddRefs(prefix)); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp.EqualsWithConversion("_moz")) continue; + // otherwise, it's another attribute, so count it + realCount1++; + // and compare it to element2's attributes + res = content1->GetAttribute(nameSpaceID, attrName, attrVal1); + res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2); + if (res != res2) return PR_FALSE; + if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE; + } + + content2->GetAttributeCount(attrCount); + for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), + *getter_AddRefs(prefix)); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp.EqualsWithConversion("_moz")) continue; + // otherwise, it's another attribute, so count it + realCount2++; + } + + if (realCount1 != realCount2) return PR_FALSE; + // otherwise, attribute counts match, and we already compared them + // when going through the first list, so we're done. + return PR_TRUE; +} + +PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + return PR_TRUE; +} + + +PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + + // check if attribute has a value + PRBool isSet; + attNode->GetSpecified(&isSet); + // if no value, and that's what we wanted, then return true + if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; + + // get attribute value + nsAutoString attrVal; + attNode->GetValue(attrVal); + + // do values match? + if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE; + return PR_FALSE; +} + + +nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange) +{ + if (!inRange) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, parent; + PRInt32 startOffset, endOffset; + + res = inRange->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + while ( startNode && + !nsHTMLEditUtils::IsBody(startNode) && + IsAtFrontOfNode(startNode, startOffset) ) + { + res = GetNodeLocation(startNode, address_of(parent), &startOffset); + if (NS_FAILED(res)) return res; + startNode = parent; + } + if (!startNode) return NS_ERROR_NULL_POINTER; + + while ( endNode && + !nsHTMLEditUtils::IsBody(endNode) && + IsAtEndOfNode(endNode, endOffset) ) + { + res = GetNodeLocation(endNode, address_of(parent), &endOffset); + if (NS_FAILED(res)) return res; + endNode = parent; + endOffset++; // we are AFTER this node + } + if (!endNode) return NS_ERROR_NULL_POINTER; + + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + if (!aOffset) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr firstNode; + GetFirstEditableChild(aNode, address_of(firstNode)); + if (!firstNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(firstNode, aNode, offset); + if (offset < aOffset) return PR_FALSE; + return PR_TRUE; + } +} + +PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + PRUint32 len; + GetLengthOfDOMNode(aNode, len); + if (aOffset == (PRInt32)len) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr lastNode; + GetLastEditableChild(aNode, address_of(lastNode)); + if (!lastNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(lastNode, aNode, offset); + if (offset < aOffset) return PR_TRUE; + return PR_FALSE; + } +} + +NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll) +{ + return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull); +} + +NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll, + nsString *outValue) +{ + if (!aProperty) + return NS_ERROR_NULL_POINTER; +/* + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); } + nsCRT::free(propCString); + } +*/ + nsresult result; + aAny=PR_FALSE; + aAll=PR_TRUE; + aFirst=PR_FALSE; + PRBool first=PR_TRUE; + nsCOMPtrselection; + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selPriv(do_QueryInterface(selection)); + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + nsCOMPtr collapsedNode; + nsCOMPtr enumerator; + result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(result)) return result; + if (!enumerator) return NS_ERROR_NULL_POINTER; + + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + // XXX: should be a while loop, to get each separate range + // XXX: ERROR_HANDLING can currentItem be null? + if ((NS_SUCCEEDED(result)) && currentItem) + { + PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag + nsCOMPtr range(do_QueryInterface(currentItem)); + + if (isCollapsed) + { + // efficiency hack. we cache prior results for being collapsed in a given text node. + // this speeds up typing. Note that other parts of the editor code have to clear out + // this cache after certain actions. + range->GetStartContainer(getter_AddRefs(collapsedNode)); + if (!collapsedNode) return NS_ERROR_FAILURE; + // refresh the cache if we need to + if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode); + // cache now current, use it! But override it with typeInState results if any... + PRBool isSet, theSetting; + if (aAttribute) + mTypeInState->GetTypingState(isSet, theSetting, aProperty, *aAttribute, outValue); + else + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + return NS_OK; + } + /* + if (aProperty == mBoldAtom.get()) + { + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + } + else + { + aFirst = aAny = aAll = mCachedBoldStyle; + } + return NS_OK; + } + else if (aProperty == mItalicAtom.get()) + { + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + } + else + { + aFirst = aAny = aAll = mCachedItalicStyle; + } + return NS_OK; + } + else if (aProperty == mUnderlineAtom.get()) + { + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + } + else + { + aFirst = aAny = aAll = mCachedUnderlineStyle; + } + return NS_OK; + } */ + } + + // either non-collapsed selection or no cached value: do it the hard way + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(result)) return result; + if (!iter) return NS_ERROR_NULL_POINTER; + + iter->Init(range); + nsCOMPtr content; + nsAutoString firstValue, theValue; + iter->CurrentNode(getter_AddRefs(content)); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + //if (gNoisy) { printf(" checking node %p\n", content.get()); } + nsCOMPtrtext; + text = do_QueryInterface(content); + PRBool skipNode = PR_FALSE; + if (text) + { + if (!isCollapsed && first && firstNodeInRange) + { + firstNodeInRange = PR_FALSE; + PRInt32 startOffset; + range->GetStartOffset(&startOffset); + PRUint32 count; + text->GetLength(&count); + if (startOffset==(PRInt32)count) + { + //if (gNoisy) { printf(" skipping node %p\n", content.get()); } + skipNode = PR_TRUE; + } + } + } + else + { // handle non-text leaf nodes here + PRBool canContainChildren; + content->CanContainChildren(canContainChildren); + if (canContainChildren) + { + //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } + skipNode = PR_TRUE; + } + else { + //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } + } + } + if (!skipNode) + { + nsCOMPtrnode; + node = do_QueryInterface(content); + if (node) + { + PRBool isSet; + nsCOMPtrresultNode; + if (first) + { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue); + aFirst = isSet; + first = PR_FALSE; + if (outValue) *outValue = firstValue; + } + else + { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue); + if (firstValue != theValue) + aAll = PR_FALSE; + } + + if (isSet) { + aAny = PR_TRUE; + } + else { + aAll = PR_FALSE; + } + } + } + result = iter->Next(); + if (NS_FAILED(result)) + break; + iter->CurrentNode(getter_AddRefs(content)); + } + } + if (!aAny) + { // make sure that if none of the selection is set, we don't report all is set + aAll = PR_FALSE; + } + //if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); } + return result; +} + + +NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties() +{ + return RemoveInlinePropertyImpl(nsnull, nsnull); +} + +NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) +{ + return RemoveInlinePropertyImpl(aProperty, aAttribute); +} + +nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsString *aAttribute) +{ + if (!mRules) return NS_ERROR_NOT_INITIALIZED; + ForceCompositionEnd(); + + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selPriv(do_QueryInterface(selection)); + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + + // For links, aProperty uses "href", use "a" instead + if (aProperty == nsIEditProperty::href) + aProperty = nsIEditProperty::a; + + if (aProperty) return mTypeInState->ClearProp(aProperty, *aAttribute); + else return mTypeInState->ClearAllProps(); + } + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + nsAutoTxnsConserveSelection dontSpazMySelection(this); + + PRBool cancel, handled; + nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (!cancel && !handled) + { + // get selection range enumerator + nsCOMPtr enumerator; + res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + + nsCOMPtr range( do_QueryInterface(currentItem) ); + + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // remove this style from ancestors of our range endpoints, + // splitting them as appropriate + res = SplitStyleAboveRange(range, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + // we're done with this range! + } + else + { + // not the easy case. range not contained in single text node. + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + iter->Init(range); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; + } + + // loop through the list, remove the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = RemoveStyleInside(node, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + enumerator->Next(); + } + } + if (!cancel) + { + // post-process + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + return res; +} + +NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize() +{ + return RelativeFontChange(1); +} + +NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize() +{ + return RelativeFontChange(-1); +} + +nsresult +nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) +{ + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + + ForceCompositionEnd(); + + // Get the selection + nsCOMPtrselection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + nsCOMPtr selPriv(do_QueryInterface(selection)); + // Is the selection collapsed? + PRBool bCollapsed; + res = selection->GetIsCollapsed(&bCollapsed); + if (NS_FAILED(res)) return res; + + // if it's collapsed set typing state + if (bCollapsed) + { + nsCOMPtr atom; + if (aSizeChange==1) atom = nsIEditProperty::big; + else atom = nsIEditProperty::small; + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return mTypeInState->SetProp(atom, nsAutoString(), nsAutoString()); + } + + // wrap with txn batching, rules sniffing, and selection preservation code + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + nsAutoTxnsConserveSelection dontSpazMySelection(this); + + // get selection range enumerator + nsCOMPtr enumerator; + res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + + nsCOMPtr range( do_QueryInterface(currentItem) ); + + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset); + if (NS_FAILED(res)) return res; + } + else + { + // not the easy case. range not contained in single text node. + // there are up to three phases here. There are all the nodes + // reported by the subtree iterator to be processed. And there + // are potentially a starting textnode and an ending textnode + // which are only partially contained by the range. + + // lets handle the nodes reported by the iterator. These nodes + // are entirely contained in the selection range. We build up + // a list of them (since doing operations on the document during + // iteration would perturb the iterator). + + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + res = iter->Init(range); + if (NS_SUCCEEDED(res)) + { + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + iter->Next(); + } + + // now that we have the list, do the font size change on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = RelativeFontChangeOnNode(aSizeChange, node); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + // now check the start and end parents of the range to see if they need to + // be seperately handled (they do if they are text nodes, due to how the + // subtree iterator works - it will not have reported them). + if (IsTextNode(startNode) && IsEditable(startNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + PRInt32 startOffset; + PRUint32 textLen; + range->GetStartOffset(&startOffset); + nodeAsText->GetLength(&textLen); + res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen); + if (NS_FAILED(res)) return res; + } + if (IsTextNode(endNode) && IsEditable(endNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(endNode); + PRInt32 endOffset; + range->GetEndOffset(&endOffset); + res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset); + if (NS_FAILED(res)) return res; + } + } + enumerator->Next(); + } + + return res; +} + +nsresult +nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, + nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset) +{ + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + if (!aTextNode) return NS_ERROR_NULL_POINTER; + + // dont need to do anything if no characters actually selected + if (aStartOffset == aEndOffset) return NS_OK; + + nsresult res = NS_OK; + nsCOMPtr tmp, node = do_QueryInterface(aTextNode); + + // do we need to split the text node? + PRUint32 textLen; + aTextNode->GetLength(&textLen); + + // -1 is a magic value meaning to the end of node + if (aEndOffset == -1) aEndOffset = textLen; + + if ( (PRUint32)aEndOffset != textLen ) + { + // we need to split off back of text node + res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + node = tmp; // remember left node + } + if ( aStartOffset ) + { + // we need to split off front of text node + res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + } + + // reparent the node inside font node with appropriate relative size + res = InsertContainerAbove(node, address_of(tmp), NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small")); + return res; +} + + +nsresult +nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, + nsIDOMNode *aNode) +{ + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + if (!aNode) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsCOMPtr tmp; + nsAutoString tag; + if (aSizeChange == 1) tag.AssignWithConversion("big"); + else tag.AssignWithConversion("small"); + + // is this node a text node? + if (IsTextNode(aNode)) + { + res = InsertContainerAbove(aNode, address_of(tmp), tag); + return res; + } + // is it the opposite of what we want? + if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) || + ((aSizeChange == -1) && nsHTMLEditUtils::IsBig(aNode)) ) + { + // in that case, just remove this node and pull up the children + res = RemoveContainer(aNode); + return res; + } + // can it be put inside a "big" or "small"? + if (TagCanContain(tag, aNode)) + { + // ok, chuck it in. + res = InsertContainerAbove(aNode, address_of(tmp), tag); + return res; + } + // none of the above? then cycle through the children. + // MOOSE: we should group the children together if possible + // into a single "big" or "small". For the moment they are + // each getting their own. + nsCOMPtr childNodes; + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + PRInt32 j; + PRUint32 childCount; + childNodes->GetLength(&childCount); + for (j=0 ; j < (PRInt32)childCount; j++) + { + nsCOMPtr childNode; + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = RelativeFontChangeOnNode(aSizeChange, childNode); + if (NS_FAILED(res)) return res; + } + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace) +{ + aMixed = PR_TRUE; + outFace.AssignWithConversion(""); + + nsresult res; + nsAutoString faceStr; faceStr.AssignWithConversion("face"); + PRBool first, any, all; + + res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + return res; + } + + res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + nsIEditProperty::tt->ToString(outFace); + } + + if (!any) + { + // there was no font face attrs of any kind. We are in normal font. + outFace.AssignWithConversion(""); + aMixed = PR_FALSE; + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetFontColorState(PRBool &aMixed, nsString &aOutColor) +{ + aMixed = PR_TRUE; + aOutColor.AssignWithConversion(""); + + nsresult res; + nsAutoString colorStr; colorStr.AssignWithConversion("color"); + PRBool first, any, all; + + res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &colorStr, nsnull, first, any, all, &aOutColor); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + return res; + } + + if (!any) + { + // there was no font color attrs of any kind.. + aOutColor.AssignWithConversion(""); + aMixed = PR_FALSE; + } + return res; +} + diff --git a/mozilla/editor/base/nsIHTMLEditRules.h b/mozilla/editor/base/nsIHTMLEditRules.h index f01abd3b109..4289b52fbfe 100644 --- a/mozilla/editor/base/nsIHTMLEditRules.h +++ b/mozilla/editor/base/nsIHTMLEditRules.h @@ -28,6 +28,8 @@ 0xa6cf9121, 0x15b3, 0x11d2, \ {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} } +#include "nsIHTMLEditor.h" + class nsIHTMLEditRules : public nsISupports { public: diff --git a/mozilla/editor/base/nsPlaintextDataTransfer.cpp b/mozilla/editor/base/nsPlaintextDataTransfer.cpp new file mode 100644 index 00000000000..ae695c372ab --- /dev/null +++ b/mozilla/editor/base/nsPlaintextDataTransfer.cpp @@ -0,0 +1,582 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ +#include "nsICaret.h" + + +#include "nsPlaintextEditor.h" +#include "nsHTMLEditUtils.h" + +#include "nsEditorEventListeners.h" + +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines +#include "nsIIndependentSelection.h" //domselections answer to frameselection + + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// netwerk +#include "nsIURI.h" +#include "nsNetUtil.h" + +// Drag & Drop, Clipboard +#include "nsWidgetsCID.h" +#include "nsIClipboard.h" +#include "nsITransferable.h" +#include "nsIDragService.h" +#include "nsIDOMNSUIEvent.h" + +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" +#include "nsIPref.h" +const PRUnichar nbsp = 160; + +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } + + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); +static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); +static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); + +// Drag & Drop, Clipboard Support +static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); +static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); +static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); +static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); +// private clipboard data flavors for html copy/paste +#define kHTMLContext "text/_moz_htmlcontext" +#define kHTMLInfo "text/_moz_htmlinfo" + + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + +NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable) +{ + // Create generic Transferable for getting the data + nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + (void**)transferable); + if (NS_FAILED(rv)) + return rv; + + // Get the nsITransferable interface for getting the data from the clipboard + if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime); + return NS_OK; +} + +NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *transferable) +{ + nsresult rv = NS_OK; + char* bestFlavor = nsnull; + nsCOMPtr genericDataObj; + PRUint32 len = 0; + if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) ) + { + nsAutoTxnsConserveSelection dontSpazMySelection(this); + nsAutoString flavor, stuffToPaste; + flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() + if (flavor.EqualsWithConversion(kUnicodeMime)) + { + nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + if (textDataObj && len > 0) + { + PRUnichar* text = nsnull; + textDataObj->ToString ( &text ); + stuffToPaste.Assign ( text, len / 2 ); + nsAutoEditBatch beginBatching(this); + rv = InsertText(stuffToPaste.GetUnicode()); + if (text) + nsMemory::Free(text); + } + } + } + nsCRT::free(bestFlavor); + + // Try to scroll the selection into view if the paste/drop succeeded + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr selCon; + if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon) + selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION); + } + + return rv; +} + +NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) +{ + ForceCompositionEnd(); + + nsresult rv; + NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr dragSession(do_QueryInterface(dragService)); + + if (!dragSession) return NS_OK; + + // Get the nsITransferable interface for getting the data from the drop + nsCOMPtr trans; + rv = PrepareTransferable(getter_AddRefs(trans)); + if (NS_FAILED(rv)) return rv; + if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL? + + PRUint32 numItems = 0; + rv = dragSession->GetNumDropItems(&numItems); + if (NS_FAILED(rv)) return rv; + + // Combine any deletion and drop insertion into one transaction + nsAutoEditBatch beginBatching(this); + + PRUint32 i; + PRBool doPlaceCaret = PR_TRUE; + for (i = 0; i < numItems; ++i) + { + rv = dragSession->GetData(trans, i); + if (NS_FAILED(rv)) return rv; + if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail? + + if ( doPlaceCaret ) + { + // check if the user pressed the key to force a copy rather than a move + // if we run into problems here, we'll just assume the user doesn't want a copy + PRBool userWantsCopy = PR_FALSE; + + nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent)); + if (!nsuiEvent) return NS_ERROR_FAILURE; + + nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) ); + if (mouseEvent) + +#ifdef XP_MAC + mouseEvent->GetAltKey(&userWantsCopy); +#else + mouseEvent->GetCtrlKey(&userWantsCopy); +#endif + // Source doc is null if source is *not* the current editor document + nsCOMPtr srcdomdoc; + rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc)); + if (NS_FAILED(rv)) return rv; + + // Current doc is destination + nsCOMPtrdestdomdoc; + rv = GetDocument(getter_AddRefs(destdomdoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + if (!selection) return NS_ERROR_FAILURE; + + PRBool isCollapsed; + rv = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(rv)) return rv; + + // Parent and offset under the mouse cursor + nsCOMPtr newSelectionParent; + PRInt32 newSelectionOffset = 0; + rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent)); + if (NS_FAILED(rv)) return rv; + if (!newSelectionParent) return NS_ERROR_FAILURE; + + rv = nsuiEvent->GetRangeOffset(&newSelectionOffset); + if (NS_FAILED(rv)) return rv; + /* Creating a range to store insert position because when + we delete the selection, range gravity will make sure the insertion + point is in the correct place */ + nsCOMPtr destinationRange; + rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange)); + if (NS_FAILED(rv)) + return rv; + if(!destinationRange) + return NS_ERROR_FAILURE; + + // We never have to delete if selection is already collapsed + PRBool deleteSelection = PR_FALSE; + PRBool cursorIsInSelection = PR_FALSE; + + // Check if mouse is in the selection + if (!isCollapsed) + { + PRInt32 rangeCount; + rv = selection->GetRangeCount(&rangeCount); + if (NS_FAILED(rv)) + return rv?rv:NS_ERROR_FAILURE; + + for (PRInt32 j = 0; j < rangeCount; j++) + { + nsCOMPtr range; + + rv = selection->GetRangeAt(j, getter_AddRefs(range)); + if (NS_FAILED(rv) || !range) + continue;//dont bail yet, iterate through them all + + nsCOMPtr nsrange(do_QueryInterface(range)); + if (NS_FAILED(rv) || !nsrange) + continue;//dont bail yet, iterate through them all + + rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection); + if(cursorIsInSelection) + break; + } + if (cursorIsInSelection) + { + // Dragging within same doc can't drop on itself -- leave! + // (We shouldn't get here - drag event shouldn't have started if over selection) + if (srcdomdoc == destdomdoc) + return NS_OK; + + // Dragging from another window onto a selection + // XXX Decision made to NOT do this, + // note that 4.x does replace if dropped on + //deleteSelection = PR_TRUE; + } + else + { + // We are NOT over the selection + if (srcdomdoc == destdomdoc) + { + // Within the same doc: delete if user doesn't want to copy + deleteSelection = !userWantsCopy; + } + else + { + // Different source doc: Don't delete + deleteSelection = PR_FALSE; + } + } + } + + if (deleteSelection) + { + rv = DeleteSelection(eNone); + if (NS_FAILED(rv)) return rv; + } + + // If we deleted the selection because we dropped from another doc, + // then we don't have to relocate the caret (insert at the deletion point) + if (!(deleteSelection && srcdomdoc != destdomdoc)) + { + // Move the selection to the point under the mouse cursor + rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent)); + if (NS_FAILED(rv)) + return rv; + if(!newSelectionParent) + return NS_ERROR_FAILURE; + + rv = destinationRange->GetStartOffset(&newSelectionOffset); + if (NS_FAILED(rv)) + return rv; + selection->Collapse(newSelectionParent, newSelectionOffset); + } + // We have to figure out whether to delete and relocate caret only once + doPlaceCaret = PR_FALSE; + } + + rv = InsertTextFromTransferable(trans); + } + + return rv; +} + +NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag) +{ + /* we really should be checking the XY coordinates of the mouseevent and ensure that + * that particular point is actually within the selection (not just that there is a selection) + */ + aCanDrag = PR_FALSE; + + // KLUDGE to work around bug 50703 + // After double click and object property editing, + // we get a spurious drag event + if (mIgnoreSpuriousDragEvent) + { + mIgnoreSpuriousDragEvent = PR_FALSE; + return NS_OK; + } + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + // if we are collapsed, we have no selection so nothing to drag + if ( isCollapsed ) + return NS_OK; + + nsCOMPtr eventTarget; + res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget)); + if (NS_FAILED(res)) return res; + if ( eventTarget ) + { + nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget); + if ( eventTargetDomNode ) + { + PRBool amTargettedCorrectly = PR_FALSE; + res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly); + if (NS_FAILED(res)) return res; + + aCanDrag = amTargettedCorrectly; + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent) +{ + nsresult rv; + + nsCOMPtr eventTarget; + rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget)); + if (NS_FAILED(rv)) return rv; + nsCOMPtr domnode = do_QueryInterface(eventTarget); + + /* get the selection to be dragged */ + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + + /* create an array of transferables */ + nsCOMPtr transferableArray; + NS_NewISupportsArray(getter_AddRefs(transferableArray)); + if (transferableArray == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + + /* get the drag service */ + NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv); + if (NS_FAILED(rv)) return rv; + + /* create html flavor transferable */ + nsCOMPtr trans; + rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + getter_AddRefs(trans)); + if (NS_FAILED(rv)) return rv; + if ( !trans ) return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr domdoc; + rv = GetDocument(getter_AddRefs(domdoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr doc = do_QueryInterface(domdoc); + if (doc) + { + nsCOMPtr docEncoder; + + docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html"); + NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); + + docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0); + docEncoder->SetSelection(selection); + + nsAutoString buffer; + + rv = docEncoder->EncodeToString(buffer); + + if (NS_FAILED(rv)) + return rv; + + if ( !buffer.IsEmpty() ) + { + nsCOMPtr htmlConverter; + rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter), + getter_AddRefs(htmlConverter)); + if (NS_FAILED(rv)) return rv; + if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr dataWrapper; + rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper)); + if (NS_FAILED(rv)) return rv; + if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY; + + rv = trans->AddDataFlavor(kHTMLMime); + if (NS_FAILED(rv)) return rv; + rv = trans->SetConverter(htmlConverter); + if (NS_FAILED(rv)) return rv; + + rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) ); + if (NS_FAILED(rv)) return rv; + + // QI the data object an |nsISupports| so that when the transferable holds + // onto it, it will addref the correct interface. + nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) ); + rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2); + if (NS_FAILED(rv)) return rv; + + /* add the transferable to the array */ + rv = transferableArray->AppendElement(trans); + if (NS_FAILED(rv)) return rv; + + /* invoke drag */ + unsigned int flags; + // in some cases we'll want to cut rather than copy... hmmmmm... + // if ( wantToCut ) + // flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE; + // else + flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; + + rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags); + if (NS_FAILED(rv)) return rv; + + aDragEvent->PreventBubble(); + } + } + + return rv; +} + +NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType) +{ + ForceCompositionEnd(); + + // Get Clipboard Service + nsresult rv; + NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv ); + if ( NS_FAILED(rv) ) + return rv; + + // Get the nsITransferable interface for getting the data from the clipboard + nsCOMPtr trans; + rv = PrepareTransferable(getter_AddRefs(trans)); + if (NS_SUCCEEDED(rv) && trans) + { + // Get the Data from the clipboard + if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) + { + rv = InsertTextFromTransferable(trans); + } + } + + return rv; +} + + +NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste) +{ + aCanPaste = PR_FALSE; + + nsresult rv; + NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv); + if (NS_FAILED(rv)) return rv; + + // the flavors that we can deal with + char* textEditorFlavors[] = { kUnicodeMime, nsnull }; + + nsCOMPtr flavorsList; + rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList)); + if (NS_FAILED(rv)) return rv; + + PRUint32 editorFlags; + GetFlags(&editorFlags); + + // add the flavors for text editors + for (char** flavor = textEditorFlavors; *flavor; flavor++) + { + nsCOMPtr flavorString; + nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString)); + if (flavorString) + { + flavorString->SetData(*flavor); + flavorsList->AppendElement(flavorString); + } + } + + PRBool haveFlavors; + rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors); + if (NS_FAILED(rv)) return rv; + + aCanPaste = haveFlavors; + return NS_OK; +} diff --git a/mozilla/editor/base/nsPlaintextEditor.cpp b/mozilla/editor/base/nsPlaintextEditor.cpp new file mode 100644 index 00000000000..6c4d2867e85 --- /dev/null +++ b/mozilla/editor/base/nsPlaintextEditor.cpp @@ -0,0 +1,1808 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ + + +#include "nsPlaintextEditor.h" +#include "nsICaret.h" +#include "nsHTMLEditUtils.h" +#include "nsTextEditRules.h" +#include "nsEditorEventListeners.h" +#include "nsIEditActionListener.h" +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsIIndependentSelection.h" //domselections answer to frameselection + +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// netwerk +#include "nsIURI.h" +#include "nsNetUtil.h" + +// Transactionas +#include "PlaceholderTxn.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" +#include "nsIPref.h" +#include "nsStyleConsts.h" +#include "nsIStyleContext.h" + +const PRUnichar nbsp = 160; + +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } + + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); +static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); +static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + +nsIAtom *nsPlaintextEditor::gTypingTxnName; +nsIAtom *nsPlaintextEditor::gIMETxnName; +nsIAtom *nsPlaintextEditor::gDeleteTxnName; + +// prototype for rules creation shortcut +nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); + +nsPlaintextEditor::nsPlaintextEditor() +: nsEditor() +, mIgnoreSpuriousDragEvent(PR_FALSE) +, mRules(nsnull) +, mIsComposing(PR_FALSE) +, mMaxTextLength(-1) +, mInitTriggerCounter(0) +{ +// Done in nsEditor +// NS_INIT_REFCNT(); + if (!gTypingTxnName) + gTypingTxnName = NS_NewAtom("Typing"); + else + NS_ADDREF(gTypingTxnName); + if (!gIMETxnName) + gIMETxnName = NS_NewAtom("IME"); + else + NS_ADDREF(gIMETxnName); + if (!gDeleteTxnName) + gDeleteTxnName = NS_NewAtom("Deleting"); + else + NS_ADDREF(gDeleteTxnName); +} + +nsPlaintextEditor::~nsPlaintextEditor() +{ + /* first, delete the transaction manager if there is one. + this will release any remaining transactions. + this is important because transactions can hold onto the atoms (gTypingTxnName, ...) + and to make the optimization (holding refcounted statics) work correctly, + the editor instance needs to hold the last refcount. + If you get this wrong, expect to deref a garbage gTypingTxnName pointer if you bring up a second editor. + */ + if (mTxnMgr) { + mTxnMgr = 0; + } + nsrefcnt refCount=0; + if (gTypingTxnName) // we addref'd in the constructor + { // want to release it without nulling out the pointer. + refCount = gTypingTxnName->Release(); + if (0==refCount) { + gTypingTxnName = nsnull; + } + } + + if (gIMETxnName) // we addref'd in the constructor + { // want to release it without nulling out the pointer. + refCount = gIMETxnName->Release(); + if (0==refCount) { + gIMETxnName = nsnull; + } + } + + if (gDeleteTxnName) // we addref'd in the constructor + { // want to release it without nulling out the pointer. + refCount = gDeleteTxnName->Release(); + if (0==refCount) { + gDeleteTxnName = nsnull; + } + } + + // remove the rules as an action listener. Else we get a bad ownership loop later on. + // it's ok if the rules aren't a listener; we ignore the error. + nsCOMPtr mListener = do_QueryInterface(mRules); + RemoveEditActionListener(mListener); + + nsCOMPtr erP; + nsresult result = GetDOMEventReceiver(getter_AddRefs(erP)); + if (NS_SUCCEEDED(result) && erP) + { + if (mKeyListenerP) { + erP->RemoveEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener)); + } + if (mMouseListenerP) { + erP->RemoveEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener)); + } + if (mTextListenerP) { + erP->RemoveEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener)); + } + if (mCompositionListenerP) { + erP->RemoveEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener)); + } + if (mFocusListenerP) { + erP->RemoveEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener)); + } + if (mDragListenerP) { + erP->RemoveEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener)); + } + } + else + NS_NOTREACHED("~nsTextEditor"); + +} + +NS_IMPL_ADDREF_INHERITED(nsPlaintextEditor, nsEditor) +NS_IMPL_RELEASE_INHERITED(nsPlaintextEditor, nsEditor) + + +NS_IMETHODIMP nsPlaintextEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + + *aInstancePtr = nsnull; + + if (aIID.Equals(NS_GET_IID(nsIPlaintextEditor))) { + *aInstancePtr = NS_STATIC_CAST(nsIPlaintextEditor*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + + return nsEditor::QueryInterface(aIID, aInstancePtr); +} + + +NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc, + nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags) +{ + NS_PRECONDITION(aDoc && aPresShell, "bad arg"); + if (!aDoc || !aPresShell) + return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK, rulesRes = NS_OK; + + if (1) + { + // block to scope nsAutoEditInitRulesTrigger + nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes); + + // Init the base editor + res = nsEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags); + } + + if (NS_FAILED(rulesRes)) return rulesRes; + return res; +} + +void +nsPlaintextEditor::BeginEditorInit() +{ + mInitTriggerCounter++; +} + +nsresult +nsPlaintextEditor::EndEditorInit() +{ + nsresult res = NS_OK; + NS_PRECONDITION(mInitTriggerCounter > 0, "ended editor init before we began?"); + mInitTriggerCounter--; + if (mInitTriggerCounter == 0) + { + res = InitRules(); + if (NS_SUCCEEDED(res)) + EnableUndo(PR_TRUE); + } + return res; +} + +NS_IMETHODIMP +nsPlaintextEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) +{ + nsresult result; + + result = nsEditor::SetDocumentCharacterSet(characterSet); + + // update META charset tag + if (NS_SUCCEEDED(result)) { + nsCOMPtrdomdoc; + result = GetDocument(getter_AddRefs(domdoc)); + if (NS_SUCCEEDED(result) && domdoc) { + nsAutoString newMetaString; + nsCOMPtrmetaList; + nsCOMPtrmetaNode; + nsCOMPtrmetaElement; + PRBool newMetaCharset = PR_TRUE; + + // get a list of META tags + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("meta"), getter_AddRefs(metaList)); + if (NS_SUCCEEDED(result) && metaList) { + PRUint32 listLength = 0; + (void) metaList->GetLength(&listLength); + + for (PRUint32 i = 0; i < listLength; i++) { + metaList->Item(i, getter_AddRefs(metaNode)); + if (!metaNode) continue; + metaElement = do_QueryInterface(metaNode); + if (!metaElement) continue; + + const NS_ConvertASCIItoUCS2 content("charset="); + nsString currentValue; + + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("http-equiv"), currentValue))) continue; + + if (kNotFound != currentValue.Find("content-type", PR_TRUE)) { + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("content"), currentValue))) continue; + + PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE); + if (kNotFound != offset) { + currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html) + newMetaString.Append(content); + newMetaString.Append(characterSet); + result = nsEditor::SetAttribute(metaElement, NS_ConvertASCIItoUCS2("content"), newMetaString); + if (NS_SUCCEEDED(result)) + newMetaCharset = PR_FALSE; + break; + } + } + } + } + + if (newMetaCharset) { + nsCOMPtrheadList; + nsCOMPtrheadNode; + nsCOMPtrresultNode; + + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"),getter_AddRefs(headList)); + if (NS_SUCCEEDED(result) && headList) { + headList->Item(0, getter_AddRefs(headNode)); + if (headNode) { + // Create a new meta charset tag + // can't use |NS_LITERAL_STRING| here until |CreateNode| is fixed to accept readables + result = CreateNode(NS_ConvertASCIItoUCS2("meta"), headNode, 0, getter_AddRefs(resultNode)); + if (NS_FAILED(result)) + return NS_ERROR_FAILURE; + + // Set attributes to the created element + if (resultNode && nsCRT::strlen(characterSet) > 0) { + metaElement = do_QueryInterface(resultNode); + if (metaElement) { + // not undoable, undo should undo CreateNode + result = metaElement->SetAttribute(NS_LITERAL_STRING("http-equiv"), NS_LITERAL_STRING("Content-Type")); + if (NS_SUCCEEDED(result)) { + newMetaString.AssignWithConversion("text/html;charset="); + newMetaString.Append(characterSet); + // not undoable, undo should undo CreateNode + result = metaElement->SetAttribute(NS_LITERAL_STRING("content"), newMetaString); + } + } + } + } + } + } + } + } + + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::PostCreate() +{ + nsresult result = InstallEventListeners(); + if (NS_FAILED(result)) return result; + + result = nsEditor::PostCreate(); + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::InstallEventListeners() +{ + NS_ASSERTION(mDocWeak, "no document set on this editor"); + if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED; + + nsresult result; + // get a key listener + result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // get a mouse listener + result = NS_NewEditorMouseListener(getter_AddRefs(mMouseListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // get a text listener + result = NS_NewEditorTextListener(getter_AddRefs(mTextListenerP),this); + if (NS_FAILED(result)) { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a composition listener + result = NS_NewEditorCompositionListener(getter_AddRefs(mCompositionListenerP),this); + if (NS_FAILED(result)) { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a drag listener + result = NS_NewEditorDragListener(getter_AddRefs(mDragListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // get a focus listener + result = NS_NewEditorFocusListener(getter_AddRefs(mFocusListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + nsCOMPtr erP; + result = GetDOMEventReceiver(getter_AddRefs(erP)); + + //end hack + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // register the event listeners with the DOM event reveiver + result = erP->AddEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register key listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register mouse listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register focus listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register text listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register composition listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register drag listener"); + } + } + } + } + } + if (NS_FAILED(result)) { + HandleEventListenerError(); + } + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetFlags(PRUint32 *aFlags) +{ + if (!mRules || !aFlags) { return NS_ERROR_NULL_POINTER; } + return mRules->GetFlags(aFlags); +} + + +NS_IMETHODIMP +nsPlaintextEditor::SetFlags(PRUint32 aFlags) +{ + if (!mRules) { return NS_ERROR_NULL_POINTER; } + return mRules->SetFlags(aFlags); +} + + +NS_IMETHODIMP nsPlaintextEditor::InitRules() +{ + // instantiate the rules for this text editor + nsresult res = NS_ERROR_FAILURE; + res = NS_NewTextEditRules(getter_AddRefs(mRules)); + if (NS_FAILED(res)) return res; + if (!mRules) return NS_ERROR_UNEXPECTED; + res = mRules->Init(this, mFlags); + + return res; +} + + +PRBool nsPlaintextEditor::IsModifiable() +{ + PRUint32 flags; + if (NS_SUCCEEDED(GetFlags(&flags))) + return ((flags & eEditorReadonlyMask) == 0); + else + return PR_FALSE; +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIHTMLEditor methods +#pragma mark - +#endif + +NS_IMETHODIMP nsPlaintextEditor::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent) +{ + PRUint32 keyCode, character; + PRBool isShift, ctrlKey, altKey, metaKey; + + if (!aKeyEvent) return NS_ERROR_NULL_POINTER; + + if (NS_SUCCEEDED(aKeyEvent->GetKeyCode(&keyCode)) && + NS_SUCCEEDED(aKeyEvent->GetShiftKey(&isShift)) && + NS_SUCCEEDED(aKeyEvent->GetCtrlKey(&ctrlKey)) && + NS_SUCCEEDED(aKeyEvent->GetAltKey(&altKey)) && + NS_SUCCEEDED(aKeyEvent->GetMetaKey(&metaKey))) + { + aKeyEvent->GetCharCode(&character); + if (keyCode == nsIDOMKeyEvent::DOM_VK_RETURN + || keyCode == nsIDOMKeyEvent::DOM_VK_ENTER) + { + nsString empty; + return TypedText(empty.GetUnicode(), eTypedBreak); + } + else if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE) + { + // pass escape keypresses through as empty strings: needed for ime support + nsString empty; + return TypedText(empty.GetUnicode(), eTypedText); + } + + if (character && !altKey && !ctrlKey && !isShift && !metaKey) + { + nsAutoString key(character); + return TypedText(key.GetUnicode(), eTypedText); + } + } + return NS_ERROR_FAILURE; +} + +/* This routine is needed to provide a bottleneck for typing for logging + purposes. Can't use EditorKeyPress() (above) for that since it takes + a nsIDOMUIEvent* parameter. So instead we pass enough info through + to TypedText() to determine what action to take, but without passing + an event. + */ +NS_IMETHODIMP nsPlaintextEditor::TypedText(const PRUnichar* aString, + PRInt32 aAction) +{ + nsAutoPlaceHolderBatch batch(this, gTypingTxnName); + + switch (aAction) + { + case eTypedText: + { + return InsertText(aString); + } + case eTypedBreak: + { + return InsertLineBreak(); + } + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsPlaintextEditor::CreateBRImpl(nsCOMPtr *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr *outBRNode, EDirection aSelect) +{ + if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER; + *outBRNode = nsnull; + nsresult res; + + // we need to insert a br. unfortunately, we may have to split a text node to do it. + nsCOMPtr node = *aInOutParent; + PRInt32 theOffset = *aInOutOffset; + nsCOMPtr nodeAsText = do_QueryInterface(node); + nsAutoString brType; brType.AssignWithConversion("br"); + nsCOMPtr brNode; + if (nodeAsText) + { + nsCOMPtr tmp; + PRInt32 offset; + PRUint32 len; + nodeAsText->GetLength(&len); + GetNodeLocation(node, &tmp, &offset); + if (!tmp) return NS_ERROR_FAILURE; + if (!theOffset) + { + // we are already set to go + } + else if (theOffset == (PRInt32)len) + { + // update offset to point AFTER the text node + offset++; + } + else + { + // split the text node + res = SplitNode(node, theOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + res = GetNodeLocation(node, &tmp, &offset); + if (NS_FAILED(res)) return res; + } + // create br + res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode)); + if (NS_FAILED(res)) return res; + *aInOutParent = tmp; + *aInOutOffset = offset+1; + } + else + { + res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode)); + if (NS_FAILED(res)) return res; + (*aInOutOffset)++; + } + + *outBRNode = brNode; + if (*outBRNode && (aSelect != eNone)) + { + nsCOMPtr selection; + nsCOMPtr parent; + PRInt32 offset; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsCOMPtr selPriv(do_QueryInterface(selection)); + res = GetNodeLocation(*outBRNode, &parent, &offset); + if (NS_FAILED(res)) return res; + if (aSelect == eNext) + { + // position selection after br + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(parent, offset+1); + } + else if (aSelect == ePrevious) + { + // position selection before br + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(parent, offset); + } + } + return NS_OK; +} + + +NS_IMETHODIMP nsPlaintextEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect) +{ + nsCOMPtr parent = aNode; + PRInt32 offset = aOffset; + return CreateBRImpl(&parent, &offset, outBRNode, aSelect); +} + +NS_IMETHODIMP nsPlaintextEditor::InsertBR(nsCOMPtr *outBRNode) +{ + PRBool bCollapsed; + nsCOMPtr selection; + + if (!outBRNode) return NS_ERROR_NULL_POINTER; + *outBRNode = nsnull; + + // calling it text insertion to trigger moz br treatment by rules + nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext); + + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsCOMPtr selPriv(do_QueryInterface(selection)); + res = selection->GetIsCollapsed(&bCollapsed); + if (NS_FAILED(res)) return res; + if (!bCollapsed) + { + res = DeleteSelection(nsIEditor::eNone); + if (NS_FAILED(res)) return res; + } + nsCOMPtr selNode; + PRInt32 selOffset; + res = GetStartNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + + res = CreateBR(selNode, selOffset, outBRNode); + if (NS_FAILED(res)) return res; + + // position selection after br + res = GetNodeLocation(*outBRNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(selNode, selOffset+1); + + return res; +} +nsresult nsPlaintextEditor::GetTextSelectionOffsets(nsISelection *aSelection, + PRInt32 &aOutStartOffset, + PRInt32 &aOutEndOffset) +{ + if(!aSelection) { return NS_ERROR_NULL_POINTER; } + nsresult result; + // initialize out params + aOutStartOffset = 0; // default to first char in selection + aOutEndOffset = -1; // default to total length of text in selection + + nsCOMPtr startNode, endNode, parentNode; + PRInt32 startOffset, endOffset; + aSelection->GetAnchorNode(getter_AddRefs(startNode)); + aSelection->GetAnchorOffset(&startOffset); + aSelection->GetFocusNode(getter_AddRefs(endNode)); + aSelection->GetFocusOffset(&endOffset); + + nsCOMPtr enumerator; + nsCOMPtr selection(aSelection); + nsCOMPtr selPriv(do_QueryInterface(selection)); + result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(result)) return result; + if (!enumerator) return NS_ERROR_NULL_POINTER; + + // don't use "result" in this block + enumerator->First(); + nsCOMPtr currentItem; + nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(findParentResult)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + range->GetCommonAncestorContainer(getter_AddRefs(parentNode)); + } + else + { + parentNode = do_QueryInterface(startNode); + } + + + return GetAbsoluteOffsetsForPoints(startNode, startOffset, + endNode, endOffset, + parentNode, + aOutStartOffset, aOutEndOffset); +} + +nsresult +nsPlaintextEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, + PRInt32 aInStartOffset, + nsIDOMNode *aInEndNode, + PRInt32 aInEndOffset, + nsIDOMNode *aInCommonParentNode, + PRInt32 &aOutStartOffset, + PRInt32 &aOutEndOffset) +{ + if(!aInStartNode || !aInEndNode || !aInCommonParentNode) + return NS_ERROR_NULL_POINTER; + + nsresult result; + // initialize out params + aOutStartOffset = 0; // default to first char in selection + aOutEndOffset = -1; // default to total length of text in selection + + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(result)) return result; + if (!iter) return NS_ERROR_NULL_POINTER; + + PRUint32 totalLength=0; + nsCOMPtrtextNode; + nsCOMPtrblockParentContent = do_QueryInterface(aInCommonParentNode); + iter->Init(blockParentContent); + // loop through the content iterator for each content node + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + textNode = do_QueryInterface(content); + if (textNode) + { + nsCOMPtrcurrentNode = do_QueryInterface(textNode); + if (!currentNode) {return NS_ERROR_NO_INTERFACE;} + if (IsEditable(currentNode)) + { + if (currentNode.get() == aInStartNode) + { + aOutStartOffset = totalLength + aInStartOffset; + } + if (currentNode.get() == aInEndNode) + { + aOutEndOffset = totalLength + aInEndOffset; + break; + } + PRUint32 length; + textNode->GetLength(&length); + totalLength += length; + } + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + if (-1==aOutEndOffset) { + aOutEndOffset = totalLength; + } + + // guarantee that aOutStartOffset <= aOutEndOffset + if (aOutEndOffset end"); + return result; +} + +nsresult +nsPlaintextEditor::GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver) +{ + if (!aEventReceiver) + return NS_ERROR_NULL_POINTER; + + *aEventReceiver = 0; + + nsCOMPtr rootElement; + + nsresult result = GetRootElement(getter_AddRefs(rootElement)); + + if (NS_FAILED(result)) + return result; + + if (!rootElement) + return NS_ERROR_FAILURE; + + // Now hack to make sure we are not anonymous content. + // If we are grab the parent of root element for our observer. + + nsCOMPtr content = do_QueryInterface(rootElement); + + if (content) + { + nsCOMPtr parent; + if (NS_SUCCEEDED(content->GetParent(*getter_AddRefs(parent))) && parent) + { + PRInt32 index; + if (NS_FAILED(parent->IndexOf(content, index)) || index < 0 ) + { + rootElement = do_QueryInterface(parent); //this will put listener on the form element basically + result = rootElement->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), (void **)aEventReceiver); + } + else + rootElement = 0; // Let the event receiver work on the document instead of the root element + } + } + else + rootElement = 0; + + if (!rootElement && mDocWeak) + { + // Don't use getDocument here, because we have no way of knowing if + // Init() was ever called. So we need to get the document ourselves, + // if it exists. + + nsCOMPtr domdoc = do_QueryReferent(mDocWeak); + + if (!domdoc) + return NS_ERROR_FAILURE; + + result = domdoc->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), (void **)aEventReceiver); + } + + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::CollapseSelectionToStart() +{ + nsCOMPtr bodyElement; + nsresult res = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtr bodyNode = do_QueryInterface(bodyElement); + nsCOMPtr selection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + return selection->Collapse(bodyNode,0); +} + +NS_IMETHODIMP nsPlaintextEditor::DeleteSelection(nsIEditor::EDirection aAction) +{ + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel, handled; + nsresult result; + + // delete placeholder txns merge. + nsAutoPlaceHolderBatch batch(this, gDeleteTxnName); + nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); + + // If it's one of these modes, + // we have to extend the selection first. + // This needs to happen inside selection batching, + // otherwise the deleted text is autocopied to the clipboard. + if (aAction == eNextWord || aAction == ePreviousWord + || aAction == eToBeginningOfLine || aAction == eToEndOfLine) + { + if (!mSelConWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr selCont (do_QueryReferent(mSelConWeak)); + if (!selCont) + return NS_ERROR_NO_INTERFACE; + + switch (aAction) + { + case eNextWord: + result = selCont->WordMove(PR_TRUE, PR_TRUE); + // DeleteSelectionImpl doesn't handle these actions + // because it's inside batching, so don't confuse it: + aAction = eNone; + break; + case ePreviousWord: + result = selCont->WordMove(PR_FALSE, PR_TRUE); + aAction = eNone; + break; + case eToBeginningOfLine: + selCont->IntraLineMove(PR_TRUE, PR_FALSE); // try to move to end + result = selCont->IntraLineMove(PR_FALSE, PR_TRUE); // select to beginning + aAction = eNone; + break; + case eToEndOfLine: + result = selCont->IntraLineMove(PR_TRUE, PR_TRUE); + // Bugs 54449/54452: the selection jumps to the wrong place + // when deleting past a
and action is eNext or ePrev, + // so setting action to eNone makes delete-to-end marginally usable. + // aAction should really be set to eNext + aAction = eNone; + break; + default: // avoid several compiler warnings + result = NS_OK; + break; + } + if (NS_FAILED(result)) + { +#ifdef DEBUG + printf("Selection controller interface didn't work!\n"); +#endif + return result; + } + } + + // pre-process + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection); + ruleInfo.collapsedAction = aAction; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(result)) return result; + if (!cancel && !handled) + { + result = DeleteSelectionImpl(aAction); + } + if (!cancel) + { + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + + return result; +} + +NS_IMETHODIMP nsPlaintextEditor::InsertText(const PRUnichar* aStringToInsert) +{ + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel, handled; + PRInt32 theAction = nsTextEditRules::kInsertText; + PRInt32 opID = kOpInsertText; + if (mInIMEMode) + { + theAction = nsTextEditRules::kInsertTextIME; + opID = kOpInsertIMEText; + } + nsAutoPlaceHolderBatch batch(this, nsnull); + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); + + // pre-process + nsresult result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + nsAutoString resultString; + // XXX can we trust instring to outlive ruleInfo, + // XXX and ruleInfo not to refer to instring in its dtor? + nsAutoString instring(aStringToInsert); + nsTextRulesInfo ruleInfo(theAction); + ruleInfo.inString = &instring; + ruleInfo.outString = &resultString; + ruleInfo.maxLength = mMaxTextLength; + + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(result)) return result; + if (!cancel && !handled) + { + // we rely on rules code for now - no default implementation + } + if (!cancel) + { + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + return result; +} + + +NS_IMETHODIMP nsPlaintextEditor::InsertLineBreak() +{ + nsresult res; + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext); + nsCOMPtr selection; + PRBool cancel, handled; + + // pre-process + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertBreak); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (!cancel && !handled) + { + // create the new BR node + nsCOMPtr newNode; + nsAutoString tag; tag.AssignWithConversion("BR"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (!newNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called + if (NS_SUCCEEDED(res)) + { + // set the selection to the new node + nsCOMPtrparent; + res = newNode->GetParentNode(getter_AddRefs(parent)); + if (!parent) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called + if (NS_SUCCEEDED(res)) + { + PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not + nsCOMPtrnextNode; + newNode->GetNextSibling(getter_AddRefs(nextNode)); + if (nextNode) + { + nsCOMPtrnextTextNode; + nextTextNode = do_QueryInterface(nextNode); + if (!nextTextNode) { + nextNode = do_QueryInterface(newNode); + } + else { + offsetInParent=0; + } + } + else { + nextNode = do_QueryInterface(newNode); + } + res = GetSelection(getter_AddRefs(selection)); + if (!selection) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called + if (NS_SUCCEEDED(res)) + { + nsCOMPtr selPriv(do_QueryInterface(selection)); + if (-1==offsetInParent) + { + nextNode->GetParentNode(getter_AddRefs(parent)); + res = GetChildOffset(nextNode, parent, offsetInParent); + if (NS_SUCCEEDED(res)) { + // SetInterlinePosition(PR_TRUE) means we want the caret to stick to the content on the "right". + // We want the caret to stick to whatever is past the break. This is + // because the break is on the same line we were on, but the next content + // will be on the following line. + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break + } + } + else + { + res = selection->Collapse(nextNode, offsetInParent); + } + } + } + } + } + if (!cancel) + { + // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + + return res; +} + + +NS_IMETHODIMP +nsPlaintextEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty) +{ + if (!aDocumentIsEmpty) + return NS_ERROR_NULL_POINTER; + + if (!mRules) + return NS_ERROR_NOT_INITIALIZED; + + return mRules->DocumentIsEmpty(aDocumentIsEmpty); +} + +NS_IMETHODIMP +nsPlaintextEditor::GetTextLength(PRInt32 *aCount) +{ + if (!aCount) { return NS_ERROR_NULL_POINTER; } + nsresult result; + // initialize out params + *aCount = 0; + + // special-case for empty document, to account for the bogus text node + PRBool docEmpty; + result = GetDocumentIsEmpty(&docEmpty); + if (NS_FAILED(result)) return result; + if (docEmpty) + { + *aCount = 0; + return NS_OK; + } + + // get the body node + nsCOMPtr bodyElement; + result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(result)) { return result; } + if (!bodyElement) { return NS_ERROR_NULL_POINTER; } + + // get the offsets of the first and last children of the body node + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + if (!bodyNode) { return NS_ERROR_NULL_POINTER; } + PRInt32 numBodyChildren=0; + nsCOMPtrlastChild; + result = bodyNode->GetLastChild(getter_AddRefs(lastChild)); + if (NS_FAILED(result)) { return result; } + if (!lastChild) { return NS_ERROR_NULL_POINTER; } + result = GetChildOffset(lastChild, bodyNode, numBodyChildren); + if (NS_FAILED(result)) { return result; } + + // count + PRInt32 start, end; + result = GetAbsoluteOffsetsForPoints(bodyNode, 0, + bodyNode, numBodyChildren, + bodyNode, start, end); + if (NS_SUCCEEDED(result)) + { + NS_ASSERTION(0==start, "GetAbsoluteOffsetsForPoints failed to set start correctly."); + NS_ASSERTION(0<=end, "GetAbsoluteOffsetsForPoints failed to set end correctly."); + if (0<=end) { + *aCount = end; + } + } + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::SetMaxTextLength(PRInt32 aMaxTextLength) +{ + mMaxTextLength = aMaxTextLength; + return NS_OK; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetMaxTextLength(PRInt32* aMaxTextLength) +{ + if (!aMaxTextLength) + return NS_ERROR_INVALID_POINTER; + *aMaxTextLength = mMaxTextLength; + return NS_OK; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetBodyStyleContext(nsIStyleContext** aStyleContext) +{ + nsCOMPtr body; + nsresult res = GetRootElement(getter_AddRefs(body)); + if (NS_FAILED(res)) return res; + nsCOMPtr content = do_QueryInterface(body); + + nsIFrame *frame; + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + res = ps->GetPrimaryFrameFor(content, &frame); + if (NS_FAILED(res)) return res; + + return ps->GetStyleContextFor(frame, aStyleContext); +} + +// +// Get the wrap width for the root of the document. +// +NS_IMETHODIMP +nsPlaintextEditor::GetWrapWidth(PRInt32 *aWrapColumn) +{ + nsresult res; + + if (! aWrapColumn) + return NS_ERROR_NULL_POINTER; + + *aWrapColumn = -1; // default: no wrap + + nsCOMPtr styleContext; + res = GetBodyStyleContext(getter_AddRefs(styleContext)); + if (NS_FAILED(res)) return res; + + const nsStyleText* styleText = + (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text); + + if (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) + { + *aWrapColumn = 0; // wrap to window width + } + else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace) + { + const nsStylePosition* stylePosition = + (const nsStylePosition*)styleContext->GetStyleData(eStyleStruct_Position); + if (stylePosition->mWidth.GetUnit() == eStyleUnit_Chars) + *aWrapColumn = stylePosition->mWidth.GetIntValue(); + else + { + *aWrapColumn = -1; + return NS_ERROR_UNEXPECTED; + } + } + else + { + *aWrapColumn = -1; + } + return NS_OK; +} + +// +// See if the style value includes this attribute, and if it does, +// cut out everything from the attribute to the next semicolon. +// +static void CutStyle(const char* stylename, nsString& styleValue) +{ + // Find the current wrapping type: + PRInt32 styleStart = styleValue.Find(stylename, PR_TRUE); + if (styleStart >= 0) + { + PRInt32 styleEnd = styleValue.Find(";", PR_FALSE, styleStart); + if (styleEnd > styleStart) + styleValue.Cut(styleStart, styleEnd - styleStart + 1); + else + styleValue.Cut(styleStart, styleValue.Length() - styleStart); + } +} + +// +// Change the wrap width on the root of this document. +// +NS_IMETHODIMP +nsPlaintextEditor::SetWrapWidth(PRInt32 aWrapColumn) +{ + nsresult res; + + // Ought to set a style sheet here ... + // Probably should keep around an mPlaintextStyleSheet for this purpose. + nsCOMPtr bodyElement; + res = GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + + // Get the current style for this body element: + nsAutoString styleName; styleName.AssignWithConversion("style"); + nsAutoString styleValue; + res = bodyElement->GetAttribute(styleName, styleValue); + if (NS_FAILED(res)) return res; + + // We'll replace styles for these values: + CutStyle("white-space", styleValue); + CutStyle("width", styleValue); + CutStyle("font-family", styleValue); + + // If we have other style left, trim off any existing semicolons + // or whitespace, then add a known semicolon-space: + if (styleValue.Length() > 0) + { + styleValue.Trim("; \t", PR_FALSE, PR_TRUE); + styleValue.AppendWithConversion("; "); + } + + // Make sure we have fixed-width font. This should be done for us, + // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;". + // Only do this if we're wrapping. + PRUint32 flags = 0; + GetFlags(&flags); + if ((flags & eEditorEnableWrapHackMask) && aWrapColumn >= 0) + styleValue.AppendWithConversion("font-family: -moz-fixed; "); + + // and now we're ready to set the new whitespace/wrapping style. + if (aWrapColumn > 0) // Wrap to a fixed column + { + styleValue.AppendWithConversion("white-space: -moz-pre-wrap; width: "); + styleValue.AppendInt(aWrapColumn); + styleValue.AppendWithConversion("ch;"); + } + else if (aWrapColumn == 0) + styleValue.AppendWithConversion("white-space: -moz-pre-wrap;"); + else + styleValue.AppendWithConversion("white-space: pre;"); + + res = bodyElement->SetAttribute(styleName, styleValue); + return res; +} + + + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIEditor overrides +#pragma mark - +#endif + +NS_IMETHODIMP +nsPlaintextEditor::Undo(PRUint32 aCount) +{ + ForceCompositionEnd(); + nsresult result = NS_OK; + + nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone); + + nsTextRulesInfo ruleInfo(nsTextEditRules::kUndo); + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + PRBool cancel, handled; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + + if (!cancel && NS_SUCCEEDED(result)) + { + result = nsEditor::Undo(aCount); + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + + return result; +} + + +NS_IMETHODIMP +nsPlaintextEditor::Redo(PRUint32 aCount) +{ + nsresult result = NS_OK; + + nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone); + + nsTextRulesInfo ruleInfo(nsTextEditRules::kRedo); + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + PRBool cancel, handled; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + + if (!cancel && NS_SUCCEEDED(result)) + { + result = nsEditor::Redo(aCount); + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + + return result; +} + +NS_IMETHODIMP nsPlaintextEditor::Cut() +{ + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (!NS_SUCCEEDED(res)) + return res; + + PRBool isCollapsed; + if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed) + return NS_ERROR_NOT_AVAILABLE; + + res = Copy(); + if (NS_SUCCEEDED(res)) + res = DeleteSelection(eNone); + return res; +} + +NS_IMETHODIMP nsPlaintextEditor::CanCut(PRBool &aCanCut) +{ + aCanCut = PR_FALSE; + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + aCanCut = !isCollapsed && IsModifiable(); + return NS_OK; +} + +NS_IMETHODIMP nsPlaintextEditor::Copy() +{ + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + return ps->DoCopy(); +} + +NS_IMETHODIMP nsPlaintextEditor::CanCopy(PRBool &aCanCopy) +{ + aCanCopy = PR_FALSE; + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + aCanCopy = !isCollapsed; + return NS_OK; +} + + +// Shared between OutputToString and OutputToStream +NS_IMETHODIMP +nsPlaintextEditor::GetAndInitDocEncoder(const nsAReadableString& aFormatType, + PRUint32 aFlags, + const nsAReadableString* aCharset, + nsIDocumentEncoder** encoder) +{ + nsCOMPtr presShell; + nsresult rv = GetPresShell(getter_AddRefs(presShell)); + if (NS_FAILED(rv)) return rv; + if (!presShell) return NS_ERROR_FAILURE; + + nsCAutoString formatType(NS_DOC_ENCODER_CONTRACTID_BASE); + formatType.AppendWithConversion(aFormatType); + nsCOMPtr docEncoder (do_CreateInstance(formatType, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr doc; + rv = presShell->GetDocument(getter_AddRefs(doc)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = docEncoder->Init(doc, aFormatType, aFlags); + NS_ENSURE_SUCCESS(rv, rv); + + if (aCharset && aCharset->Length() != 0 + && !(aCharset->Equals(NS_LITERAL_STRING("null")))) + docEncoder->SetCharset(*aCharset); + + PRInt32 wc; + (void) GetWrapWidth(&wc); + if (wc >= 0) + (void) docEncoder->SetWrapColumn(wc); + + // Set the selection, if appropriate. + // We do this either if the OutputSelectionOnly flag is set, + // in which case we use our existing selection ... + if (aFlags & nsIDocumentEncoder::OutputSelectionOnly) + { + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(rv) && selection) + rv = docEncoder->SetSelection(selection); + NS_ENSURE_SUCCESS(rv, rv); + } + // ... or if the root element is not a body, + // in which case we set the selection to encompass the root. + else + { + nsCOMPtr rootElement; + GetRootElement(getter_AddRefs(rootElement)); + NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE); + if (!nsHTMLEditUtils::IsBody(rootElement)) + { + // XXX Why does this use range rather than selection collapse/extend? + nsCOMPtr range (do_CreateInstance(kCRangeCID, &rv)); + if (NS_FAILED(rv)) return rv; + if (!range) return NS_ERROR_FAILURE; + nsCOMPtr selection (do_CreateInstance(kCDOMSelectionCID, + &rv)); + if (NS_FAILED(rv)) return rv; + if (!selection) return NS_ERROR_FAILURE; + + // get the independent selection interface + nsCOMPtr indSel = do_QueryInterface(selection); + if (indSel) + indSel->SetPresShell(presShell); + + nsCOMPtr content(do_QueryInterface(rootElement)); + if (content) + { + range->SetStart(rootElement,0); + PRInt32 children; + if (NS_SUCCEEDED(content->ChildCount(children))) + range->SetEnd(rootElement,children); + // XXX else, should we return the error code? + + if (NS_FAILED(selection->AddRange(range))) + return NS_ERROR_FAILURE; + } + rv = docEncoder->SetSelection(selection); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + NS_ADDREF(*encoder = docEncoder); + return rv; +} + + +NS_IMETHODIMP +nsPlaintextEditor::OutputToString(nsAWritableString& aOutputString, + const nsAReadableString& aFormatType, + PRUint32 aFlags) +{ + PRBool cancel, handled; + nsString resultString; + nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText); + ruleInfo.outString = &resultString; + // XXX Struct should store a nsAReadable* + nsAutoString str(aFormatType); + ruleInfo.outputFormat = &str; + nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel, &handled); + if (cancel || NS_FAILED(rv)) { return rv; } + if (handled) + { // this case will get triggered by password fields + aOutputString.Assign(*(ruleInfo.outString)); + return rv; + } + + nsCOMPtr encoder; + rv = GetAndInitDocEncoder(aFormatType, aFlags, 0, getter_AddRefs(encoder)); + if (NS_FAILED(rv)) + return rv; + rv = encoder->EncodeToString(aOutputString); + return rv; +} + +NS_IMETHODIMP +nsPlaintextEditor::OutputToStream(nsIOutputStream* aOutputStream, + const nsAReadableString& aFormatType, + const nsAReadableString* aCharset, + PRUint32 aFlags) +{ + nsresult rv; + + // special-case for empty document when requesting plain text, + // to account for the bogus text node. + // XXX Should there be a similar test in OutputToString? + if (aFormatType == NS_LITERAL_STRING("text/plain")) + { + PRBool docEmpty; + rv = GetDocumentIsEmpty(&docEmpty); + if (NS_FAILED(rv)) return rv; + + if (docEmpty) + return NS_OK; // output nothing + } + + nsCOMPtr encoder; + rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset, + getter_AddRefs(encoder)); + + if (NS_FAILED(rv)) + return rv; + + return encoder->EncodeToStream(aOutputStream); +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIEditorIMESupport overrides +#pragma mark - +#endif + +NS_IMETHODIMP +nsPlaintextEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply) +{ + NS_ASSERTION(aTextRangeList, "null ptr"); + if(nsnull == aTextRangeList) + return NS_ERROR_NULL_POINTER; + nsCOMPtr caretP; + + // workaround for windows ime bug 23558: we get every ime event twice. + // for escape keypress, this causes an empty string to be passed + // twice, which freaks out the editor. This is to detect and aviod that + // situation: + if (aCompositionString.IsEmpty() && !mIMETextNode) + { + return NS_OK; + } + + nsCOMPtr selection; + nsresult result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + + mIMETextRangeList = aTextRangeList; + nsAutoPlaceHolderBatch batch(this, gIMETxnName); + + result = InsertText(aCompositionString.GetUnicode()); + + mIMEBufferLength = aCompositionString.Length(); + + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + ps->GetCaret(getter_AddRefs(caretP)); + caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed,selection); + + // second part of 23558 fix: + if (aCompositionString.IsEmpty()) + { + mIMETextNode = nsnull; + } + + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetReconversionString(nsReconversionEventReply* aReply) +{ + nsresult res; + + nsCOMPtr selection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res) || !selection) + return (res == NS_OK) ? NS_ERROR_FAILURE : res; + + // get the first range in the selection. Since it is + // unclear what to do if reconversion happens with a + // multirange selection, we will ignore any additional ranges. + + nsCOMPtr range; + res = selection->GetRangeAt(0, getter_AddRefs(range)); + if (NS_FAILED(res) || !range) + return (res == NS_OK) ? NS_ERROR_FAILURE : res; + + nsAutoString textValue; + res = range->ToString(textValue); + if (NS_FAILED(res)) + return res; + + aReply->mReconversionString = (PRUnichar*) nsMemory::Clone(textValue.GetUnicode(), + (textValue.Length() + 1) * sizeof(PRUnichar)); + if (!aReply->mReconversionString) + return NS_ERROR_OUT_OF_MEMORY; + + // delete the selection + res = DeleteSelection(eNone); + + return res; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsEditor overrides +#pragma mark - +#endif + + +/** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ +NS_IMETHODIMP +nsPlaintextEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) +{ + nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection + if (mRules) return mRules->BeforeEdit(mAction, mDirection); + return NS_OK; +} + + +/** All editor operations which alter the doc should be followed + * with a call to EndOperation */ +NS_IMETHODIMP +nsPlaintextEditor::EndOperation() +{ + // post processing + nsresult res = NS_OK; + if (mRules) res = mRules->AfterEdit(mAction, mDirection); + nsEditor::EndOperation(); // will clear mAction, mDirection + return res; +} + + +NS_IMETHODIMP +nsPlaintextEditor::SelectEntireDocument(nsISelection *aSelection) +{ + nsresult res; + if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; } + + // get body node + nsCOMPtrbodyElement; + res = GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + if (!bodyNode) return NS_ERROR_FAILURE; + + // is doc empty? + PRBool bDocIsEmpty; + res = mRules->DocumentIsEmpty(&bDocIsEmpty); + if (NS_FAILED(res)) return res; + + if (bDocIsEmpty) + { + // if its empty dont select entire doc - that would select the bogus node + return aSelection->Collapse(bodyNode, 0); + } + else + { + return nsEditor::SelectEntireDocument(aSelection); + } + return res; +} + + + +#ifdef XP_MAC +#pragma mark - +#pragma mark Random methods +#pragma mark - +#endif + + +NS_IMETHODIMP nsPlaintextEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject) +{ + nsresult result = NS_ERROR_FAILURE; // we return an error unless we get the index + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + + if ((nsnull!=aNode)) + { // get the content interface + nsCOMPtr nodeAsContent( do_QueryInterface(aNode) ); + if (nodeAsContent) + { // get the frame from the content interface + //Note: frames are not ref counted, so don't use an nsCOMPtr + *aLayoutObject = nsnull; + result = ps->GetLayoutObjectFor(nodeAsContent, aLayoutObject); + } + } + else { + result = NS_ERROR_NULL_POINTER; + } + + return result; +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +NS_IMETHODIMP +nsPlaintextEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) +{ + static char bodyTag[] = "body"; + static char tdTag[] = "td"; + static char thTag[] = "th"; + static char captionTag[] = "caption"; + if (aTag.EqualsIgnoreCase(bodyTag) || + aTag.EqualsIgnoreCase(tdTag) || + aTag.EqualsIgnoreCase(thTag) || + aTag.EqualsIgnoreCase(captionTag) ) + { + aIsTag = PR_TRUE; + } + else { + aIsTag = PR_FALSE; + } + return NS_OK; +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +void nsPlaintextEditor::HandleEventListenerError() +{ + if (gNoisy) { printf("failed to add event listener\n"); } + // null out the nsCOMPtrs + mKeyListenerP = nsnull; + mMouseListenerP = nsnull; + mTextListenerP = nsnull; + mDragListenerP = nsnull; + mCompositionListenerP = nsnull; + mFocusListenerP = nsnull; +} + +#ifdef XP_MAC +#pragma mark - +#endif + diff --git a/mozilla/editor/base/nsPlaintextEditor.h b/mozilla/editor/base/nsPlaintextEditor.h new file mode 100644 index 00000000000..c2bc4435a22 --- /dev/null +++ b/mozilla/editor/base/nsPlaintextEditor.h @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsPlaintextEditor_h__ +#define nsPlaintextEditor_h__ + +#include "nsCOMPtr.h" + +#include "nsIPlaintextEditor.h" + +#include "nsEditor.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventListener.h" + +#include "TypeInState.h" +#include "nsEditRules.h" + +class nsIDOMKeyEvent; +class nsITransferable; +class nsIDOMEventReceiver; +class nsIDocumentEncoder; + +/** + * The text editor implementation. + * Use to edit text document represented as a DOM tree. + */ +class nsPlaintextEditor : public nsEditor, + public nsIPlaintextEditor +{ + +public: + +// Interfaces for addref and release and queryinterface +// NOTE macro used is for classes that inherit from +// another class. Only the base class should use NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED + + /* below used by TypedText() */ + enum { + eTypedText, /* user typed text */ + eTypedBR, /* user typed shift-enter to get a br */ + eTypedBreak /* user typed enter */ + }; + + nsPlaintextEditor(); + virtual ~nsPlaintextEditor(); + + /* ------------ nsIPlaintextEditor methods -------------- */ + NS_DECL_NSIPLAINTEXTEDITOR + + /* ------------ nsIEditorIMESupport overrides -------------- */ + + NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply); + NS_IMETHOD GetReconversionString(nsReconversionEventReply* aReply); + + /* ------------ Overrides of nsEditor interface methods -------------- */ + + /** prepare the editor for use */ + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags); + + NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty); + + NS_IMETHOD DeleteSelection(EDirection aAction); + + NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet); + + /** we override this here to install event listeners */ + NS_IMETHOD PostCreate(); + + NS_IMETHOD GetFlags(PRUint32 *aFlags); + NS_IMETHOD SetFlags(PRUint32 aFlags); + + NS_IMETHOD Undo(PRUint32 aCount); + NS_IMETHOD Redo(PRUint32 aCount); + + NS_IMETHOD Cut(); + NS_IMETHOD CanCut(PRBool &aCanCut); + NS_IMETHOD Copy(); + NS_IMETHOD CanCopy(PRBool &aCanCopy); + NS_IMETHOD Paste(PRInt32 aSelectionType); + NS_IMETHOD CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste); + + NS_IMETHOD CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag); + NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent); + NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent); + + NS_IMETHOD OutputToString(nsAWritableString& aOutputString, + const nsAReadableString& aFormatType, + PRUint32 aFlags); + + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsAReadableString& aFormatType, + const nsAReadableString* aCharsetOverride, + PRUint32 aFlags); + + + /** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ + NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection); + + /** All editor operations which alter the doc should be followed + * with a call to EndOperation */ + NS_IMETHOD EndOperation(); + + /** make the given selection span the entire document */ + NS_IMETHOD SelectEntireDocument(nsISelection *aSelection); + + /* ------------ Utility Routines, not part of public API -------------- */ + NS_IMETHOD TypedText(const PRUnichar* aString, PRInt32 aAction); + + /** returns the absolute position of the end points of aSelection + * in the document as a text stream. + */ + nsresult GetTextSelectionOffsets(nsISelection *aSelection, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset); + + nsresult GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, + PRInt32 aInStartOffset, + nsIDOMNode *aInEndNode, + PRInt32 aInEndOffset, + nsIDOMNode *aInCommonParentNode, + PRInt32 &aOutStartOffset, + PRInt32 &aEndOffset); + +protected: + + NS_IMETHOD InitRules(); + void BeginEditorInit(); + nsresult EndEditorInit(); + + /** install the event listeners for the editor + * used to be part of Init, but now broken out into a separate method + * called by PostCreate, giving the caller the chance to interpose + * their own listeners before we install our own backstops. + */ + NS_IMETHOD InstallEventListeners(); + + /** returns the layout object (nsIFrame in the real world) for aNode + * @param aNode the content to get a frame for + * @param aLayoutObject the "primary frame" for aNode, if one exists. May be null + * @return NS_OK whether a frame is found or not + * an error if some serious error occurs + */ + NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject); + NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); + + // Helpers for output routines + NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType, + PRUint32 aFlags, + const nsAReadableString* aCharset, + nsIDocumentEncoder** encoder); + + // key event helpers + NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, + nsCOMPtr *outBRNode, EDirection aSelect = eNone); + NS_IMETHOD CreateBRImpl(nsCOMPtr *aInOutParent, + PRInt32 *aInOutOffset, + nsCOMPtr *outBRNode, + EDirection aSelect); + NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); + + NS_IMETHOD IsRootTag(nsString &aTag, PRBool &aIsTag); + + + // factored methods for handling insertion of data from transferables (drag&drop or clipboard) + NS_IMETHOD PrepareTransferable(nsITransferable **transferable); + NS_IMETHOD InsertTextFromTransferable(nsITransferable *transferable); + + /** simple utility to handle any error with event listener allocation or registration */ + void HandleEventListenerError(); + + /* small utility routine to test the eEditorReadonly bit */ + PRBool IsModifiable(); + + nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver); + + //XXX Kludge: Used to suppress spurious drag/drop events (bug 50703) + PRBool mIgnoreSpuriousDragEvent; + NS_IMETHOD IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;} + +// Data members +protected: + + nsCOMPtr mRules; + nsCOMPtr mKeyListenerP; + nsCOMPtr mMouseListenerP; + nsCOMPtr mTextListenerP; + nsCOMPtr mCompositionListenerP; + nsCOMPtr mDragListenerP; + nsCOMPtr mFocusListenerP; + PRBool mIsComposing; + PRInt32 mMaxTextLength; + PRInt32 mInitTriggerCounter; + +public: + static nsIAtom *gTypingTxnName; + static nsIAtom *gIMETxnName; + static nsIAtom *gDeleteTxnName; + +// friends +friend class nsHTMLEditRules; +friend class nsTextEditRules; +friend class nsAutoEditInitRulesTrigger; + +}; + +#endif //nsPlaintextEditor_h__ + diff --git a/mozilla/editor/base/nsSelectionState.cpp b/mozilla/editor/base/nsSelectionState.cpp new file mode 100644 index 00000000000..62573f759a2 --- /dev/null +++ b/mozilla/editor/base/nsSelectionState.cpp @@ -0,0 +1,640 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ + +#include "nsSelectionState.h" +#include "nsIDOMCharacterData.h" +#include "nsIDOMNode.h" +#include "nsIDOMRange.h" +#include "nsISelection.h" +#include "nsEditor.h" +#include "nsLayoutCID.h" + +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); + + +/*************************************************************************** + * class for recording selection info. stores selection as collection of + * { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store + * ranges since dom gravity will possibly change the ranges. + */ +nsSelectionState::nsSelectionState() : mArray(){} + +nsSelectionState::~nsSelectionState() +{ + MakeEmpty(); +} + +nsresult +nsSelectionState::SaveSelection(nsISelection *aSel) +{ + if (!aSel) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + PRInt32 i,rangeCount, arrayCount = mArray.Count(); + nsRangeStore *item; + aSel->GetRangeCount(&rangeCount); + + // if we need more items in the array, new them + if (arrayCountarrayCount) + { + while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount))) + { + delete item; + mArray.RemoveElementAt(rangeCount); + } + } + + // now store the selection ranges + for (i=0; i range; + res = aSel->GetRangeAt(i, getter_AddRefs(range)); + item->StoreRange(range); + } + + return res; +} + +nsresult +nsSelectionState::RestoreSelection(nsISelection *aSel) +{ + if (!aSel) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + PRInt32 i, arrayCount = mArray.Count(); + nsRangeStore *item; + + // clear out selection + aSel->RemoveAllRanges(); + + // set the selection ranges anew + for (i=0; i range; + item->GetRange(address_of(range)); + if (!range) return NS_ERROR_UNEXPECTED; + + res = aSel->AddRange(range); + if(NS_FAILED(res)) return res; + + } + return NS_OK; +} + +PRBool +nsSelectionState::IsCollapsed() +{ + if (1 != mArray.Count()) return PR_FALSE; + nsRangeStore *item; + item = (nsRangeStore*)mArray.ElementAt(0); + if (!item) return PR_FALSE; + nsCOMPtr range; + item->GetRange(address_of(range)); + if (!range) return PR_FALSE; + PRBool bIsCollapsed; + range->GetCollapsed(&bIsCollapsed); + return bIsCollapsed; +} + +PRBool +nsSelectionState::IsEqual(nsSelectionState *aSelState) +{ + if (!aSelState) return NS_ERROR_NULL_POINTER; + PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count(); + if (myCount != itsCount) return PR_FALSE; + if (myCount < 1) return PR_FALSE; + + nsRangeStore *myItem, *itsItem; + + for (i=0; imArray.ElementAt(0)); + if (!myItem || !itsItem) return PR_FALSE; + + nsCOMPtr myRange, itsRange; + myItem->GetRange(address_of(myRange)); + itsItem->GetRange(address_of(itsRange)); + if (!myRange || !itsRange) return PR_FALSE; + + PRInt32 compResult; + myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult); + if (compResult) return PR_FALSE; + myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult); + if (compResult) return PR_FALSE; + } + // if we got here, they are equal + return PR_TRUE; +} + +void +nsSelectionState::MakeEmpty() +{ + // free any items in the array + nsRangeStore *item; + while ((item = (nsRangeStore*)mArray.ElementAt(0))) + { + delete item; + mArray.RemoveElementAt(0); + } +} + +PRBool +nsSelectionState::IsEmpty() +{ + return (mArray.Count() == 0); +} + +/*************************************************************************** + * nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions. + */ + +nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {} + +nsRangeUpdater::~nsRangeUpdater() +{ + // free any items in the array + nsRangeStore *item; + while ((item = (nsRangeStore*)mArray.ElementAt(0))) + { + delete item; + mArray.RemoveElementAt(0); + } +} + +void* +nsRangeUpdater::RegisterRange(nsIDOMRange *aRange) +{ + nsRangeStore *item = new nsRangeStore; + if (!item) return nsnull; + item->StoreRange(aRange); + mArray.AppendElement(item); + return item; +} + +nsCOMPtr +nsRangeUpdater::ReclaimRange(void *aCookie) +{ + nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie); + if (!item) return nsnull; + nsCOMPtr outRange; + item->GetRange(address_of(outRange)); + mArray.RemoveElement(aCookie); + delete item; + return outRange; +} + +void +nsRangeUpdater::DropRange(void *aCookie) +{ + nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie); + if (!item) return; + mArray.RemoveElement(aCookie); + delete item; +} + +void +nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem) +{ + if (!aRangeItem) return; + mArray.AppendElement(aRangeItem); + return; +} + +void +nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem) +{ + if (!aRangeItem) return; + mArray.RemoveElement(aRangeItem); + return; +} + +nsresult +nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState) +{ + PRInt32 i, theCount = aSelState.mArray.Count(); + if (theCount < 1) return NS_ERROR_FAILURE; + + nsRangeStore *item; + + for (i=0; istartNode.get() == aParent) && (item->startOffset > aPosition)) + item->startOffset++; + if ((item->endNode.get() == aParent) && (item->endOffset > aPosition)) + item->endOffset++; + } + return NS_OK; +} + +nsresult +nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition) +{ + return SelAdjCreateNode(aParent, aPosition); +} + + +nsresult +nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsRangeStore *item; + + for (i=0; istartNode.get() == aParent) && (item->startOffset > aOffset)) + item->startOffset--; + if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) + item->endOffset--; + } + // MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't + // actually hit this case in the usage i forsee for this. + return NS_OK; +} + + +nsresult +nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsCOMPtr parent; + PRInt32 offset; + nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset); + if (NS_FAILED(result)) return result; + + // first part is same as inserting aNewLeftnode + result = SelAdjInsertNode(parent,offset-1); + if (NS_FAILED(result)) return result; + + // next step is to check for range enpoints inside aOldRightNode + nsRangeStore *item; + + for (i=0; istartNode.get() == aOldRightNode) + { + if (item->startOffset > aOffset) + { + item->startOffset -= aOffset; + } + else + { + item->startNode = aNewLeftNode; + } + } + if (item->endNode.get() == aOldRightNode) + { + if (item->endOffset > aOffset) + { + item->endOffset -= aOffset; + } + else + { + item->endNode = aNewLeftNode; + } + } + } + return NS_OK; +} + + +nsresult +nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRInt32 aOldLeftNodeLength) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsRangeStore *item; + + for (i=0; istartNode.get() == aParent) + { + if (item->startOffset > aOffset) + { + item->startOffset--; + } + else if (item->startOffset == aOffset) + { + // join keeps right hand node + item->startNode = aRightNode; + item->startOffset = aOldLeftNodeLength; + } + } + if (item->endNode.get() == aParent) + { + if (item->endOffset > aOffset) + { + item->endOffset--; + } + else if (item->endOffset == aOffset) + { + // join keeps right hand node + item->endNode = aRightNode; + item->endOffset = aOldLeftNodeLength; + } + } + // adjust endpoints in aRightNode + if (item->startNode.get() == aRightNode) + item->startOffset += aOldLeftNodeLength; + if (item->endNode.get() == aRightNode) + item->endOffset += aOldLeftNodeLength; + } + + return NS_OK; +} + + +nsresult +nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + return NS_OK; +} + + +nsresult +nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + return NS_OK; +} + + +nsresult +nsRangeUpdater::WillReplaceContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsRangeStore *item; + + for (i=0; istartNode.get() == aOriginalNode) + item->startNode = aNewNode; + if (item->endNode.get() == aOriginalNode) + item->endNode = aNewNode; + } + return NS_OK; +} + + +nsresult +nsRangeUpdater::WillRemoveContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aNode || !aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsRangeStore *item; + + for (i=0; istartNode.get() == aNode) + { + item->startNode = aParent; + item->startOffset += aOffset; + } + if (item->endNode.get() == aNode) + { + item->endNode = aParent; + item->endOffset += aOffset; + } + if ((item->startNode.get() == aParent) && (item->startOffset > aOffset)) + item->startOffset += (PRInt32)aNodeOrigLen-1; + if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) + item->endOffset += (PRInt32)aNodeOrigLen-1; + } + return NS_OK; +} + + +nsresult +nsRangeUpdater::WillInsertContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsRangeUpdater::DidInsertContainer() +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + return NS_OK; +} + + +nsresult +nsRangeUpdater::WillMoveNode() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsRangeStore *item; + + for (i=0; istartNode.get() == aOldParent) && (item->startOffset > aOldOffset)) + item->startOffset--; + if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset)) + item->endOffset--; + + // and like an insert in aNewParent + if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset)) + item->startOffset++; + if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset)) + item->endOffset++; + } + return NS_OK; +} + + + +/*************************************************************************** + * helper class for nsSelectionState. nsRangeStore stores range endpoints. + */ + + // DEBUG: PRInt32 nsRangeStore::n = 0; + +nsRangeStore::nsRangeStore() +{ + // DEBUG: n++; printf("range store alloc count=%d\n", n); +} +nsRangeStore::~nsRangeStore() +{ + // DEBUG: n--; printf("range store alloc count=%d\n", n); +} + +nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange) +{ + if (!aRange) return NS_ERROR_NULL_POINTER; + aRange->GetStartContainer(getter_AddRefs(startNode)); + aRange->GetEndContainer(getter_AddRefs(endNode)); + aRange->GetStartOffset(&startOffset); + aRange->GetEndOffset(&endOffset); + return NS_OK; +} + +nsresult nsRangeStore::GetRange(nsCOMPtr *outRange) +{ + if (!outRange) return NS_ERROR_NULL_POINTER; + nsresult res = nsComponentManager::CreateInstance(kCRangeCID, + nsnull, + NS_GET_IID(nsIDOMRange), + getter_AddRefs(*outRange)); + if(NS_FAILED(res)) return res; + + res = (*outRange)->SetStart(startNode, startOffset); + if(NS_FAILED(res)) return res; + + res = (*outRange)->SetEnd(endNode, endOffset); + return res; +} diff --git a/mozilla/editor/base/nsSelectionState.h b/mozilla/editor/base/nsSelectionState.h new file mode 100644 index 00000000000..28cb3dd0dbb --- /dev/null +++ b/mozilla/editor/base/nsSelectionState.h @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef __selectionstate_h__ +#define __selectionstate_h__ + +#include "nsCOMPtr.h" +#include "nsVoidArray.h" +#include "nsIDOMNode.h" + +class nsIDOMCharacterData; +class nsIDOMRange; +class nsISelection; + +/*************************************************************************** + * class for recording selection info. stores selection as collection of + * { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store + * ranges since dom gravity will possibly change the ranges. + */ + +// first a helper struct for saving/setting ranges +struct nsRangeStore +{ + nsRangeStore(); + ~nsRangeStore(); + nsresult StoreRange(nsIDOMRange *aRange); + nsresult GetRange(nsCOMPtr *outRange); + + nsCOMPtr startNode; + PRInt32 startOffset; + nsCOMPtr endNode; + PRInt32 endOffset; + // DEBUG: static PRInt32 n; +}; + +class nsSelectionState +{ + public: + + nsSelectionState(); + ~nsSelectionState(); + + nsresult SaveSelection(nsISelection *aSel); + nsresult RestoreSelection(nsISelection *aSel); + PRBool IsCollapsed(); + PRBool IsEqual(nsSelectionState *aSelState); + void MakeEmpty(); + PRBool IsEmpty(); + protected: + nsVoidArray mArray; + + friend class nsRangeUpdater; +}; + +class nsRangeUpdater +{ + public: + + nsRangeUpdater(); + ~nsRangeUpdater(); + + void* RegisterRange(nsIDOMRange *aRange); + nsCOMPtr ReclaimRange(void *aCookie); + void DropRange(void *aCookie); + void RegisterRangeItem(nsRangeStore *aRangeItem); + void DropRangeItem(nsRangeStore *aRangeItem); + nsresult RegisterSelectionState(nsSelectionState &aSelState); + nsresult DropSelectionState(nsSelectionState &aSelState); + + // editor selection gravity routines. Note that we can't always depend on + // DOM Range gravity to do what we want to the "real" selection. For instance, + // if you move a node, that corresponds to deleting it and reinserting it. + // DOM Range gravity will promote the selection out of the node on deletion, + // which is not what you want if you know you are reinserting it. + nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition); + nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition); + nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset); + nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode); + nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRInt32 aOldLeftNodeLength); + nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); + nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); + // the following gravity routines need will/did sandwiches, because the other gravity + // routines will be called inside of these sandwiches, but should be ignored. + nsresult WillReplaceContainer(); + nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode); + nsresult WillRemoveContainer(); + nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen); + nsresult WillInsertContainer(); + nsresult DidInsertContainer(); + nsresult WillMoveNode(); + nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset); + protected: + nsVoidArray mArray; + PRBool mLock; +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidReplaceContainer() + */ + +class nsAutoReplaceContainerSelNotify +{ + private: + nsRangeUpdater &mRU; + nsIDOMNode *mOriginalNode; + nsIDOMNode *mNewNode; + + public: + nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) : + mRU(aRangeUpdater) + ,mOriginalNode(aOriginalNode) + ,mNewNode(aNewNode) + { + mRU.WillReplaceContainer(); + } + + ~nsAutoReplaceContainerSelNotify() + { + mRU.DidReplaceContainer(mOriginalNode, mNewNode); + } +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidRemoveContainer() + */ + +class nsAutoRemoveContainerSelNotify +{ + private: + nsRangeUpdater &mRU; + nsIDOMNode *mNode; + nsIDOMNode *mParent; + PRInt32 mOffset; + PRUint32 mNodeOrigLen; + + public: + nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater, + nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRUint32 aNodeOrigLen) : + mRU(aRangeUpdater) + ,mNode(aNode) + ,mParent(aParent) + ,mOffset(aOffset) + ,mNodeOrigLen(aNodeOrigLen) + { + mRU.WillRemoveContainer(); + } + + ~nsAutoRemoveContainerSelNotify() + { + mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen); + } +}; + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidInsertContainer() + */ + +class nsAutoInsertContainerSelNotify +{ + private: + nsRangeUpdater &mRU; + + public: + nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) : + mRU(aRangeUpdater) + { + mRU.WillInsertContainer(); + } + + ~nsAutoInsertContainerSelNotify() + { + mRU.DidInsertContainer(); + } +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidMoveNode() + */ + +class nsAutoMoveNodeSelNotify +{ + private: + nsRangeUpdater &mRU; + nsIDOMNode *mOldParent; + nsIDOMNode *mNewParent; + PRInt32 mOldOffset; + PRInt32 mNewOffset; + + public: + nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater, + nsIDOMNode *aOldParent, + PRInt32 aOldOffset, + nsIDOMNode *aNewParent, + PRInt32 aNewOffset) : + mRU(aRangeUpdater) + ,mOldParent(aOldParent) + ,mNewParent(aNewParent) + ,mOldOffset(aOldOffset) + ,mNewOffset(aNewOffset) + { + mRU.WillMoveNode(); + } + + ~nsAutoMoveNodeSelNotify() + { + mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset); + } +}; + +#endif + + diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index ab8f8df4e4c..844031ae288 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -48,7 +48,7 @@ static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ - if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \ + if ((mFlags & nsIPlaintextEditor::eEditorReadonlyMask) || (mFlags & nsIPlaintextEditor::eEditorDisabledMask)) \ { \ *aCancel = PR_TRUE; \ return NS_OK; \ @@ -101,7 +101,7 @@ NS_IMPL_QUERY_INTERFACE1(nsTextEditRules, nsIEditRules) ********************************************************/ NS_IMETHODIMP -nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) +nsTextEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags) { if (!aEditor) { return NS_ERROR_NULL_POINTER; } @@ -125,10 +125,8 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) if (NS_FAILED(res)) return res; // create a range that is the entire body contents - nsCOMPtr wholeDoc; - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(wholeDoc)); - if (NS_FAILED(res)) return res; + nsCOMPtr wholeDoc = do_CreateInstance(kRangeCID); + if (!wholeDoc) return NS_ERROR_NULL_POINTER; wholeDoc->SetStart(mBody,0); nsCOMPtr list; res = mBody->GetChildNodes(getter_AddRefs(list)); @@ -163,11 +161,9 @@ nsTextEditRules::SetFlags(PRUint32 aFlags) // SetFlags() is really meant to only be called once // and at editor init time. - PRBool willBePlaintext = (aFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0; - PRBool alreadyPlaintext = (mFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0; - + PRBool willBePlaintext = (aFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; + PRBool alreadyPlaintext = (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; mFlags = aFlags; - return NS_OK; } @@ -207,8 +203,6 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) res = CreateBogusNodeIfNeeded(selection); if (NS_FAILED(res)) return res; - // create moz-br and adjust selection if needed - res = AdjustSelection(selection, aDirection); } return res; } @@ -340,42 +334,7 @@ nsTextEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel) mBogusNode = do_QueryInterface(nsnull); } - // this next only works for collapsed selections right now, - // because selection is a pain to work with when not collapsed. - // (no good way to extend start or end of selection) - PRBool bCollapsed; - nsresult res = aSelection->GetIsCollapsed(&bCollapsed); - if (NS_FAILED(res)) return res; - if (!bCollapsed) return NS_OK; - - // if we are after a mozBR in the same block, then move selection - // to be before it - nsCOMPtr selNode, priorNode; - PRInt32 selOffset; - // get the (collapsed) selection location - res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); - if (NS_FAILED(res)) return res; - // get prior node - res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(priorNode)); - if (NS_SUCCEEDED(res) && priorNode && nsHTMLEditUtils::IsMozBR(priorNode)) - { - nsCOMPtr block1, block2; - if (mEditor->IsBlockNode(selNode)) block1 = selNode; - else block1 = mEditor->GetBlockNodeParent(selNode); - block2 = mEditor->GetBlockNodeParent(priorNode); - - if (block1 != block2) return NS_OK; - - // if we are here then the selection is right after a mozBR - // that is in the same block as the selection. We need to move - // the selection start to be before the mozBR. - res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode,selOffset); - if (NS_FAILED(res)) return res; - } - - return res; + return NS_OK; } nsresult @@ -384,44 +343,13 @@ nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult) return NS_OK; } -nsresult -nsTextEditRules::GetTopEnclosingPre(nsIDOMNode *aNode, - nsIDOMNode** aOutPreNode) -{ - // check parms - if (!aNode || !aOutPreNode) - return NS_ERROR_NULL_POINTER; - *aOutPreNode = 0; - - nsresult res = NS_OK; - nsCOMPtr node, parentNode; - node = do_QueryInterface(aNode); - - while (node) - { - nsAutoString tag; - nsEditor::GetTagString(node, tag); - if (tag.EqualsWithConversion("pre", PR_TRUE)) - *aOutPreNode = node; - else if (tag.EqualsWithConversion("body", PR_TRUE)) - break; - - res = node->GetParentNode(getter_AddRefs(parentNode)); - if (NS_FAILED(res)) return res; - node = parentNode; - } - - NS_IF_ADDREF(*aOutPreNode); - return res; -} - nsresult nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled) { if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } CANCEL_OPERATION_IF_READONLY_OR_DISABLED *aHandled = PR_FALSE; - if (mFlags & nsIHTMLEditor::eEditorSingleLineMask) { + if (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) { *aCancel = PR_TRUE; } else @@ -444,72 +372,6 @@ nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo // we want to ignore result of WillInsert() *aCancel = PR_FALSE; - // Mail rule: split any
 tags in the way,
-    // since they're probably quoted text.
-    // For now, do this for all plaintext since mail is our main customer
-    // and we don't currently set eEditorMailMask for plaintext mail.
-    if (mTheAction != nsHTMLEditor::kOpInsertQuotation) // && mFlags & nsIHTMLEditor::eEditorMailMask)
-    {
-      nsCOMPtr preNode, selNode;
-      PRInt32 selOffset, newOffset;
-      res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
-      if (NS_FAILED(res)) return res;
-
-      // If any of the following fail, then just proceed with the
-      // normal break insertion without worrying about the error
-      res = GetTopEnclosingPre(selNode, getter_AddRefs(preNode));
-      if (NS_SUCCEEDED(res) && preNode)
-      {
-        // Only split quote nodes: see if it has the attribute _moz_quote
-        nsCOMPtr preElement (do_QueryInterface(preNode));
-        if (preElement)
-        {
-          nsString mozQuote; mozQuote.AssignWithConversion("_moz_quote");
-          nsString mozQuoteVal;
-          PRBool isMozQuote = PR_FALSE;
-          if (NS_SUCCEEDED(mEditor->GetAttributeValue(preElement, mozQuote,
-                                                      mozQuoteVal, isMozQuote))
-              && isMozQuote)
-          {
-            printf("It's a moz quote -- splitting\n");
-            nsCOMPtr outLeftNode;
-            nsCOMPtr outRightNode;
-            res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, PR_TRUE, address_of(outLeftNode), address_of(outRightNode));
-            if (NS_FAILED(res)) return res;
-            PRBool bIsEmptyNode;
-            
-            // rememeber parent of selNode now, since we might delete selNode below
-            res = preNode->GetParentNode(getter_AddRefs(selNode));
-            if (NS_FAILED(res)) return res;
-            
-            if (outLeftNode)
-            {
-              res = mEditor->IsEmptyNode(outLeftNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
-              if (NS_FAILED(res)) return res;
-              if (bIsEmptyNode) mEditor->DeleteNode(outLeftNode);
-            }
-            if (outRightNode)
-            {
-              // HACK alert: consume a br if there is one at front of node
-              nsCOMPtr firstNode;
-              res =  mEditor->GetFirstEditableNode(outRightNode, address_of(firstNode));
-              if (firstNode &&  nsHTMLEditUtils::IsBreak(firstNode))
-              {
-                mEditor->DeleteNode(firstNode);
-              }
-                
-              res = mEditor->IsEmptyNode(outRightNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
-              if (NS_FAILED(res)) return res;
-              if (bIsEmptyNode) mEditor->DeleteNode(outRightNode);
-            }
-            nsCOMPtr brNode;
-            // last ePrevious param causes selection to be set before the break
-            res = mEditor->CreateBR(selNode, newOffset, address_of(brNode), nsIEditor::ePrevious);
-            *aHandled = PR_TRUE;
-          }
-        }
-      }
-    }  
   }
   return NS_OK;
 }
@@ -519,42 +381,44 @@ nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
 {
   // we only need to execute the stuff below if we are a plaintext editor.
   // html editors have a different mechanism for putting in mozBR's
-  // (because there are a bunch more placesyou have to worry about it in html) 
-  if (!nsIHTMLEditor::eEditorPlaintextMask & mFlags) return NS_OK;
+  // (because there are a bunch more places you have to worry about it in html) 
+  if (!nsIPlaintextEditor::eEditorPlaintextMask & mFlags) return NS_OK;
 
   // if we are at the end of the document, we need to insert 
   // a special mozBR following the normal br, and then set the
   // selection to stick to the mozBR.
   PRInt32 selOffset;
-  nsCOMPtr nearNode, selNode;
+  nsCOMPtr nearNode, selNode, root, temp;
+  nsCOMPtr rootElem;
   nsresult res;
   res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
   if (NS_FAILED(res)) return res;
-  res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
+  // confirm we are at end of document
+  if (selOffset == 0) return NS_OK;  // cant be after a br if we are at offset 0
+  res = mEditor->GetRootElement(getter_AddRefs(rootElem));
   if (NS_FAILED(res)) return res;
+  root = do_QueryInterface(rootElem);
+  if (!root) return NS_ERROR_NULL_POINTER;
+  if (selNode != root) return NS_OK; // must be inside text node or somewhere other than end of root
+  temp = mEditor->GetChildAt(selNode, selOffset);
+  if (temp) return NS_OK; // cant be at end of there is a node after us.
+  nearNode = mEditor->GetChildAt(selNode, selOffset-1);
   if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) && !nsHTMLEditUtils::IsMozBR(nearNode))
   {
-    PRBool bIsLast;
-    res = mEditor->IsLastEditableChild(nearNode, &bIsLast);
+    nsCOMPtr sel(aSelection);
+    nsCOMPtrselPrivate(do_QueryInterface(sel));
+    // need to insert special moz BR. Why?  Because if we don't
+    // the user will see no new line for the break.  Also, things
+    // like table cells won't grow in height.
+    nsCOMPtr brNode;
+    res = CreateMozBR(selNode, selOffset, address_of(brNode));
+    if (NS_FAILED(res)) return res;
+    res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
+    if (NS_FAILED(res)) return res;
+    selPrivate->SetInterlinePosition(PR_TRUE);
+    res = aSelection->Collapse(selNode,selOffset);
     if (NS_FAILED(res)) return res;
-    if (bIsLast)
-    {
-      nsCOMPtr sel(aSelection);
-      nsCOMPtrselPrivate(do_QueryInterface(sel));
-      // need to insert special moz BR. Why?  Because if we don't
-      // the user will see no new line for the break.  Also, things
-      // like table cells won't grow in height.
-      nsCOMPtr brNode;
-      res = CreateMozBR(selNode, selOffset, address_of(brNode));
-      if (NS_FAILED(res)) return res;
-      res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
-      if (NS_FAILED(res)) return res;
-      selPrivate->SetInterlinePosition(PR_TRUE);
-      res = aSelection->Collapse(selNode,selOffset);
-      if (NS_FAILED(res)) return res;
-    }
   }
-//  mEditor->DumpContentTree();
   return res;
 }
 
@@ -595,7 +459,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
   if (NS_FAILED(res)) return res;
   
   // handle password field docs
-  if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
     NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
@@ -621,7 +485,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
   // handle password field data
   // this has the side effect of changing all the characters in aOutString
   // to the replacement character
-  if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     res = EchoInsertionToPWBuff(start, end, outString);
     if (NS_FAILED(res)) return res;
@@ -646,7 +510,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
     rv = prefs->GetIntPref("editor.singleLine.pasteNewlines",
                            &singleLineNewlineBehavior);
 
-  if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
+  if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
   {
     if (singleLineNewlineBehavior == eReplaceWithSpaces)
       outString->ReplaceChar(CRLF, ' ');
@@ -731,7 +595,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
         // is it a return?
         if (subStr.EqualsWithConversion("\n"))
         {
-          if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
+          if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
           {
             NS_ASSERTION((singleLineNewlineBehavior == ePasteIntact),
                   "Newline improperly getting into single-line edit field!");
@@ -814,7 +678,7 @@ nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, PRBool *aCancel,
   nsresult res = NS_OK;
 
   // XXX: should probably return a success value other than NS_OK that means "not allowed"
-  if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
+  if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
     *aCancel = PR_TRUE;
   }
   return res;
@@ -834,7 +698,7 @@ nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, PRBool *aCance
   nsresult res = NS_OK;
 
   // XXX: should probably return a success value other than NS_OK that means "not allowed"
-  if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
+  if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
     *aCancel = PR_TRUE;
   }
   return res;
@@ -864,7 +728,7 @@ nsTextEditRules::WillDeleteSelection(nsISelection *aSelection,
     *aCancel = PR_TRUE;
     return NS_OK;
   }
-  if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     // manage the password buffer
     PRInt32 start, end;
@@ -1098,7 +962,7 @@ nsTextEditRules::WillOutputText(nsISelection *aSelection,
 
   if (PR_TRUE == aOutputFormat->EqualsWithConversion("text/plain"))
   { // only use these rules for plain text output
-    if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+    if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
     {
       *aOutString = mPasswordText;
       *aHandled = PR_TRUE;
@@ -1288,7 +1152,7 @@ nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection,
   nsresult res = NS_OK;
   *aOutString = *aInString;
   
-  if ((-1 != aMaxLength) && (mFlags & nsIHTMLEditor::eEditorPlaintextMask))
+  if ((-1 != aMaxLength) && (mFlags & nsIPlaintextEditor::eEditorPlaintextMask))
   {
     // Get the current text length.
     // Get the length of inString.
@@ -1388,69 +1252,3 @@ nsTextEditRules::DeleteEmptyTextNode(nsIDOMNode *aNode)
   return PR_FALSE;
 }
 
-
-nsresult 
-nsTextEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection)
-{
-  if (!aSelection) return NS_ERROR_NULL_POINTER;
-  
-  // if the selection isn't collapsed, do nothing.
-  PRBool bCollapsed;
-  nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
-  if (NS_FAILED(res)) return res;
-  if (!bCollapsed) return res;
-
-  // get the (collapsed) selection location
-  nsCOMPtr selNode, temp;
-  PRInt32 selOffset;
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
-  if (NS_FAILED(res)) return res;
-  temp = selNode;
-  
-  // are we in an editable node?
-  while (!mEditor->IsEditable(selNode))
-  {
-    // scan up the tree until we find an editable place to be
-    res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset);
-    if (NS_FAILED(res)) return res;
-    if (!selNode) return NS_ERROR_FAILURE;
-    temp = selNode;
-  }
-  
-  // are we in a text node? 
-  nsCOMPtr textNode = do_QueryInterface(selNode);
-  if (textNode) 
-    return NS_OK; // we LIKE it when we are in a text node.  that RULZ
-  
-  // do we need to insert a special mozBR?  We do if we are:
-  // 1) after a block element AND
-  // 2) at the end of the body OR before another block
-
-  nsCOMPtr priorNode, nextNode;
-  res = mEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(priorNode));
-  if (NS_FAILED(res)) return res;
-  res = mEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nextNode));
-  if (NS_FAILED(res)) return res;
-  
-  // is priorNode a block?
-  if (priorNode && mEditor->IsBlockNode(priorNode)) 
-  {
-    if (!nextNode || mEditor->IsBlockNode(nextNode))
-    {
-      nsCOMPtr sel(aSelection);
-      nsCOMPtrselPrivate(do_QueryInterface(sel));
-
-      nsCOMPtr brNode;
-      res = CreateMozBR(selNode, selOffset, address_of(brNode));
-      if (NS_FAILED(res)) return res;
-      res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
-      if (NS_FAILED(res)) return res;
-      // selection stays *before* moz-br, sticking to it
-      selPrivate->SetInterlinePosition(PR_TRUE);
-      res = aSelection->Collapse(selNode,selOffset);
-      if (NS_FAILED(res)) return res;
-    }
-  }
-  return res;
-}
-
diff --git a/mozilla/editor/base/nsTextEditRules.h b/mozilla/editor/base/nsTextEditRules.h
index def80ad9dad..8e295d7d14f 100644
--- a/mozilla/editor/base/nsTextEditRules.h
+++ b/mozilla/editor/base/nsTextEditRules.h
@@ -25,7 +25,7 @@
 
 #include "nsCOMPtr.h"
 
-#include "nsHTMLEditor.h"
+#include "nsPlaintextEditor.h"
 #include "nsIDOMNode.h"
 
 #include "nsEditRules.h"
@@ -50,7 +50,7 @@ public:
   virtual     ~nsTextEditRules();
 
   // nsIEditRules methods
-  NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags);
+  NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
@@ -163,17 +163,15 @@ protected:
 
   PRBool DeleteEmptyTextNode(nsIDOMNode *aNode);
 
-  nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection);
-  
   // data members
-  nsHTMLEditor *mEditor;  // note that we do not refcount the editor
-  nsString      mPasswordText;  // a buffer we use to store the real value of password editors
-  nsCOMPtr mBogusNode;  // magic node acts as placeholder in empty doc
-  nsCOMPtr mBody;    // cached root node
-  PRUint32 mFlags;
-  PRUint32 mActionNesting;
-  PRBool   mLockRulesSniffing;
-  PRInt32  mTheAction;    // the top level editor action
+  nsPlaintextEditor   *mEditor;        // note that we do not refcount the editor
+  nsString             mPasswordText;  // a buffer we use to store the real value of password editors
+  nsCOMPtr mBogusNode;     // magic node acts as placeholder in empty doc
+  nsCOMPtr mBody;          // cached root node
+  PRUint32             mFlags;
+  PRUint32             mActionNesting;
+  PRBool               mLockRulesSniffing;
+  PRInt32              mTheAction;     // the top level editor action
   // friends
   friend class nsAutoLockRulesSniffing;
 
diff --git a/mozilla/editor/base/nsTextEditorReg.cpp b/mozilla/editor/base/nsTextEditorReg.cpp
new file mode 100644
index 00000000000..6a79e2d8de6
--- /dev/null
+++ b/mozilla/editor/base/nsTextEditorReg.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s): 
+ */
+
+#include "nsIGenericFactory.h"
+
+#include "nsEditorCID.h"
+#include "nsEditor.h"           // for gInstanceCount
+#include "nsPlaintextEditor.h"
+#include "nsEditorService.h" 
+#include "nsEditorController.h" //CID
+
+////////////////////////////////////////////////////////////////////////
+// Define the contructor function for the objects
+//
+// NOTE: This creates an instance of objects by using the default constructor
+//
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
+
+////////////////////////////////////////////////////////////////////////
+// Define a table of CIDs implemented by this module along with other
+// information like the function to create an instance, contractid, and
+// class name.
+//
+static nsModuleComponentInfo components[] = {
+    { "Text Editor", NS_TEXTEDITOR_CID,
+      "@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
+    { "Editor Controller", NS_EDITORCONTROLLER_CID,
+      "@mozilla.org/editor/editorcontroller;1", nsEditorControllerConstructor, }
+};
+
+////////////////////////////////////////////////////////////////////////
+// Implement the NSGetModule() exported function for your module
+// and the entire implementation of the module object.
+//
+NS_IMPL_NSGETMODULE("nsEditorModule", components)
diff --git a/mozilla/editor/composer/src/nsEditorShell.cpp b/mozilla/editor/composer/src/nsEditorShell.cpp
index a66e2df3842..88ded6ceb97 100644
--- a/mozilla/editor/composer/src/nsEditorShell.cpp
+++ b/mozilla/editor/composer/src/nsEditorShell.cpp
@@ -910,7 +910,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
   {
     if (mEditorTypeString.EqualsWithConversion("text"))
     {
-      err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorPlaintextMask | nsIHTMLEditor::eEditorEnableWrapHackMask);
+      err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorPlaintextMask | nsIPlaintextEditor::eEditorEnableWrapHackMask);
       mEditorType = ePlainTextEditorType;
     }
     else if (mEditorTypeString.EqualsWithConversion("html") || mEditorTypeString.IsEmpty())  // empty string default to HTML editor
@@ -920,7 +920,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
     }
     else if (mEditorTypeString.EqualsWithConversion("htmlmail"))  //  HTML editor with special mail rules
     {
-      err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorMailMask);
+      err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorMailMask);
       mEditorType = eHTMLTextEditorType;
     }
     else
@@ -3423,7 +3423,7 @@ nsEditorShell::GetDocumentEditable(PRBool *aDocumentEditable)
   PRUint32  editorFlags;
   editor->GetFlags(&editorFlags);
   
-  if (editorFlags & nsIHTMLEditor::eEditorReadonlyMask)
+  if (editorFlags & nsIPlaintextEditor::eEditorReadonlyMask)
     return NS_OK;
   
   nsCOMPtr doc;
diff --git a/mozilla/editor/idl/nsIPlaintextEditor.idl b/mozilla/editor/idl/nsIPlaintextEditor.idl
index 54b19021c5c..c40bbeae8cd 100644
--- a/mozilla/editor/idl/nsIPlaintextEditor.idl
+++ b/mozilla/editor/idl/nsIPlaintextEditor.idl
@@ -31,6 +31,32 @@
 [scriptable, uuid(b5f39ed4-1dd1-11b2-9d00-fd54d6f54962)]
 interface nsIPlaintextEditor : nsISupports
 {
+
+  /* the bits in an editor behavior mask. */
+  const short eEditorPlaintextBit = 0;                  /* only plain text entry is allowed via events */
+  const short eEditorSingleLineBit = 1;                 /* enter key and CR-LF handled specially */
+  const short eEditorPasswordBit = 2;                   /* text is not entered into content, only a representative character */
+  const short eEditorReadonlyBit = 3;                   /* editing events are disabled.  Editor may still accept focus. */
+  const short eEditorDisabledBit = 4;                   /* all events are disabled (like scrolling).  Editor will not accept focus. */
+  const short eEditorFilterInputBit = 5;                /* text input is limited to certain character types, use mFilter */
+  const short eEditorMailBit = 6;                       /* use mail-compose editting rules */
+  const short eEditorDisableForcedUpdatesBit = 7;       /* prevent immediate view refreshes */
+  const short eEditorDisableForcedReflowsBit = 8;       /* prevent immediate reflows */
+  const short eEditorEnableWrapHackBit = 9;             /* allow the editor to set font: monospace on the root node */
+  const short eEditorWidgetBit = 10;                    /* bit for widgets */
+
+  const long eEditorPlaintextMask            = 1;
+  const long eEditorSingleLineMask           = 2;
+  const long eEditorPasswordMask             = 4;
+  const long eEditorReadonlyMask             = 8;
+  const long eEditorDisabledMask             = 16;
+  const long eEditorFilterInputMask          = 32;
+  const long eEditorMailMask                 = 64;
+  const long eEditorDisableForcedUpdatesMask = 128;
+  const long eEditorDisableForcedReflowsMask = 256;
+  const long eEditorEnableWrapHackMask       = 512;
+  const long eEditorWidgetMask               = 1024;
+  
   /**
     * The length of the contents in characters.
     */
diff --git a/mozilla/editor/libeditor/base/nsEditRules.h b/mozilla/editor/libeditor/base/nsEditRules.h
index 2af710fd2dc..c3b8223de97 100644
--- a/mozilla/editor/libeditor/base/nsEditRules.h
+++ b/mozilla/editor/libeditor/base/nsEditRules.h
@@ -28,7 +28,7 @@
 0xa6cf911d, 0x15b3, 0x11d2, \
 {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
 
-class nsHTMLEditor;
+class nsPlaintextEditor;
 class nsISelection;
 
 /***************************************************************************
@@ -57,7 +57,7 @@ public:
 //Interfaces for addref and release and queryinterface
 //NOTE: Use   NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
 
-  NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags)=0;
+  NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)=0;
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
   NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
   NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0;
diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp
index c12b2d45e6a..7a21c310d40 100644
--- a/mozilla/editor/libeditor/base/nsEditor.cpp
+++ b/mozilla/editor/libeditor/base/nsEditor.cpp
@@ -59,6 +59,7 @@
 #include "nsIKBStateControl.h"
 #include "nsIWidget.h"
 #include "nsIScrollbar.h"
+#include "nsIPlaintextEditor.h"
 
 #include "nsIFrame.h"  // Needed by IME code
 
@@ -93,7 +94,7 @@
 #include "JoinElementTxn.h"
 #include "nsStyleSheetTxns.h"
 #include "IMETextTxn.h"
-#include "nsIHTMLEditor.h"
+
 // included for nsEditor::CreateHTMLContent
 #include "nsIElementFactory.h"
 #include "nsINodeInfo.h"
@@ -148,738 +149,6 @@ const PRUnichar nbsp = 160;
 PRInt32 nsEditor::gInstanceCount = 0;
 
 
-/***************************************************************************
- * class for recording selection info.  stores selection as collection of
- * { {startnode, startoffset} , {endnode, endoffset} } tuples.  Cant store
- * ranges since dom gravity will possibly change the ranges.
- */
-nsSelectionState::nsSelectionState() : mArray(){}
-
-nsSelectionState::~nsSelectionState() 
-{
-  MakeEmpty();
-}
-
-nsresult  
-nsSelectionState::SaveSelection(nsISelection *aSel)
-{
-  if (!aSel) return NS_ERROR_NULL_POINTER;
-  nsresult res = NS_OK;
-  PRInt32 i,rangeCount, arrayCount = mArray.Count();
-  nsRangeStore *item;
-  aSel->GetRangeCount(&rangeCount);
-  
-  // if we need more items in the array, new them
-  if (arrayCountarrayCount)
-  {
-    while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
-    {
-      delete item;
-      mArray.RemoveElementAt(rangeCount);
-    }
-  }
-  
-  // now store the selection ranges
-  for (i=0; i range;
-    res = aSel->GetRangeAt(i, getter_AddRefs(range));
-    item->StoreRange(range);
-  }
-  
-  return res;
-}
-
-nsresult  
-nsSelectionState::RestoreSelection(nsISelection *aSel)
-{
-  if (!aSel) return NS_ERROR_NULL_POINTER;
-  nsresult res = NS_OK;
-  PRInt32 i, arrayCount = mArray.Count();
-  nsRangeStore *item;
-
-  // clear out selection
-  aSel->RemoveAllRanges();
-  
-  // set the selection ranges anew
-  for (i=0; i range;
-    item->GetRange(address_of(range));
-    if (!range) return NS_ERROR_UNEXPECTED;
-   
-    res = aSel->AddRange(range);
-    if(NS_FAILED(res)) return res;
-
-  }
-  return NS_OK;
-}
-
-PRBool
-nsSelectionState::IsCollapsed()
-{
-  if (1 != mArray.Count()) return PR_FALSE;
-  nsRangeStore *item;
-  item = (nsRangeStore*)mArray.ElementAt(0);
-  if (!item) return PR_FALSE;
-  nsCOMPtr range;
-  item->GetRange(address_of(range));
-  if (!range) return PR_FALSE;
-  PRBool bIsCollapsed;
-  range->GetCollapsed(&bIsCollapsed);
-  return bIsCollapsed;
-}
-
-PRBool
-nsSelectionState::IsEqual(nsSelectionState *aSelState)
-{
-  if (!aSelState) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count();
-  if (myCount != itsCount) return PR_FALSE;
-  if (myCount < 1) return PR_FALSE;
-
-  nsRangeStore *myItem, *itsItem;
-  
-  for (i=0; imArray.ElementAt(0));
-    if (!myItem || !itsItem) return PR_FALSE;
-    
-    nsCOMPtr myRange, itsRange;
-    myItem->GetRange(address_of(myRange));
-    itsItem->GetRange(address_of(itsRange));
-    if (!myRange || !itsRange) return PR_FALSE;
-  
-    PRInt32 compResult;
-    myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
-    if (compResult) return PR_FALSE;
-    myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
-    if (compResult) return PR_FALSE;
-  }
-  // if we got here, they are equal
-  return PR_TRUE;
-}
-
-void     
-nsSelectionState::MakeEmpty()
-{
-  // free any items in the array
-  nsRangeStore *item;
-  while ((item = (nsRangeStore*)mArray.ElementAt(0)))
-  {
-    delete item;
-    mArray.RemoveElementAt(0);
-  }
-}
-
-PRBool   
-nsSelectionState::IsEmpty()
-{
-  return (mArray.Count() == 0);
-}
-
-/***************************************************************************
- * nsRangeUpdater:  class for updating nsIDOMRanges in response to editor actions.
- */
-
-nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
-
-nsRangeUpdater::~nsRangeUpdater()
-{
-  // free any items in the array
-  nsRangeStore *item;
-  while ((item = (nsRangeStore*)mArray.ElementAt(0)))
-  {
-    delete item;
-    mArray.RemoveElementAt(0);
-  }
-}
-  
-void* 
-nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
-{
-  nsRangeStore *item = new nsRangeStore;
-  if (!item) return nsnull;
-  item->StoreRange(aRange);
-  mArray.AppendElement(item);
-  return item;
-}
-
-nsCOMPtr 
-nsRangeUpdater::ReclaimRange(void *aCookie)
-{
-  nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
-  if (!item) return nsnull;
-  nsCOMPtr outRange;
-  item->GetRange(address_of(outRange));
-  mArray.RemoveElement(aCookie);
-  delete item;
-  return outRange;
-}
-    
-void 
-nsRangeUpdater::DropRange(void *aCookie)
-{
-  nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
-  if (!item) return;
-  mArray.RemoveElement(aCookie);
-  delete item;
-}
-
-void 
-nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
-{
-  if (!aRangeItem) return;
-  mArray.AppendElement(aRangeItem);
-  return;
-}
-
-void 
-nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
-{
-  if (!aRangeItem) return;
-  mArray.RemoveElement(aRangeItem);
-  return;
-}
-
-nsresult 
-nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
-{
-  PRInt32 i, theCount = aSelState.mArray.Count();
-  if (theCount < 1) return NS_ERROR_FAILURE;
-
-  nsRangeStore *item;
-  
-  for (i=0; istartNode.get() == aParent) && (item->startOffset > aPosition))
-      item->startOffset++;
-    if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
-      item->endOffset++;
-  }
-  return NS_OK;
-}
-
-nsresult
-nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
-{
-  return SelAdjCreateNode(aParent, aPosition);
-}
-
-
-nsresult
-nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
-{
-  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
-  if (!aNode) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, count = mArray.Count();
-  if (!count) return NS_OK;
-
-  nsRangeStore *item;
-  
-  for (i=0; istartNode.get() == aParent) && (item->startOffset > aOffset))
-      item->startOffset--;
-    if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
-      item->endOffset--;
-  }
-  // MOOSE: also check inside of aNode, expensive.  But in theory, we shouldn't
-  // actually hit this case in the usage i forsee for this.
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
-{
-  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
-  if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, count = mArray.Count();
-  if (!count) return NS_OK;
-
-  nsCOMPtr parent;
-  PRInt32 offset;
-  nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset);
-  if (NS_FAILED(result)) return result;
-  
-  // first part is same as inserting aNewLeftnode
-  result = SelAdjInsertNode(parent,offset-1);
-  if (NS_FAILED(result)) return result;
-
-  // next step is to check for range enpoints inside aOldRightNode
-  nsRangeStore *item;
-  
-  for (i=0; istartNode.get() == aOldRightNode)
-    {
-      if (item->startOffset > aOffset)
-      {
-        item->startOffset -= aOffset;
-      }
-      else
-      {
-        item->startNode = aNewLeftNode;
-      }
-    }
-    if (item->endNode.get() == aOldRightNode)
-    {
-      if (item->endOffset > aOffset)
-      {
-        item->endOffset -= aOffset;
-      }
-      else
-      {
-        item->endNode = aNewLeftNode;
-      }
-    }
-  }
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode, 
-                                  nsIDOMNode *aRightNode, 
-                                  nsIDOMNode *aParent, 
-                                  PRInt32 aOffset,
-                                  PRInt32 aOldLeftNodeLength)
-{
-  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
-  if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, count = mArray.Count();
-  if (!count) return NS_OK;
-
-  nsRangeStore *item;
-
-  for (i=0; istartNode.get() == aParent)
-    {
-      if (item->startOffset > aOffset)
-      {
-        item->startOffset--;
-      }
-      else if (item->startOffset == aOffset)
-      {
-        // join keeps right hand node
-        item->startNode = aRightNode;
-        item->startOffset = aOldLeftNodeLength;
-      }
-    }
-    if (item->endNode.get() == aParent)
-    {
-      if (item->endOffset > aOffset)
-      {
-        item->endOffset--;
-      }
-      else if (item->endOffset == aOffset)
-      {
-        // join keeps right hand node
-        item->endNode = aRightNode;
-        item->endOffset = aOldLeftNodeLength;
-      }
-    }
-    // adjust endpoints in aRightNode
-    if (item->startNode.get() == aRightNode)
-      item->startOffset += aOldLeftNodeLength;
-    if (item->endNode.get() == aRightNode)
-      item->endOffset += aOldLeftNodeLength;
-  }
-  
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
-{
-  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
-{
-  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::WillReplaceContainer()
-{
-  if (mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_TRUE;
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
-{
-  if (!mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_FALSE;
-
-  if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, count = mArray.Count();
-  if (!count) return NS_OK;
-
-  nsRangeStore *item;
-  
-  for (i=0; istartNode.get() == aOriginalNode)
-      item->startNode = aNewNode;
-    if (item->endNode.get() == aOriginalNode)
-      item->endNode = aNewNode;
-  }
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::WillRemoveContainer()
-{
-  if (mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_TRUE;
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
-{
-  if (!mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_FALSE;
-
-  if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, count = mArray.Count();
-  if (!count) return NS_OK;
-
-  nsRangeStore *item;
-  
-  for (i=0; istartNode.get() == aNode)
-    {
-      item->startNode = aParent;
-      item->startOffset += aOffset;
-    }
-    if (item->endNode.get() == aNode)
-    {
-      item->endNode = aParent;
-      item->endOffset += aOffset;
-    }
-    if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
-      item->startOffset += (PRInt32)aNodeOrigLen-1;
-    if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
-      item->endOffset += (PRInt32)aNodeOrigLen-1;
-  }
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::WillInsertContainer()
-{
-  if (mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_TRUE;
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::DidInsertContainer()
-{
-  if (!mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_FALSE;
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::WillMoveNode()
-{
-  if (mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_TRUE;
-  return NS_OK;
-}
-
-
-nsresult
-nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
-{
-  if (!mLock) return NS_ERROR_UNEXPECTED;  
-  mLock = PR_FALSE;
-
-  if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
-  PRInt32 i, count = mArray.Count();
-  if (!count) return NS_OK;
-
-  nsRangeStore *item;
-  
-  for (i=0; istartNode.get() == aOldParent) && (item->startOffset > aOldOffset))
-      item->startOffset--;
-    if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
-      item->endOffset--;
-      
-    // and like an insert in aNewParent
-    if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
-      item->startOffset++;
-    if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
-      item->endOffset++;
-  }
-  return NS_OK;
-}
-
-
-
-/***************************************************************************
- * helper class for nsSelectionState.  nsRangeStore stores range endpoints.
- */
-
-  // DEBUG: PRInt32 nsRangeStore::n = 0;
-
-nsRangeStore::nsRangeStore() 
-{ 
-  // DEBUG: n++;  printf("range store alloc count=%d\n", n); 
-}
-nsRangeStore::~nsRangeStore()
-{
-  // DEBUG: n--;  printf("range store alloc count=%d\n", n); 
-}
-
-nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
-{
-  if (!aRange) return NS_ERROR_NULL_POINTER;
-  aRange->GetStartContainer(getter_AddRefs(startNode));
-  aRange->GetEndContainer(getter_AddRefs(endNode));
-  aRange->GetStartOffset(&startOffset);
-  aRange->GetEndOffset(&endOffset);
-  return NS_OK;
-}
-
-nsresult nsRangeStore::GetRange(nsCOMPtr *outRange)
-{
-  if (!outRange) return NS_ERROR_NULL_POINTER;
-  nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
-                             nsnull,
-                             NS_GET_IID(nsIDOMRange),
-                             getter_AddRefs(*outRange));
-  if(NS_FAILED(res)) return res;
-
-  res = (*outRange)->SetStart(startNode, startOffset);
-  if(NS_FAILED(res)) return res;
-
-  res = (*outRange)->SetEnd(endNode, endOffset);
-  return res;
-}
-
-/***************************************************************************
- * another helper class for nsSelectionState.  stack based class for doing
- * Will/DidReplaceContainer()
- */
-
-class nsAutoReplaceContainerSelNotify
-{
-  private:
-    nsRangeUpdater &mRU;
-    nsIDOMNode *mOriginalNode;
-    nsIDOMNode *mNewNode;
-
-  public:
-    nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
-    mRU(aRangeUpdater)
-    ,mOriginalNode(aOriginalNode)
-    ,mNewNode(aNewNode)
-    {
-      mRU.WillReplaceContainer();
-    }
-    
-    ~nsAutoReplaceContainerSelNotify()
-    {
-      mRU.DidReplaceContainer(mOriginalNode, mNewNode);
-    }
-};
-
-
-/***************************************************************************
- * another helper class for nsSelectionState.  stack based class for doing
- * Will/DidRemoveContainer()
- */
-
-class nsAutoRemoveContainerSelNotify
-{
-  private:
-    nsRangeUpdater &mRU;
-    nsIDOMNode *mNode;
-    nsIDOMNode *mParent;
-    PRInt32    mOffset;
-    PRUint32   mNodeOrigLen;
-
-  public:
-    nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater, 
-                                   nsIDOMNode *aNode, 
-                                   nsIDOMNode *aParent, 
-                                   PRInt32 aOffset, 
-                                   PRUint32 aNodeOrigLen) :
-    mRU(aRangeUpdater)
-    ,mNode(aNode)
-    ,mParent(aParent)
-    ,mOffset(aOffset)
-    ,mNodeOrigLen(aNodeOrigLen)
-    {
-      mRU.WillRemoveContainer();
-    }
-    
-    ~nsAutoRemoveContainerSelNotify()
-    {
-      mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
-    }
-};
-
-/***************************************************************************
- * another helper class for nsSelectionState.  stack based class for doing
- * Will/DidInsertContainer()
- */
-
-class nsAutoInsertContainerSelNotify
-{
-  private:
-    nsRangeUpdater &mRU;
-
-  public:
-    nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
-    mRU(aRangeUpdater)
-    {
-      mRU.WillInsertContainer();
-    }
-    
-    ~nsAutoInsertContainerSelNotify()
-    {
-      mRU.DidInsertContainer();
-    }
-};
-
-
-/***************************************************************************
- * another helper class for nsSelectionState.  stack based class for doing
- * Will/DidMoveNode()
- */
-
-class nsAutoMoveNodeSelNotify
-{
-  private:
-    nsRangeUpdater &mRU;
-    nsIDOMNode *mOldParent;
-    nsIDOMNode *mNewParent;
-    PRInt32    mOldOffset;
-    PRInt32    mNewOffset;
-
-  public:
-    nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater, 
-                            nsIDOMNode *aOldParent, 
-                            PRInt32 aOldOffset, 
-                            nsIDOMNode *aNewParent, 
-                            PRInt32 aNewOffset) :
-    mRU(aRangeUpdater)
-    ,mOldParent(aOldParent)
-    ,mNewParent(aNewParent)
-    ,mOldOffset(aOldOffset)
-    ,mNewOffset(aNewOffset)
-    {
-      mRU.WillMoveNode();
-    }
-    
-    ~nsAutoMoveNodeSelNotify()
-    {
-      mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
-    }
-};
-
-
 //---------------------------------------------------------------------------
 //
 // nsEditor: base editor class implementation
@@ -2606,7 +1875,7 @@ nsEditor::ForceCompositionEnd()
 #endif
 
 #ifdef XP_UNIX
-  if(mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if(mFlags & nsIPlaintextEditor::eEditorPasswordMask)
 	return NS_OK;
 #endif
 
@@ -5059,7 +4328,6 @@ nsEditor::IsNextCharWhitespace(nsIDOMNode *aParentNode,
         textNode->GetLength(&strLength);
         if (strLength)
         {
-          // you could use nsITextContent::IsOnlyWhitespace here
           textNode->SubstringData(0,1,tempString);
           *outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
           *outIsNBSP = (tempString.First() == nbsp);
@@ -5391,7 +4659,7 @@ nsresult nsEditor::EndUpdateViewBatch()
 
       PRBool forceReflow = PR_TRUE;
 
-      if (flags & nsIHTMLEditor::eEditorDisableForcedReflowsMask)
+      if (flags & nsIPlaintextEditor::eEditorDisableForcedReflowsMask)
         forceReflow = PR_FALSE;
 
       nsCOMPtr    presShell;
@@ -5401,7 +4669,7 @@ nsresult nsEditor::EndUpdateViewBatch()
 
       PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE;
 
-      if (flags & nsIHTMLEditor::eEditorDisableForcedUpdatesMask)
+      if (flags & nsIPlaintextEditor::eEditorDisableForcedUpdatesMask)
         updateFlag = NS_VMREFRESH_NO_SYNC;
 
 #ifdef HACK_FORCE_REDRAW
@@ -5482,9 +4750,129 @@ nsEditor::DeleteSelectionImpl(nsIEditor::EDirection aAction)
   return res;
 }
 
+// XXX: error handling in this routine needs to be cleaned up!
+NS_IMETHODIMP
+nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag,
+                                           nsIDOMNode ** aNewNode)
+{
+  nsCOMPtr parentSelectedNode;
+  PRInt32 offsetOfNewNode;
+  nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
+                                                          offsetOfNewNode);
+  if (!NS_SUCCEEDED(result))
+    return result;
+
+  nsCOMPtr newNode;
+  result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
+                      getter_AddRefs(newNode));
+  // XXX: ERROR_HANDLING  check result, and make sure aNewNode is set correctly in success/failure cases
+  *aNewNode = newNode;
+  NS_IF_ADDREF(*aNewNode);
+
+  // we want the selection to be just after the new node
+  nsCOMPtr selection;
+  result = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(result)) return result;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+  result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
+
+  return result;
+}
+
 
 /* Non-interface, protected methods */
 
+NS_IMETHODIMP
+nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode)
+{
+  nsresult result=NS_ERROR_NOT_INITIALIZED;
+  nsCOMPtr selection;
+  result = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(result)) return result;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+
+  PRBool collapsed;
+  result = selection->GetIsCollapsed(&collapsed);
+  if (NS_SUCCEEDED(result) && !collapsed) 
+  {
+    result = DeleteSelection(nsIEditor::eNone);
+    if (NS_FAILED(result)) {
+      return result;
+    }
+    // get the new selection
+    result = GetSelection(getter_AddRefs(selection));
+    if (NS_FAILED(result)) {
+      return result;
+    }
+#ifdef NS_DEBUG
+    nsCOMPtrtestSelectedNode;
+    nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
+    // no selection is ok.
+    // if there is a selection, it must be collapsed
+    if (testSelectedNode)
+    {
+      PRBool testCollapsed;
+      debugResult = selection->GetIsCollapsed(&testCollapsed);
+      NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
+      NS_ASSERTION(testCollapsed, "selection not reset after deletion");
+    }
+#endif
+  }
+  // split the selected node
+  PRInt32 offsetOfSelectedNode;
+  result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
+  if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
+  {
+    nsCOMPtr selectedNode;
+    PRUint32 selectedNodeContentCount=0;
+    nsCOMPtrselectedParentNodeAsText;
+    selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
+
+    offsetOfNewNode = offsetOfSelectedNode;
+    
+    /* if the selection is a text node, split the text node if necesary
+       and compute where to put the new node
+    */
+    if (selectedParentNodeAsText) 
+    { 
+      PRInt32 indexOfTextNodeInParent;
+      selectedNode = do_QueryInterface(parentSelectedNode);
+      selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
+      selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
+      GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
+
+      if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
+      {
+        nsCOMPtr newSiblingNode;
+        result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
+        // now get the node's offset in it's parent, and insert the new tag there
+        if (NS_SUCCEEDED(result)) {
+          result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
+        }
+      }
+      else 
+      { // determine where to insert the new node
+        if (0==offsetOfSelectedNode) {
+          offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
+        }
+        else {                 // insert new node as last child
+          GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
+          offsetOfNewNode++;    // offsets are 0-based, and we need the index of the new node
+        }
+      }
+    }
+    // Here's where the new node was inserted
+  }
+#ifdef DEBUG
+  else {
+    printf("InsertLineBreak into an empty document is not yet supported\n");
+  }
+#endif
+  return result;
+}
+
+
+
 NS_IMETHODIMP 
 nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
 {
diff --git a/mozilla/editor/libeditor/base/nsEditor.h b/mozilla/editor/libeditor/base/nsEditor.h
index 9c2ed1ca15c..9197fd038e6 100644
--- a/mozilla/editor/libeditor/base/nsEditor.h
+++ b/mozilla/editor/libeditor/base/nsEditor.h
@@ -46,6 +46,7 @@
 #include "nsIDTD.h"
 #include "nsIDOMElement.h"
 #include "nsVoidArray.h"
+#include "nsSelectionState.h"
 
 class nsIEditActionListener;
 class nsIDocumentStateListener;
@@ -72,92 +73,6 @@ class nsIFile;
 class nsISelectionController;
 
 
-/***************************************************************************
- * class for recording selection info.  stores selection as collection of
- * { {startnode, startoffset} , {endnode, endoffset} } tuples.  Cant store
- * ranges since dom gravity will possibly change the ranges.
- */
-
-// first a helper struct for saving/setting ranges
-struct nsRangeStore 
-{
-  nsRangeStore();
-  ~nsRangeStore();
-  nsresult StoreRange(nsIDOMRange *aRange);
-  nsresult GetRange(nsCOMPtr *outRange);
-        
-  nsCOMPtr startNode;
-  PRInt32              startOffset;
-  nsCOMPtr endNode;
-  PRInt32              endOffset;
-  // DEBUG:   static PRInt32 n;
-};
-
-class nsSelectionState
-{
-  public:
-      
-    nsSelectionState();
-    ~nsSelectionState();
-  
-    nsresult SaveSelection(nsISelection *aSel);
-    nsresult RestoreSelection(nsISelection *aSel);
-    PRBool   IsCollapsed();
-    PRBool   IsEqual(nsSelectionState *aSelState);
-    void     MakeEmpty();
-    PRBool   IsEmpty();
-  protected:    
-    nsVoidArray mArray;
-    
-    friend class nsRangeUpdater;
-};
-
-class nsRangeUpdater
-{
-  public:    
-  
-    nsRangeUpdater();
-    ~nsRangeUpdater();
-  
-    void* RegisterRange(nsIDOMRange *aRange);
-    nsCOMPtr ReclaimRange(void *aCookie);
-    void DropRange(void *aCookie);
-    void RegisterRangeItem(nsRangeStore *aRangeItem);
-    void DropRangeItem(nsRangeStore *aRangeItem);
-    nsresult RegisterSelectionState(nsSelectionState &aSelState);
-    nsresult DropSelectionState(nsSelectionState &aSelState);
-    
-    // editor selection gravity routines.  Note that we can't always depend on
-    // DOM Range gravity to do what we want to the "real" selection.  For instance,
-    // if you move a node, that corresponds to deleting it and reinserting it.
-    // DOM Range gravity will promote the selection out of the node on deletion,
-    // which is not what you want if you know you are reinserting it.
-    nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
-    nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
-    nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
-    nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
-    nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, 
-                             nsIDOMNode *aRightNode, 
-                             nsIDOMNode *aParent, 
-                             PRInt32 aOffset,
-                             PRInt32 aOldLeftNodeLength);
-    nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString);
-    nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
-    // the following gravity routines need will/did sandwiches, because the other gravity
-    // routines will be called inside of these sandwiches, but should be ignored.
-    nsresult WillReplaceContainer();
-    nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode);
-    nsresult WillRemoveContainer();
-    nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen);
-    nsresult WillInsertContainer();
-    nsresult DidInsertContainer();
-    nsresult WillMoveNode();
-    nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
-  protected:    
-    nsVoidArray mArray;
-    PRBool mLock;
-};
-
 /** implementation of an editor object.  it will be the controller/focal point 
  *  for the main editor services. i.e. the GUIManager, publishing, transaction 
  *  manager, event interfaces. the idea for the event interfaces is to have them 
@@ -181,18 +96,17 @@ public:
     kOpNone = 0,
     kOpUndo,
     kOpRedo,
-    kOpSetTextProperty,
-    kOpRemoveTextProperty,
     kOpInsertNode,
     kOpCreateNode,
     kOpDeleteNode,
     kOpSplitNode,
     kOpJoinNode,
-    kOpDeleteText,
-    kOpInsertText,
-    kOpInsertIMEText,
     kOpDeleteSelection,
-    kOpHTMLPaste
+    // text commands
+    kOpInsertBreak    = 1000,
+    kOpInsertText     = 1001,
+    kOpInsertIMEText  = 1002,
+    kOpDeleteText     = 1003
   };
 
   static const char* kMOZEditorBogusNodeAttr;
@@ -341,6 +255,8 @@ public:
                                            nsIDOMCharacterData *aTextNode, 
                                            PRInt32 aOffset);
   NS_IMETHOD DeleteSelectionImpl(EDirection aAction);
+  NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag,
+                                           nsIDOMNode ** aNewNode);
 
   /* helper routines for node/parent manipulations */
   nsresult ReplaceContainer(nsIDOMNode *inNode, 
@@ -454,6 +370,8 @@ protected:
                                   nsIDOMNode  *aRightNode,
                                   JoinElementTxn **aTxn);
 
+  NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, 
+                                                   PRInt32& offsetOfNewNode);
 
   // called each time we modify the document. Increments the mod
   // count of the doc.
@@ -775,7 +693,7 @@ public:
 
 protected:
 
-  PRUint32        mFlags;		// behavior flags. See nsIHTMLEditor.h for the flags we use.
+  PRUint32        mFlags;		// behavior flags. See nsPlaintextEditor.h for the flags we use.
   
   nsWeakPtr       mPresShellWeak;   // weak reference to the nsIPresShell
   nsWeakPtr       mSelConWeak;   // weak reference to the nsISelectionController
diff --git a/mozilla/editor/libeditor/base/nsEditorUtils.h b/mozilla/editor/libeditor/base/nsEditorUtils.h
index 9f715271bb4..4827800df70 100644
--- a/mozilla/editor/libeditor/base/nsEditorUtils.h
+++ b/mozilla/editor/libeditor/base/nsEditorUtils.h
@@ -32,6 +32,7 @@
 #include "nsIAtom.h"
 #include "nsVoidArray.h"
 #include "nsEditor.h"
+#include "nsPlaintextEditor.h"
 #include "nsIContentIterator.h"
 
 /***************************************************************************
@@ -48,6 +49,22 @@ class nsAutoPlaceHolderBatch
     ~nsAutoPlaceHolderBatch() { if (mEd) mEd->EndPlaceHolderTransaction(); }
 };
 
+/***************************************************************************
+ * stack based helper class for detecting end of editor initialization, in
+ * order to triger "end of init" initialization of the edit rules.
+ */
+class nsAutoEditInitRulesTrigger
+{
+  private:
+    nsPlaintextEditor *mEd;
+    nsresult &mRes;
+  public:
+    nsAutoEditInitRulesTrigger( nsPlaintextEditor *aEd, nsresult &aRes) : mEd(aEd), mRes(aRes)
+                   { if (mEd) mEd->BeginEditorInit(); }
+    ~nsAutoEditInitRulesTrigger() { if (mEd) mRes = mEd->EndEditorInit(); }
+};
+
+
 
 /***************************************************************************
  * stack based helper class for batching a collection of txns.  
diff --git a/mozilla/editor/libeditor/base/nsSelectionState.cpp b/mozilla/editor/libeditor/base/nsSelectionState.cpp
new file mode 100644
index 00000000000..62573f759a2
--- /dev/null
+++ b/mozilla/editor/libeditor/base/nsSelectionState.cpp
@@ -0,0 +1,640 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ */
+
+#include "nsSelectionState.h"
+#include "nsIDOMCharacterData.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMRange.h"
+#include "nsISelection.h"
+#include "nsEditor.h"
+#include "nsLayoutCID.h"
+
+static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
+
+
+/***************************************************************************
+ * class for recording selection info.  stores selection as collection of
+ * { {startnode, startoffset} , {endnode, endoffset} } tuples.  Cant store
+ * ranges since dom gravity will possibly change the ranges.
+ */
+nsSelectionState::nsSelectionState() : mArray(){}
+
+nsSelectionState::~nsSelectionState() 
+{
+  MakeEmpty();
+}
+
+nsresult  
+nsSelectionState::SaveSelection(nsISelection *aSel)
+{
+  if (!aSel) return NS_ERROR_NULL_POINTER;
+  nsresult res = NS_OK;
+  PRInt32 i,rangeCount, arrayCount = mArray.Count();
+  nsRangeStore *item;
+  aSel->GetRangeCount(&rangeCount);
+  
+  // if we need more items in the array, new them
+  if (arrayCountarrayCount)
+  {
+    while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
+    {
+      delete item;
+      mArray.RemoveElementAt(rangeCount);
+    }
+  }
+  
+  // now store the selection ranges
+  for (i=0; i range;
+    res = aSel->GetRangeAt(i, getter_AddRefs(range));
+    item->StoreRange(range);
+  }
+  
+  return res;
+}
+
+nsresult  
+nsSelectionState::RestoreSelection(nsISelection *aSel)
+{
+  if (!aSel) return NS_ERROR_NULL_POINTER;
+  nsresult res = NS_OK;
+  PRInt32 i, arrayCount = mArray.Count();
+  nsRangeStore *item;
+
+  // clear out selection
+  aSel->RemoveAllRanges();
+  
+  // set the selection ranges anew
+  for (i=0; i range;
+    item->GetRange(address_of(range));
+    if (!range) return NS_ERROR_UNEXPECTED;
+   
+    res = aSel->AddRange(range);
+    if(NS_FAILED(res)) return res;
+
+  }
+  return NS_OK;
+}
+
+PRBool
+nsSelectionState::IsCollapsed()
+{
+  if (1 != mArray.Count()) return PR_FALSE;
+  nsRangeStore *item;
+  item = (nsRangeStore*)mArray.ElementAt(0);
+  if (!item) return PR_FALSE;
+  nsCOMPtr range;
+  item->GetRange(address_of(range));
+  if (!range) return PR_FALSE;
+  PRBool bIsCollapsed;
+  range->GetCollapsed(&bIsCollapsed);
+  return bIsCollapsed;
+}
+
+PRBool
+nsSelectionState::IsEqual(nsSelectionState *aSelState)
+{
+  if (!aSelState) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count();
+  if (myCount != itsCount) return PR_FALSE;
+  if (myCount < 1) return PR_FALSE;
+
+  nsRangeStore *myItem, *itsItem;
+  
+  for (i=0; imArray.ElementAt(0));
+    if (!myItem || !itsItem) return PR_FALSE;
+    
+    nsCOMPtr myRange, itsRange;
+    myItem->GetRange(address_of(myRange));
+    itsItem->GetRange(address_of(itsRange));
+    if (!myRange || !itsRange) return PR_FALSE;
+  
+    PRInt32 compResult;
+    myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
+    if (compResult) return PR_FALSE;
+    myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
+    if (compResult) return PR_FALSE;
+  }
+  // if we got here, they are equal
+  return PR_TRUE;
+}
+
+void     
+nsSelectionState::MakeEmpty()
+{
+  // free any items in the array
+  nsRangeStore *item;
+  while ((item = (nsRangeStore*)mArray.ElementAt(0)))
+  {
+    delete item;
+    mArray.RemoveElementAt(0);
+  }
+}
+
+PRBool   
+nsSelectionState::IsEmpty()
+{
+  return (mArray.Count() == 0);
+}
+
+/***************************************************************************
+ * nsRangeUpdater:  class for updating nsIDOMRanges in response to editor actions.
+ */
+
+nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
+
+nsRangeUpdater::~nsRangeUpdater()
+{
+  // free any items in the array
+  nsRangeStore *item;
+  while ((item = (nsRangeStore*)mArray.ElementAt(0)))
+  {
+    delete item;
+    mArray.RemoveElementAt(0);
+  }
+}
+  
+void* 
+nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
+{
+  nsRangeStore *item = new nsRangeStore;
+  if (!item) return nsnull;
+  item->StoreRange(aRange);
+  mArray.AppendElement(item);
+  return item;
+}
+
+nsCOMPtr 
+nsRangeUpdater::ReclaimRange(void *aCookie)
+{
+  nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
+  if (!item) return nsnull;
+  nsCOMPtr outRange;
+  item->GetRange(address_of(outRange));
+  mArray.RemoveElement(aCookie);
+  delete item;
+  return outRange;
+}
+    
+void 
+nsRangeUpdater::DropRange(void *aCookie)
+{
+  nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
+  if (!item) return;
+  mArray.RemoveElement(aCookie);
+  delete item;
+}
+
+void 
+nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
+{
+  if (!aRangeItem) return;
+  mArray.AppendElement(aRangeItem);
+  return;
+}
+
+void 
+nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
+{
+  if (!aRangeItem) return;
+  mArray.RemoveElement(aRangeItem);
+  return;
+}
+
+nsresult 
+nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
+{
+  PRInt32 i, theCount = aSelState.mArray.Count();
+  if (theCount < 1) return NS_ERROR_FAILURE;
+
+  nsRangeStore *item;
+  
+  for (i=0; istartNode.get() == aParent) && (item->startOffset > aPosition))
+      item->startOffset++;
+    if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
+      item->endOffset++;
+  }
+  return NS_OK;
+}
+
+nsresult
+nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
+{
+  return SelAdjCreateNode(aParent, aPosition);
+}
+
+
+nsresult
+nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
+{
+  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
+  if (!aNode) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, count = mArray.Count();
+  if (!count) return NS_OK;
+
+  nsRangeStore *item;
+  
+  for (i=0; istartNode.get() == aParent) && (item->startOffset > aOffset))
+      item->startOffset--;
+    if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
+      item->endOffset--;
+  }
+  // MOOSE: also check inside of aNode, expensive.  But in theory, we shouldn't
+  // actually hit this case in the usage i forsee for this.
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
+{
+  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
+  if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, count = mArray.Count();
+  if (!count) return NS_OK;
+
+  nsCOMPtr parent;
+  PRInt32 offset;
+  nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset);
+  if (NS_FAILED(result)) return result;
+  
+  // first part is same as inserting aNewLeftnode
+  result = SelAdjInsertNode(parent,offset-1);
+  if (NS_FAILED(result)) return result;
+
+  // next step is to check for range enpoints inside aOldRightNode
+  nsRangeStore *item;
+  
+  for (i=0; istartNode.get() == aOldRightNode)
+    {
+      if (item->startOffset > aOffset)
+      {
+        item->startOffset -= aOffset;
+      }
+      else
+      {
+        item->startNode = aNewLeftNode;
+      }
+    }
+    if (item->endNode.get() == aOldRightNode)
+    {
+      if (item->endOffset > aOffset)
+      {
+        item->endOffset -= aOffset;
+      }
+      else
+      {
+        item->endNode = aNewLeftNode;
+      }
+    }
+  }
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode, 
+                                  nsIDOMNode *aRightNode, 
+                                  nsIDOMNode *aParent, 
+                                  PRInt32 aOffset,
+                                  PRInt32 aOldLeftNodeLength)
+{
+  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
+  if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, count = mArray.Count();
+  if (!count) return NS_OK;
+
+  nsRangeStore *item;
+
+  for (i=0; istartNode.get() == aParent)
+    {
+      if (item->startOffset > aOffset)
+      {
+        item->startOffset--;
+      }
+      else if (item->startOffset == aOffset)
+      {
+        // join keeps right hand node
+        item->startNode = aRightNode;
+        item->startOffset = aOldLeftNodeLength;
+      }
+    }
+    if (item->endNode.get() == aParent)
+    {
+      if (item->endOffset > aOffset)
+      {
+        item->endOffset--;
+      }
+      else if (item->endOffset == aOffset)
+      {
+        // join keeps right hand node
+        item->endNode = aRightNode;
+        item->endOffset = aOldLeftNodeLength;
+      }
+    }
+    // adjust endpoints in aRightNode
+    if (item->startNode.get() == aRightNode)
+      item->startOffset += aOldLeftNodeLength;
+    if (item->endNode.get() == aRightNode)
+      item->endOffset += aOldLeftNodeLength;
+  }
+  
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
+{
+  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
+{
+  if (mLock) return NS_OK;  // lock set by Will/DidReplaceParent, etc...
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::WillReplaceContainer()
+{
+  if (mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_TRUE;
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
+{
+  if (!mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_FALSE;
+
+  if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, count = mArray.Count();
+  if (!count) return NS_OK;
+
+  nsRangeStore *item;
+  
+  for (i=0; istartNode.get() == aOriginalNode)
+      item->startNode = aNewNode;
+    if (item->endNode.get() == aOriginalNode)
+      item->endNode = aNewNode;
+  }
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::WillRemoveContainer()
+{
+  if (mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_TRUE;
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
+{
+  if (!mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_FALSE;
+
+  if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, count = mArray.Count();
+  if (!count) return NS_OK;
+
+  nsRangeStore *item;
+  
+  for (i=0; istartNode.get() == aNode)
+    {
+      item->startNode = aParent;
+      item->startOffset += aOffset;
+    }
+    if (item->endNode.get() == aNode)
+    {
+      item->endNode = aParent;
+      item->endOffset += aOffset;
+    }
+    if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
+      item->startOffset += (PRInt32)aNodeOrigLen-1;
+    if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
+      item->endOffset += (PRInt32)aNodeOrigLen-1;
+  }
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::WillInsertContainer()
+{
+  if (mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_TRUE;
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::DidInsertContainer()
+{
+  if (!mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_FALSE;
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::WillMoveNode()
+{
+  if (mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_TRUE;
+  return NS_OK;
+}
+
+
+nsresult
+nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
+{
+  if (!mLock) return NS_ERROR_UNEXPECTED;  
+  mLock = PR_FALSE;
+
+  if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
+  PRInt32 i, count = mArray.Count();
+  if (!count) return NS_OK;
+
+  nsRangeStore *item;
+  
+  for (i=0; istartNode.get() == aOldParent) && (item->startOffset > aOldOffset))
+      item->startOffset--;
+    if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
+      item->endOffset--;
+      
+    // and like an insert in aNewParent
+    if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
+      item->startOffset++;
+    if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
+      item->endOffset++;
+  }
+  return NS_OK;
+}
+
+
+
+/***************************************************************************
+ * helper class for nsSelectionState.  nsRangeStore stores range endpoints.
+ */
+
+  // DEBUG: PRInt32 nsRangeStore::n = 0;
+
+nsRangeStore::nsRangeStore() 
+{ 
+  // DEBUG: n++;  printf("range store alloc count=%d\n", n); 
+}
+nsRangeStore::~nsRangeStore()
+{
+  // DEBUG: n--;  printf("range store alloc count=%d\n", n); 
+}
+
+nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
+{
+  if (!aRange) return NS_ERROR_NULL_POINTER;
+  aRange->GetStartContainer(getter_AddRefs(startNode));
+  aRange->GetEndContainer(getter_AddRefs(endNode));
+  aRange->GetStartOffset(&startOffset);
+  aRange->GetEndOffset(&endOffset);
+  return NS_OK;
+}
+
+nsresult nsRangeStore::GetRange(nsCOMPtr *outRange)
+{
+  if (!outRange) return NS_ERROR_NULL_POINTER;
+  nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
+                             nsnull,
+                             NS_GET_IID(nsIDOMRange),
+                             getter_AddRefs(*outRange));
+  if(NS_FAILED(res)) return res;
+
+  res = (*outRange)->SetStart(startNode, startOffset);
+  if(NS_FAILED(res)) return res;
+
+  res = (*outRange)->SetEnd(endNode, endOffset);
+  return res;
+}
diff --git a/mozilla/editor/libeditor/base/nsSelectionState.h b/mozilla/editor/libeditor/base/nsSelectionState.h
new file mode 100644
index 00000000000..28cb3dd0dbb
--- /dev/null
+++ b/mozilla/editor/libeditor/base/nsSelectionState.h
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s): 
+ */
+
+#ifndef __selectionstate_h__
+#define __selectionstate_h__
+
+#include "nsCOMPtr.h"
+#include "nsVoidArray.h"
+#include "nsIDOMNode.h"
+
+class nsIDOMCharacterData;
+class nsIDOMRange;
+class nsISelection;
+
+/***************************************************************************
+ * class for recording selection info.  stores selection as collection of
+ * { {startnode, startoffset} , {endnode, endoffset} } tuples.  Cant store
+ * ranges since dom gravity will possibly change the ranges.
+ */
+
+// first a helper struct for saving/setting ranges
+struct nsRangeStore 
+{
+  nsRangeStore();
+  ~nsRangeStore();
+  nsresult StoreRange(nsIDOMRange *aRange);
+  nsresult GetRange(nsCOMPtr *outRange);
+        
+  nsCOMPtr startNode;
+  PRInt32              startOffset;
+  nsCOMPtr endNode;
+  PRInt32              endOffset;
+  // DEBUG:   static PRInt32 n;
+};
+
+class nsSelectionState
+{
+  public:
+      
+    nsSelectionState();
+    ~nsSelectionState();
+  
+    nsresult SaveSelection(nsISelection *aSel);
+    nsresult RestoreSelection(nsISelection *aSel);
+    PRBool   IsCollapsed();
+    PRBool   IsEqual(nsSelectionState *aSelState);
+    void     MakeEmpty();
+    PRBool   IsEmpty();
+  protected:    
+    nsVoidArray mArray;
+    
+    friend class nsRangeUpdater;
+};
+
+class nsRangeUpdater
+{
+  public:    
+  
+    nsRangeUpdater();
+    ~nsRangeUpdater();
+  
+    void* RegisterRange(nsIDOMRange *aRange);
+    nsCOMPtr ReclaimRange(void *aCookie);
+    void DropRange(void *aCookie);
+    void RegisterRangeItem(nsRangeStore *aRangeItem);
+    void DropRangeItem(nsRangeStore *aRangeItem);
+    nsresult RegisterSelectionState(nsSelectionState &aSelState);
+    nsresult DropSelectionState(nsSelectionState &aSelState);
+    
+    // editor selection gravity routines.  Note that we can't always depend on
+    // DOM Range gravity to do what we want to the "real" selection.  For instance,
+    // if you move a node, that corresponds to deleting it and reinserting it.
+    // DOM Range gravity will promote the selection out of the node on deletion,
+    // which is not what you want if you know you are reinserting it.
+    nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
+    nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
+    nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
+    nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
+    nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, 
+                             nsIDOMNode *aRightNode, 
+                             nsIDOMNode *aParent, 
+                             PRInt32 aOffset,
+                             PRInt32 aOldLeftNodeLength);
+    nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString);
+    nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
+    // the following gravity routines need will/did sandwiches, because the other gravity
+    // routines will be called inside of these sandwiches, but should be ignored.
+    nsresult WillReplaceContainer();
+    nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode);
+    nsresult WillRemoveContainer();
+    nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen);
+    nsresult WillInsertContainer();
+    nsresult DidInsertContainer();
+    nsresult WillMoveNode();
+    nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
+  protected:    
+    nsVoidArray mArray;
+    PRBool mLock;
+};
+
+
+/***************************************************************************
+ * another helper class for nsSelectionState.  stack based class for doing
+ * Will/DidReplaceContainer()
+ */
+
+class nsAutoReplaceContainerSelNotify
+{
+  private:
+    nsRangeUpdater &mRU;
+    nsIDOMNode *mOriginalNode;
+    nsIDOMNode *mNewNode;
+
+  public:
+    nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
+    mRU(aRangeUpdater)
+    ,mOriginalNode(aOriginalNode)
+    ,mNewNode(aNewNode)
+    {
+      mRU.WillReplaceContainer();
+    }
+    
+    ~nsAutoReplaceContainerSelNotify()
+    {
+      mRU.DidReplaceContainer(mOriginalNode, mNewNode);
+    }
+};
+
+
+/***************************************************************************
+ * another helper class for nsSelectionState.  stack based class for doing
+ * Will/DidRemoveContainer()
+ */
+
+class nsAutoRemoveContainerSelNotify
+{
+  private:
+    nsRangeUpdater &mRU;
+    nsIDOMNode *mNode;
+    nsIDOMNode *mParent;
+    PRInt32    mOffset;
+    PRUint32   mNodeOrigLen;
+
+  public:
+    nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater, 
+                                   nsIDOMNode *aNode, 
+                                   nsIDOMNode *aParent, 
+                                   PRInt32 aOffset, 
+                                   PRUint32 aNodeOrigLen) :
+    mRU(aRangeUpdater)
+    ,mNode(aNode)
+    ,mParent(aParent)
+    ,mOffset(aOffset)
+    ,mNodeOrigLen(aNodeOrigLen)
+    {
+      mRU.WillRemoveContainer();
+    }
+    
+    ~nsAutoRemoveContainerSelNotify()
+    {
+      mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
+    }
+};
+
+/***************************************************************************
+ * another helper class for nsSelectionState.  stack based class for doing
+ * Will/DidInsertContainer()
+ */
+
+class nsAutoInsertContainerSelNotify
+{
+  private:
+    nsRangeUpdater &mRU;
+
+  public:
+    nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
+    mRU(aRangeUpdater)
+    {
+      mRU.WillInsertContainer();
+    }
+    
+    ~nsAutoInsertContainerSelNotify()
+    {
+      mRU.DidInsertContainer();
+    }
+};
+
+
+/***************************************************************************
+ * another helper class for nsSelectionState.  stack based class for doing
+ * Will/DidMoveNode()
+ */
+
+class nsAutoMoveNodeSelNotify
+{
+  private:
+    nsRangeUpdater &mRU;
+    nsIDOMNode *mOldParent;
+    nsIDOMNode *mNewParent;
+    PRInt32    mOldOffset;
+    PRInt32    mNewOffset;
+
+  public:
+    nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater, 
+                            nsIDOMNode *aOldParent, 
+                            PRInt32 aOldOffset, 
+                            nsIDOMNode *aNewParent, 
+                            PRInt32 aNewOffset) :
+    mRU(aRangeUpdater)
+    ,mOldParent(aOldParent)
+    ,mNewParent(aNewParent)
+    ,mOldOffset(aOldOffset)
+    ,mNewOffset(aNewOffset)
+    {
+      mRU.WillMoveNode();
+    }
+    
+    ~nsAutoMoveNodeSelNotify()
+    {
+      mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
+    }
+};
+
+#endif
+
+
diff --git a/mozilla/editor/libeditor/build/nsEditorRegistration.cpp b/mozilla/editor/libeditor/build/nsEditorRegistration.cpp
index ff5f86e2f0a..f262c18cdd5 100644
--- a/mozilla/editor/libeditor/build/nsEditorRegistration.cpp
+++ b/mozilla/editor/libeditor/build/nsEditorRegistration.cpp
@@ -27,6 +27,7 @@
 #include "nsEditor.h"				// for gInstanceCount
 #include "nsEditorController.h" //CID
 #include "nsEditorService.h" 
+#include "nsPlaintextEditor.h"
 
 ////////////////////////////////////////////////////////////////////////
 // Define the contructor function for the objects
@@ -38,6 +39,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorShell)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsComposerController)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
 
 #ifdef ENABLE_EDITOR_API_LOG
 #include "nsHTMLEditorLog.h"
@@ -52,6 +54,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLEditor)
 // class name.
 //
 static nsModuleComponentInfo components[] = {
+    { "Text Editor", NS_TEXTEDITOR_CID,
+      "@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
 #ifdef ENABLE_EDITOR_API_LOG
     { "HTML Editor", NS_HTMLEDITOR_CID,
       "@mozilla.org/editor/htmleditor;1", nsHTMLEditorLogConstructor, },
diff --git a/mozilla/editor/libeditor/build/nsTextEditorReg.cpp b/mozilla/editor/libeditor/build/nsTextEditorReg.cpp
new file mode 100644
index 00000000000..6a79e2d8de6
--- /dev/null
+++ b/mozilla/editor/libeditor/build/nsTextEditorReg.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s): 
+ */
+
+#include "nsIGenericFactory.h"
+
+#include "nsEditorCID.h"
+#include "nsEditor.h"           // for gInstanceCount
+#include "nsPlaintextEditor.h"
+#include "nsEditorService.h" 
+#include "nsEditorController.h" //CID
+
+////////////////////////////////////////////////////////////////////////
+// Define the contructor function for the objects
+//
+// NOTE: This creates an instance of objects by using the default constructor
+//
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
+
+////////////////////////////////////////////////////////////////////////
+// Define a table of CIDs implemented by this module along with other
+// information like the function to create an instance, contractid, and
+// class name.
+//
+static nsModuleComponentInfo components[] = {
+    { "Text Editor", NS_TEXTEDITOR_CID,
+      "@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
+    { "Editor Controller", NS_EDITORCONTROLLER_CID,
+      "@mozilla.org/editor/editorcontroller;1", nsEditorControllerConstructor, }
+};
+
+////////////////////////////////////////////////////////////////////////
+// Implement the NSGetModule() exported function for your module
+// and the entire implementation of the module object.
+//
+NS_IMPL_NSGETMODULE("nsEditorModule", components)
diff --git a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp
new file mode 100644
index 00000000000..99b54b9854a
--- /dev/null
+++ b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -0,0 +1,1619 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ */
+#include "nsICaret.h"
+
+
+#include "nsHTMLEditor.h"
+#include "nsHTMLEditRules.h"
+#include "nsHTMLEditUtils.h"
+
+#include "nsEditorEventListeners.h"
+
+#include "nsIDOMText.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMAttr.h"
+#include "nsIDocument.h"
+#include "nsIDOMEventReceiver.h" 
+#include "nsIDOMKeyEvent.h"
+#include "nsIDOMKeyListener.h" 
+#include "nsIDOMMouseListener.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsISelection.h"
+#include "nsISelectionPrivate.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsISelectionController.h"
+
+#include "nsIFrameSelection.h"  // For TABLESELECTION_ defines
+#include "nsIIndependentSelection.h" //domselections answer to frameselection
+
+
+#include "nsICSSLoader.h"
+#include "nsICSSStyleSheet.h"
+#include "nsIHTMLContentContainer.h"
+#include "nsIStyleSet.h"
+#include "nsIDocumentObserver.h"
+#include "nsIDocumentStateListener.h"
+
+#include "nsIStyleContext.h"
+
+#include "nsIEnumerator.h"
+#include "nsIContent.h"
+#include "nsIContentIterator.h"
+#include "nsEditorCID.h"
+#include "nsLayoutCID.h"
+#include "nsIDOMRange.h"
+#include "nsIDOMNSRange.h"
+#include "nsISupportsArray.h"
+#include "nsVoidArray.h"
+#include "nsFileSpec.h"
+#include "nsIFile.h"
+#include "nsIURL.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsWidgetsCID.h"
+#include "nsIDocumentEncoder.h"
+#include "nsIDOMDocumentFragment.h"
+#include "nsIPresShell.h"
+#include "nsIPresContext.h"
+#include "nsIParser.h"
+#include "nsParserCIID.h"
+#include "nsIImage.h"
+#include "nsAOLCiter.h"
+#include "nsInternetCiter.h"
+#include "nsISupportsPrimitives.h"
+#include "InsertTextTxn.h"
+
+// netwerk
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+
+// Drag & Drop, Clipboard
+#include "nsWidgetsCID.h"
+#include "nsIClipboard.h"
+#include "nsITransferable.h"
+#include "nsIDragService.h"
+#include "nsIDOMNSUIEvent.h"
+
+// Transactionas
+#include "PlaceholderTxn.h"
+#include "nsStyleSheetTxns.h"
+
+// Misc
+#include "TextEditorTest.h"
+#include "nsEditorUtils.h"
+#include "nsIPref.h"
+const PRUnichar nbsp = 160;
+
+// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd
+#define NS_CTRANSITIONAL_DTD_CID \
+{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } }
+
+
+static NS_DEFINE_CID(kHTMLEditorCID,  NS_HTMLEDITOR_CID);
+static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
+static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
+static NS_DEFINE_CID(kCRangeCID,      NS_RANGE_CID);
+static NS_DEFINE_CID(kCDOMSelectionCID,      NS_DOMSELECTION_CID);
+static NS_DEFINE_IID(kFileWidgetCID,  NS_FILEWIDGET_CID);
+static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
+static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); 
+static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); 
+static NS_DEFINE_CID(kCTransitionalDTDCID,  NS_CTRANSITIONAL_DTD_CID);
+
+// Drag & Drop, Clipboard Support
+static NS_DEFINE_CID(kCClipboardCID,    NS_CLIPBOARD_CID);
+static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
+static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID);
+static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID);
+// private clipboard data flavors for html copy/paste
+#define kHTMLContext   "text/_moz_htmlcontext"
+#define kHTMLInfo      "text/_moz_htmlinfo"
+
+
+#if defined(NS_DEBUG) && defined(DEBUG_buster)
+static PRBool gNoisy = PR_FALSE;
+#else
+static const PRBool gNoisy = PR_FALSE;
+#endif
+static nsCOMPtr GetListParent(nsIDOMNode* aNode)
+{
+  if (!aNode) return nsnull;
+  nsCOMPtr parent, tmp;
+  aNode->GetParentNode(getter_AddRefs(parent));
+  while (parent)
+  {
+    if (nsHTMLEditUtils::IsList(parent)) return parent;
+    parent->GetParentNode(getter_AddRefs(tmp));
+    parent = tmp;
+  }
+  return nsnull;
+}
+
+static nsCOMPtr GetTableParent(nsIDOMNode* aNode)
+{
+  if (!aNode) return nsnull;
+  nsCOMPtr parent, tmp;
+  aNode->GetParentNode(getter_AddRefs(parent));
+  while (parent)
+  {
+    if (nsHTMLEditUtils::IsTable(parent)) return parent;
+    parent->GetParentNode(getter_AddRefs(tmp));
+    parent = tmp;
+  }
+  return nsnull;
+}
+
+
+NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString)
+{
+  nsAutoString charset;
+  return InsertHTMLWithCharset(aInputString, charset);
+}
+
+nsresult nsHTMLEditor::InsertHTMLWithContext(const nsString& aInputString, const nsString& aContextStr, const nsString& aInfoStr)
+{
+  nsAutoString charset;
+  return InsertHTMLWithCharsetAndContext(aInputString, charset, aContextStr, aInfoStr);
+}
+
+
+NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset)
+{
+  return InsertHTMLWithCharsetAndContext(aInputString, aCharset, nsAutoString(), nsAutoString());
+}
+
+
+nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsString& aInputString,
+                                                       const nsString& aCharset,
+                                                       const nsString& aContextStr,
+                                                       const nsString& aInfoStr)
+{
+  if (!mRules) return NS_ERROR_NOT_INITIALIZED;
+
+  // First, make sure there are no return chars in the document.
+  // Bad things happen if you insert returns (instead of dom newlines, \n)
+  // into an editor document.
+  nsAutoString inputString (aInputString);  // hope this does copy-on-write
+
+  // Windows linebreaks: Map CRLF to LF:
+  inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r\n"),
+                               NS_ConvertASCIItoUCS2("\n"));
+ 
+  // Mac linebreaks: Map any remaining CR to LF:
+  inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r"),
+                               NS_ConvertASCIItoUCS2("\n"));
+
+  // force IME commit; set up rules sniffing and batching
+  ForceCompositionEnd();
+  nsAutoEditBatch beginBatching(this);
+  nsAutoRules beginRulesSniffing(this, kOpHTMLPaste, nsIEditor::eNext);
+  
+  // Get selection
+  nsresult res;
+  nsCOMPtrselection;
+  res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  
+  // Get the first range in the selection, for context:
+  nsCOMPtr range;
+  res = selection->GetRangeAt(0, getter_AddRefs(range));
+  if (NS_FAILED(res))
+    return res;
+
+  nsCOMPtr nsrange (do_QueryInterface(range));
+  if (!nsrange)
+    return NS_ERROR_NO_INTERFACE;
+
+  // create a dom document fragment that represents the structure to paste
+  nsCOMPtr fragmentAsNode;
+  PRInt32 rangeStartHint, rangeEndHint;
+  res = CreateDOMFragmentFromPaste(nsrange, inputString, aContextStr, aInfoStr, 
+                                            address_of(fragmentAsNode),
+                                            &rangeStartHint, &rangeEndHint);
+  NS_ENSURE_SUCCESS(res, res);
+
+
+  // make a list of what nodes in docFrag we need to move
+  nsCOMPtr nodeList;
+  res = CreateListOfNodesToPaste(fragmentAsNode, address_of(nodeList), rangeStartHint, rangeEndHint);
+  NS_ENSURE_SUCCESS(res, res);
+  
+  // are there any table elements in the list?  
+  // node and offset for insertion
+  nsCOMPtr parentNode;
+  PRInt32 offsetOfNewNode;
+  
+  // check for table cell selection mode
+  PRBool cellSelectionMode = PR_FALSE;
+  nsCOMPtr cell;
+  res = GetFirstSelectedCell(getter_AddRefs(cell), nsnull);
+  if (NS_SUCCEEDED(res) && cell)
+  {
+    cellSelectionMode = PR_TRUE;
+  }
+  
+  if (cellSelectionMode)
+  {
+    // do we have table content to paste?  If so, we want to delete
+    // the selected table cells and replace with new table elements;
+    // but if not we want to delete _contents_ of cells and replace
+    // with non-table elements.  Use cellSelectionMode bool to 
+    // indicate results.
+    nsCOMPtr isupports = nodeList->ElementAt(0);
+    nsCOMPtr firstNode( do_QueryInterface(isupports) );
+    if (!nsHTMLEditUtils::IsTableElement(firstNode))
+      cellSelectionMode = PR_FALSE;
+  }
+
+  if (!cellSelectionMode)
+  {
+    res = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode);
+    NS_ENSURE_SUCCESS(res, res);
+
+    // pasting does not inherit local inline styles
+    res = RemoveAllInlineProperties();
+    NS_ENSURE_SUCCESS(res, res);
+  }
+  else
+  {
+    // delete whole cells: we will replace with new table content
+    if (1)
+    {
+      // Save current selection since DeleteTableCell perturbs it
+      nsAutoSelectionReset selectionResetter(selection, this);
+      res = DeleteTableCell(1);
+      NS_ENSURE_SUCCESS(res, res);
+    }
+    // colapse selection to beginning of deleted table content
+    selection->CollapseToStart();
+  }
+  
+  // give rules a chance to handle or cancel
+  nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
+  PRBool cancel, handled;
+  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  if (NS_FAILED(res)) return res;
+  if (cancel) return NS_OK; // rules canceled the operation
+  if (!handled)
+  {
+    // The rules code (WillDoAction above) might have changed the selection.  
+    // refresh our memory...
+    res = GetStartNodeAndOffset(selection, address_of(parentNode), &offsetOfNewNode);
+    if (!parentNode) res = NS_ERROR_FAILURE;
+    if (NS_FAILED(res)) return res;
+    
+    // are we in a text node?  If so, split it.
+    if (IsTextNode(parentNode))
+    {
+      nsCOMPtr temp;
+      res = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode);
+      if (NS_FAILED(res)) return res;
+      res = parentNode->GetParentNode(getter_AddRefs(temp));
+      if (NS_FAILED(res)) return res;
+      parentNode = temp;
+    }
+
+    // build up list of parents of first node in lst that are either:
+    // lists, or tables.  
+    nsCOMPtr isup = nodeList->ElementAt(0);
+    nsCOMPtr  pNode( do_QueryInterface(isup) );
+    nsCOMPtr listAndTableArray;
+    res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray));
+    NS_ENSURE_SUCCESS(res, res);
+    while (pNode)
+    {
+      if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode))
+      {
+        isup = do_QueryInterface(pNode);
+        listAndTableArray->AppendElement(isup);
+      }
+      nsCOMPtr parent;
+      pNode->GetParentNode(getter_AddRefs(parent));
+      pNode = parent;
+    }
+    
+    // remember number of lists and tables above us
+    PRUint32 listAndTableParents;
+    PRInt32 highWaterMark = -1;
+    listAndTableArray->Count(&listAndTableParents);
+    
+    PRUint32 listCount, j;
+    if (listAndTableParents)
+    {
+      // scan insertion list for table elements (other than table).  
+      nodeList->Count(&listCount);
+      for (j=0; j isupports = nodeList->ElementAt(j);
+        nsCOMPtr curNode( do_QueryInterface(isupports) );
+
+        NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
+        if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode))
+        {
+          nsCOMPtr theTable = GetTableParent(curNode);
+          if (theTable)
+          {
+            nsCOMPtr isupTable(do_QueryInterface(theTable));
+            PRInt32 indexT = listAndTableArray->IndexOf(isupTable);
+            if (indexT >= 0)
+            {
+              highWaterMark = indexT;
+              if ((PRUint32)highWaterMark == listAndTableParents-1) break;
+            }
+            else
+            {
+              break;
+            }
+          }
+        }
+        if (nsHTMLEditUtils::IsListItem(curNode))
+        {
+          nsCOMPtr theList = GetListParent(curNode);
+          if (theList)
+          {
+            nsCOMPtr isupList(do_QueryInterface(theList));
+            PRInt32 indexL = listAndTableArray->IndexOf(isupList);
+            if (indexL >= 0)
+            {
+              highWaterMark = indexL;
+              if ((PRUint32)highWaterMark == listAndTableParents-1) break;
+            }
+            else
+            {
+              break;
+            }
+          }
+        }
+      }
+    }
+    // if we have pieces of tables or lists to be inserted, let's force the paste 
+    // to deal with table elements right away, so that it doesn't orphan some 
+    // table or list contents outside the table or list.
+    if (highWaterMark >= 0)
+    {
+      nsCOMPtr isupports = listAndTableArray->ElementAt(highWaterMark);
+      nsCOMPtr curNode( do_QueryInterface(isupports) );
+      nsCOMPtr replaceNode, tmp;
+      if (nsHTMLEditUtils::IsTable(curNode))
+      {
+        // look upward from curNode for a piece of this table
+        isup  = nodeList->ElementAt(0);
+        pNode = do_QueryInterface(isup);
+        while (pNode)
+        {
+          if (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode))
+          {
+            nsCOMPtr tableP = GetTableParent(pNode);
+            if (tableP == curNode)
+            {
+              replaceNode = pNode;
+              break;
+            }
+          }
+          nsCOMPtr parent;
+          pNode->GetParentNode(getter_AddRefs(parent));
+          pNode = parent;
+        }
+      }
+      else // list case
+      {
+        // look upward from curNode for a piece of this list
+        isup  = nodeList->ElementAt(0);
+        pNode = do_QueryInterface(isup);
+        while (pNode)
+        {
+          if (nsHTMLEditUtils::IsListItem(pNode))
+          {
+            nsCOMPtr listP = GetListParent(pNode);
+            if (listP == curNode)
+            {
+              replaceNode = pNode;
+              break;
+            }
+          }
+          nsCOMPtr parent;
+          pNode->GetParentNode(getter_AddRefs(parent));
+          pNode = parent;
+        }
+      }
+      
+      if (replaceNode)
+      {
+        isupports = do_QueryInterface(replaceNode);
+        nodeList->ReplaceElementAt(isupports, 0);
+        // postprocess list to remove any descendants of this node
+        // so that we dont insert them twice.
+        do
+        {
+          isupports = nodeList->ElementAt(1);
+          tmp = do_QueryInterface(isupports);
+          if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode))
+            nodeList->RemoveElementAt(1);
+          else
+            break;
+        } while(tmp);
+      }
+    }
+    
+    // Loop over the node list and paste the nodes:
+    PRBool bDidInsert = PR_FALSE;
+    nsCOMPtr lastInsertNode, insertedContextParent;
+    nodeList->Count(&listCount);
+    for (j=0; j isupports = nodeList->ElementAt(j);
+      nsCOMPtr curNode( do_QueryInterface(isupports) );
+
+      NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
+      NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
+      NS_ENSURE_TRUE(!nsHTMLEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
+      
+      if (insertedContextParent)
+      {
+        // if we had to insert something higher up in the paste heirarchy, we want to 
+        // skip any further paste nodes that descend from that.  Else we will paste twice.
+        if (nsHTMLEditUtils::IsDescendantOf(curNode, insertedContextParent))
+          continue;
+      }
+      
+      // give the user a hand on table element insertion.  if they have
+      // a table or table row on the clipboard, and are trying to insert
+      // into a table or table row, insert the appropriate children instead.
+      if (  (nsHTMLEditUtils::IsTableRow(curNode) && nsHTMLEditUtils::IsTableRow(parentNode))
+         && (nsHTMLEditUtils::IsTable(curNode)    || nsHTMLEditUtils::IsTable(parentNode)) )
+      {
+        nsCOMPtr child;
+        curNode->GetFirstChild(getter_AddRefs(child));
+        while (child)
+        {
+          InsertNodeAtPoint(child, parentNode, offsetOfNewNode, PR_TRUE);
+          if (NS_SUCCEEDED(res)) 
+          {
+            bDidInsert = PR_TRUE;
+            lastInsertNode = curNode;
+            offsetOfNewNode++;
+          }
+          curNode->GetFirstChild(getter_AddRefs(child));
+        }
+      }
+      else
+      {
+        // try to insert
+        res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE);
+        if (NS_SUCCEEDED(res)) 
+        {
+          bDidInsert = PR_TRUE;
+          lastInsertNode = curNode;
+        }
+          
+        // assume failure means no legal parent in the document heirarchy.
+        // try again with the parent of curNode in the paste heirarchy.
+        nsCOMPtr parent;
+        while (NS_FAILED(res) && curNode)
+        {
+          curNode->GetParentNode(getter_AddRefs(parent));
+          if (parent && !nsHTMLEditUtils::IsBody(parent))
+          {
+            res = InsertNodeAtPoint(parent, parentNode, offsetOfNewNode, PR_TRUE);
+            if (NS_SUCCEEDED(res)) 
+            {
+              bDidInsert = PR_TRUE;
+              insertedContextParent = parent;
+              lastInsertNode = parent;
+            }
+          }
+          curNode = parent;
+        }
+      }
+      if (bDidInsert)
+      {
+        res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode);
+        NS_ENSURE_SUCCESS(res, res);
+        offsetOfNewNode++;
+      }
+    }
+
+    // Now collapse the selection to the end of what we just inserted:
+    if (lastInsertNode) 
+    {
+      res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode);
+      NS_ENSURE_SUCCESS(res, res);
+      selection->Collapse(parentNode, offsetOfNewNode+1);
+    }
+  }
+  
+  res = mRules->DidDoAction(selection, &ruleInfo, res);
+  return res;
+}
+
+nsresult
+nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode)
+{
+  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
+
+  nsresult res = NS_OK;
+  nsCOMPtr content = do_QueryInterface(aNode);
+  if (IsEmptyTextContent(content))
+  {
+    nsCOMPtr parent, ignored;
+    aNode->GetParentNode(getter_AddRefs(parent));
+    if (parent)
+    {
+      res = parent->RemoveChild(aNode, getter_AddRefs(ignored));
+      return res;
+    }
+  }
+  
+  if (!nsHTMLEditUtils::IsPre(aNode))
+  {
+    nsCOMPtr child;
+    aNode->GetLastChild(getter_AddRefs(child));
+  
+    while (child)
+    {
+      nsCOMPtr tmp;
+      child->GetPreviousSibling(getter_AddRefs(tmp));
+      res = StripFormattingNodes(child);
+      NS_ENSURE_SUCCESS(res, res);
+      child = tmp;
+    }
+  }
+  return res;
+}
+
+NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable)
+{
+  // Create generic Transferable for getting the data
+  nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                          NS_GET_IID(nsITransferable), 
+                                          (void**)transferable);
+  if (NS_FAILED(rv))
+    return rv;
+
+  // Get the nsITransferable interface for getting the data from the clipboard
+  if (transferable)
+  {
+    // Create the desired DataFlavor for the type of data
+    // we want to get out of the transferable
+    if ((mFlags & eEditorPlaintextMask) == 0)  // This should only happen in html editors, not plaintext
+    {
+      (*transferable)->AddDataFlavor(kJPEGImageMime);
+      (*transferable)->AddDataFlavor(kHTMLMime);
+      (*transferable)->AddDataFlavor(kFileMime);
+    }
+    (*transferable)->AddDataFlavor(kUnicodeMime);
+  }
+  
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, 
+                                                   const nsString& aContextStr,
+                                                   const nsString& aInfoStr)
+{
+  nsresult rv = NS_OK;
+  char* bestFlavor = nsnull;
+  nsCOMPtr genericDataObj;
+  PRUint32 len = 0;
+  if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
+  {
+    nsAutoTxnsConserveSelection dontSpazMySelection(this);
+    nsAutoString flavor, stuffToPaste;
+    flavor.AssignWithConversion( bestFlavor );   // just so we can use flavor.Equals()
+#ifdef DEBUG_akkana
+    printf("Got flavor [%s]\n", bestFlavor);
+#endif
+    if (flavor.EqualsWithConversion(kHTMLMime))
+    {
+      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
+      if (textDataObj && len > 0)
+      {
+        PRUnichar* text = nsnull;
+
+        textDataObj->ToString ( &text );
+        stuffToPaste.Assign ( text, len / 2 );
+        nsAutoEditBatch beginBatching(this);
+        rv = InsertHTMLWithContext(stuffToPaste, aContextStr, aInfoStr);
+        if (text)
+          nsMemory::Free(text);
+      }
+    }
+    else if (flavor.EqualsWithConversion(kUnicodeMime))
+    {
+      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
+      if (textDataObj && len > 0)
+      {
+        PRUnichar* text = nsnull;
+        textDataObj->ToString ( &text );
+        stuffToPaste.Assign ( text, len / 2 );
+        nsAutoEditBatch beginBatching(this);
+        // pasting does not inherit local inline styles
+        RemoveAllInlineProperties();
+        rv = InsertText(stuffToPaste.GetUnicode());
+        if (text)
+          nsMemory::Free(text);
+      }
+    }
+    else if (flavor.EqualsWithConversion(kFileMime))
+    {
+      nsCOMPtr fileObj ( do_QueryInterface(genericDataObj) );
+      if (fileObj && len > 0)
+      {
+        nsCOMPtr fileURL;
+        rv = nsComponentManager::CreateInstance("@mozilla.org/network/standard-url;1", nsnull, 
+                                     NS_GET_IID(nsIURL), getter_AddRefs(fileURL));
+        if (NS_FAILED(rv))
+          return rv;
+        
+        if ( fileURL )
+        {
+          rv = fileURL->SetFile( fileObj );
+          if (NS_FAILED(rv))
+            return rv;
+          
+          PRBool insertAsImage = PR_FALSE;
+          char *fileextension = nsnull;
+          rv = fileURL->GetFileExtension( &fileextension );
+          if ( NS_SUCCEEDED(rv) && fileextension )
+          {
+            if ( (nsCRT::strcasecmp( fileextension, "jpg" ) == 0 )
+              || (nsCRT::strcasecmp( fileextension, "jpeg" ) == 0 )
+              || (nsCRT::strcasecmp( fileextension, "gif" ) == 0 )
+              || (nsCRT::strcasecmp( fileextension, "png" ) == 0 ) )
+            {
+              insertAsImage = PR_TRUE;
+            }
+          }
+          if (fileextension) nsCRT::free(fileextension);
+          
+          char *urltext = nsnull;
+          rv = fileURL->GetSpec( &urltext );
+          if ( NS_SUCCEEDED(rv) && urltext && urltext[0] != 0)
+          {
+            len = strlen(urltext);
+            if ( insertAsImage )
+            {
+              stuffToPaste.AssignWithConversion ( "" );
+            }
+            else /* insert as link */
+            {
+              stuffToPaste.AssignWithConversion ( "" );
+              stuffToPaste.AppendWithConversion ( urltext, len );
+              stuffToPaste.AppendWithConversion ( "" );
+            }
+            nsAutoEditBatch beginBatching(this);
+            rv = InsertHTML(stuffToPaste);
+          }
+          if (urltext) nsCRT::free(urltext);
+        }
+      }
+    }
+    else if (flavor.EqualsWithConversion(kJPEGImageMime))
+    {
+      // Insert Image code here
+      printf("Don't know how to insert an image yet!\n");
+      //nsIImage* image = (nsIImage *)data;
+      //NS_RELEASE(image);
+      rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code
+    }
+  }
+  nsCRT::free(bestFlavor);
+      
+  // Try to scroll the selection into view if the paste/drop succeeded
+  if (NS_SUCCEEDED(rv))
+  {
+    nsCOMPtr selCon;
+    if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
+      selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION);
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
+{
+  ForceCompositionEnd();
+  
+  nsresult rv;
+  NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr dragSession(do_QueryInterface(dragService));
+  
+  if (!dragSession) return NS_OK;
+
+  // Get the nsITransferable interface for getting the data from the drop
+  nsCOMPtr trans;
+  rv = PrepareTransferable(getter_AddRefs(trans));
+  if (NS_FAILED(rv)) return rv;
+  if (!trans) return NS_OK;  // NS_ERROR_FAILURE; SHOULD WE FAIL?
+
+  PRUint32 numItems = 0; 
+  rv = dragSession->GetNumDropItems(&numItems);
+  if (NS_FAILED(rv)) return rv;
+
+  // Combine any deletion and drop insertion into one transaction
+  nsAutoEditBatch beginBatching(this);
+
+  PRUint32 i; 
+  PRBool doPlaceCaret = PR_TRUE;
+  for (i = 0; i < numItems; ++i)
+  {
+    rv = dragSession->GetData(trans, i);
+    if (NS_FAILED(rv)) return rv;
+    if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
+
+    if ( doPlaceCaret )
+    {
+      // check if the user pressed the key to force a copy rather than a move
+      // if we run into problems here, we'll just assume the user doesn't want a copy
+      PRBool userWantsCopy = PR_FALSE;
+
+      nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent));
+      if (!nsuiEvent) return NS_ERROR_FAILURE;
+
+      nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) );
+      if (mouseEvent)
+
+#ifdef XP_MAC
+        mouseEvent->GetAltKey(&userWantsCopy);
+#else
+        mouseEvent->GetCtrlKey(&userWantsCopy);
+#endif
+      // Source doc is null if source is *not* the current editor document
+      nsCOMPtr srcdomdoc;
+      rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
+      if (NS_FAILED(rv)) return rv;
+
+      // Current doc is destination
+      nsCOMPtrdestdomdoc; 
+      rv = GetDocument(getter_AddRefs(destdomdoc)); 
+      if (NS_FAILED(rv)) return rv;
+
+      nsCOMPtr selection;
+      rv = GetSelection(getter_AddRefs(selection));
+      if (NS_FAILED(rv)) return rv;
+      if (!selection) return NS_ERROR_FAILURE;
+
+      PRBool isCollapsed;
+      rv = selection->GetIsCollapsed(&isCollapsed);
+      if (NS_FAILED(rv)) return rv;
+      
+      // Parent and offset under the mouse cursor
+      nsCOMPtr newSelectionParent;
+      PRInt32 newSelectionOffset = 0;
+      rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
+      if (NS_FAILED(rv)) return rv;
+      if (!newSelectionParent) return NS_ERROR_FAILURE;
+
+      rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
+      if (NS_FAILED(rv)) return rv;
+      /* Creating a range to store insert position because when
+         we delete the selection, range gravity will make sure the insertion
+         point is in the correct place */
+      nsCOMPtr destinationRange;
+      rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange));
+      if (NS_FAILED(rv))
+        return rv;
+      if(!destinationRange)
+        return NS_ERROR_FAILURE;
+
+      // We never have to delete if selection is already collapsed
+      PRBool deleteSelection = PR_FALSE;
+      PRBool cursorIsInSelection = PR_FALSE;
+
+      // Check if mouse is in the selection
+      if (!isCollapsed)
+      {
+        PRInt32 rangeCount;
+        rv = selection->GetRangeCount(&rangeCount);
+        if (NS_FAILED(rv)) 
+          return rv?rv:NS_ERROR_FAILURE;
+
+        for (PRInt32 j = 0; j < rangeCount; j++)
+        {
+          nsCOMPtr range;
+
+          rv = selection->GetRangeAt(j, getter_AddRefs(range));
+          if (NS_FAILED(rv) || !range) 
+            continue;//dont bail yet, iterate through them all
+
+          nsCOMPtr nsrange(do_QueryInterface(range));
+          if (NS_FAILED(rv) || !nsrange) 
+            continue;//dont bail yet, iterate through them all
+
+          rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
+          if(cursorIsInSelection)
+            break;
+        }
+        if (cursorIsInSelection)
+        {
+          // Dragging within same doc can't drop on itself -- leave!
+          // (We shouldn't get here - drag event shouldn't have started if over selection)
+          if (srcdomdoc == destdomdoc)
+            return NS_OK;
+          
+          // Dragging from another window onto a selection
+          // XXX Decision made to NOT do this,
+          //     note that 4.x does replace if dropped on
+          //deleteSelection = PR_TRUE;
+        }
+        else 
+        {
+          // We are NOT over the selection
+          if (srcdomdoc == destdomdoc)
+          {
+            // Within the same doc: delete if user doesn't want to copy
+            deleteSelection = !userWantsCopy;
+          }
+          else
+          {
+            // Different source doc: Don't delete
+            deleteSelection = PR_FALSE;
+          }
+        }
+      }
+
+      if (deleteSelection)
+      {
+        rv = DeleteSelection(eNone);
+        if (NS_FAILED(rv)) return rv;
+      }
+
+      // If we deleted the selection because we dropped from another doc,
+      //  then we don't have to relocate the caret (insert at the deletion point)
+      if (!(deleteSelection && srcdomdoc != destdomdoc))
+      {
+        // Move the selection to the point under the mouse cursor
+        rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent));
+        if (NS_FAILED(rv))
+          return rv;
+        if(!newSelectionParent)
+          return NS_ERROR_FAILURE;
+       
+        rv = destinationRange->GetStartOffset(&newSelectionOffset);
+        if (NS_FAILED(rv))
+          return rv;
+        selection->Collapse(newSelectionParent, newSelectionOffset);
+      }      
+      // We have to figure out whether to delete and relocate caret only once
+      doPlaceCaret = PR_FALSE;
+    }
+    
+    rv = InsertFromTransferable(trans, nsAutoString(), nsAutoString());
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag)
+{
+  /* we really should be checking the XY coordinates of the mouseevent and ensure that
+   * that particular point is actually within the selection (not just that there is a selection)
+   */
+  aCanDrag = PR_FALSE;
+ 
+  // KLUDGE to work around bug 50703
+  // After double click and object property editing, 
+  //  we get a spurious drag event
+  if (mIgnoreSpuriousDragEvent)
+  {
+#ifdef DEBUG_cmanske
+    printf(" *** IGNORING SPURIOUS DRAG EVENT!\n");
+#endif
+    mIgnoreSpuriousDragEvent = PR_FALSE;
+    return NS_OK;
+  }
+   
+  nsCOMPtr selection;
+  nsresult res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+    
+  PRBool isCollapsed;
+  res = selection->GetIsCollapsed(&isCollapsed);
+  if (NS_FAILED(res)) return res;
+  
+  // if we are collapsed, we have no selection so nothing to drag
+  if ( isCollapsed )
+    return NS_OK;
+
+  nsCOMPtr eventTarget;
+  res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget));
+  if (NS_FAILED(res)) return res;
+  if ( eventTarget )
+  {
+    nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget);
+    if ( eventTargetDomNode )
+    {
+      PRBool amTargettedCorrectly = PR_FALSE;
+      res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly);
+      if (NS_FAILED(res)) return res;
+
+    	aCanDrag = amTargettedCorrectly;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent)
+{
+  nsresult rv;
+
+  nsCOMPtr eventTarget;
+  rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
+  if (NS_FAILED(rv)) return rv;
+  nsCOMPtr domnode = do_QueryInterface(eventTarget);
+
+  /* get the selection to be dragged */
+  nsCOMPtr selection;
+  rv = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(rv)) return rv;
+
+  /* create an array of transferables */
+  nsCOMPtr transferableArray;
+  NS_NewISupportsArray(getter_AddRefs(transferableArray));
+  if (transferableArray == nsnull)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  /* get the drag service */
+  NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
+  if (NS_FAILED(rv)) return rv;
+
+  /* create html flavor transferable */
+  nsCOMPtr trans;
+  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                        NS_GET_IID(nsITransferable), 
+                                        getter_AddRefs(trans));
+  if (NS_FAILED(rv)) return rv;
+  if ( !trans ) return NS_ERROR_OUT_OF_MEMORY;
+
+  nsCOMPtr domdoc;
+  rv = GetDocument(getter_AddRefs(domdoc));
+  if (NS_FAILED(rv)) return rv;
+	
+  nsCOMPtr doc = do_QueryInterface(domdoc);
+  if (doc)
+  {
+    nsCOMPtr docEncoder;
+
+    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html");
+    NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
+
+    docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0);
+    docEncoder->SetSelection(selection);
+
+    nsAutoString buffer;
+
+    rv = docEncoder->EncodeToString(buffer);
+  
+    if (NS_FAILED(rv))
+      return rv;
+
+    if ( !buffer.IsEmpty() )
+    {
+      nsCOMPtr htmlConverter;
+      rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter),
+                                              getter_AddRefs(htmlConverter));
+      if (NS_FAILED(rv)) return rv;
+      if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY;
+
+      nsCOMPtr dataWrapper;
+      rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull,
+                                              NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper));
+      if (NS_FAILED(rv)) return rv;
+      if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY;
+
+      rv = trans->AddDataFlavor(kHTMLMime);
+      if (NS_FAILED(rv)) return rv;
+      rv = trans->SetConverter(htmlConverter);
+      if (NS_FAILED(rv)) return rv;
+
+      rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) );
+      if (NS_FAILED(rv)) return rv;
+
+      // QI the data object an |nsISupports| so that when the transferable holds
+      // onto it, it will addref the correct interface.
+      nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) );
+      rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2);
+      if (NS_FAILED(rv)) return rv;
+
+      /* add the transferable to the array */
+      rv = transferableArray->AppendElement(trans);
+      if (NS_FAILED(rv)) return rv;
+
+      /* invoke drag */
+      unsigned int flags;
+      // in some cases we'll want to cut rather than copy... hmmmmm...
+      // if ( wantToCut )
+      //   flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE;
+      // else
+        flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
+      
+      rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags);
+      if (NS_FAILED(rv)) return rv;
+
+      aDragEvent->PreventBubble();
+    }
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType)
+{
+  ForceCompositionEnd();
+
+  // Get Clipboard Service
+  nsresult rv;
+  NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv );
+  if ( NS_FAILED(rv) )
+    return rv;
+    
+  // Get the nsITransferable interface for getting the data from the clipboard
+  nsCOMPtr trans;
+  rv = PrepareTransferable(getter_AddRefs(trans));
+  if (NS_SUCCEEDED(rv) && trans)
+  {
+    // Get the Data from the clipboard  
+    if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
+    {
+      // also get additional html copy hints, if present
+      nsAutoString contextStr, infoStr;
+      nsCOMPtr contextDataObj, infoDataObj;
+      PRUint32 contextLen, infoLen;
+      nsCOMPtr textDataObj;
+      
+      nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID);
+      NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER);
+      contextTrans->AddDataFlavor(kHTMLContext);
+      clipboard->GetData(contextTrans, aSelectionType);
+      contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen);
+
+      nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID);
+      NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER);
+      infoTrans->AddDataFlavor(kHTMLInfo);
+      clipboard->GetData(infoTrans, aSelectionType);
+      infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen);
+      
+      if (contextDataObj)
+      {
+        PRUnichar* text = nsnull;
+        textDataObj = do_QueryInterface(contextDataObj);
+        textDataObj->ToString ( &text );
+        contextStr.Assign ( text, contextLen / 2 );
+        if (text)
+          nsMemory::Free(text);
+      }
+      
+      if (infoDataObj)
+      {
+        PRUnichar* text = nsnull;
+        textDataObj = do_QueryInterface(infoDataObj);
+        textDataObj->ToString ( &text );
+        infoStr.Assign ( text, infoLen / 2 );
+        if (text)
+          nsMemory::Free(text);
+      }
+      rv = InsertFromTransferable(trans, contextStr, infoStr);
+    }
+  }
+
+  return rv;
+}
+
+
+NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste)
+{
+  aCanPaste = PR_FALSE;
+  
+  // can't paste if readonly
+  if (!IsModifiable())
+    return NS_OK;
+    
+  nsresult rv;
+  NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
+  if (NS_FAILED(rv)) return rv;
+  
+  // the flavors that we can deal with
+  char* textEditorFlavors[] = { kUnicodeMime, nsnull };
+  char* htmlEditorFlavors[] = { kJPEGImageMime, kHTMLMime, nsnull };
+
+  nsCOMPtr flavorsList;
+  rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, 
+         NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
+  if (NS_FAILED(rv)) return rv;
+  
+  PRUint32 editorFlags;
+  GetFlags(&editorFlags);
+  
+  // add the flavors for all editors
+  for (char** flavor = textEditorFlavors; *flavor; flavor++)
+  {
+    nsCOMPtr flavorString;            
+    nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, 
+         NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
+    if (flavorString)
+    {
+      flavorString->SetData(*flavor);
+      flavorsList->AppendElement(flavorString);
+    }
+  }
+  
+  // add the HTML-editor only flavors
+  if ((editorFlags & eEditorPlaintextMask) == 0)
+  {
+    for (char** htmlFlavor = htmlEditorFlavors; *htmlFlavor; htmlFlavor++)
+    {
+      nsCOMPtr flavorString;            
+      nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, 
+           NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
+      if (flavorString)
+      {
+        flavorString->SetData(*htmlFlavor);
+        flavorsList->AppendElement(flavorString);
+      }
+    }
+  }
+  
+  PRBool haveFlavors;
+  rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
+  if (NS_FAILED(rv)) return rv;
+  
+  aCanPaste = haveFlavors;
+  return NS_OK;
+}
+
+
+// 
+// HTML PasteAsQuotation: Paste in a blockquote type=cite
+//
+NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation(PRInt32 aSelectionType)
+{
+  if (mFlags & eEditorPlaintextMask)
+    return PasteAsPlaintextQuotation(aSelectionType);
+
+  nsAutoString citation;
+  return PasteAsCitedQuotation(citation, aSelectionType);
+}
+
+NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation,
+                                                  PRInt32 aSelectionType)
+{
+  nsAutoEditBatch beginBatching(this);
+  nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+
+  // get selection
+  nsCOMPtr selection;
+  nsresult res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+
+  // give rules a chance to handle or cancel
+  nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
+  PRBool cancel, handled;
+  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  if (NS_FAILED(res)) return res;
+  if (cancel) return NS_OK; // rules canceled the operation
+  if (!handled)
+  {
+    nsCOMPtr newNode;
+    nsAutoString tag; tag.AssignWithConversion("blockquote");
+    res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+    if (NS_FAILED(res)) return res;
+    if (!newNode) return NS_ERROR_NULL_POINTER;
+
+    // Try to set type=cite.  Ignore it if this fails.
+    nsCOMPtr newElement (do_QueryInterface(newNode));
+    if (newElement)
+    {
+      nsAutoString type; type.AssignWithConversion("type");
+      nsAutoString cite; cite.AssignWithConversion("cite");
+      newElement->SetAttribute(type, cite);
+    }
+
+    // Set the selection to the underneath the node we just inserted:
+    res = selection->Collapse(newNode, 0);
+    if (NS_FAILED(res))
+    {
+#ifdef DEBUG_akkana
+      printf("Couldn't collapse");
+#endif
+      // XXX: error result:  should res be returned here?
+    }
+
+    res = Paste(aSelectionType);
+  }
+  return res;
+}
+
+//
+// Paste a plaintext quotation
+//
+NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(PRInt32 aSelectionType)
+{
+  // Get Clipboard Service
+  nsresult rv;
+  NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
+  if (NS_FAILED(rv)) return rv;
+
+  // Create generic Transferable for getting the data
+  nsCOMPtr trans;
+  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                          NS_GET_IID(nsITransferable), 
+                                          (void**) getter_AddRefs(trans));
+  if (NS_SUCCEEDED(rv) && trans)
+  {
+    // We only handle plaintext pastes here
+    trans->AddDataFlavor(kUnicodeMime);
+
+    // Get the Data from the clipboard
+    clipboard->GetData(trans, aSelectionType);
+
+    // Now we ask the transferable for the data
+    // it still owns the data, we just have a pointer to it.
+    // If it can't support a "text" output of the data the call will fail
+    nsCOMPtr genericDataObj;
+    PRUint32 len = 0;
+    char* flav = 0;
+    rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj),
+                                   &len);
+    if (NS_FAILED(rv))
+    {
+#ifdef DEBUG_akkana
+      printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv);
+#endif
+      return rv;
+    }
+#ifdef DEBUG_akkana
+    printf("Got flavor [%s]\n", flav);
+#endif
+    nsAutoString flavor; flavor.AssignWithConversion(flav);
+    nsAutoString stuffToPaste;
+    if (flavor.EqualsWithConversion(kUnicodeMime))
+    {
+      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
+      if (textDataObj && len > 0)
+      {
+        PRUnichar* text = nsnull;
+        textDataObj->ToString ( &text );
+        stuffToPaste.Assign ( text, len / 2 );
+        nsAutoEditBatch beginBatching(this);
+        rv = InsertAsPlaintextQuotation(stuffToPaste, 0);
+        if (text)
+          nsMemory::Free(text);
+      }
+    }
+    nsCRT::free(flav);
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText,
+                                              nsIDOMNode **aNodeInserted)
+{
+  if (mFlags & eEditorPlaintextMask)
+    return InsertAsPlaintextQuotation(aQuotedText, aNodeInserted);
+
+  nsAutoString citation;
+  nsAutoString charset;
+  return InsertAsCitedQuotation(aQuotedText, citation, PR_FALSE,
+                                charset, aNodeInserted);
+}
+
+// text insert.
+NS_IMETHODIMP
+nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText,
+                                         nsIDOMNode **aNodeInserted)
+{
+  // We have the text.  Cite it appropriately:
+  nsCOMPtr citer;
+  nsresult rv;
+  NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv);
+  if (NS_FAILED(rv)) return rv;
+
+  char *citationType = 0;
+  rv = prefs->CopyCharPref("mail.compose.citationType", &citationType);
+                          
+  if (NS_SUCCEEDED(rv) && citationType[0])
+  {
+    if (!strncmp(citationType, "aol", 3))
+      citer = new nsAOLCiter;
+    else
+      citer = new nsInternetCiter;
+    PL_strfree(citationType);
+  }
+  else
+    citer = new nsInternetCiter;
+  
+  // Let the citer quote it for us:
+  nsString quotedStuff;
+  rv = citer->GetCiteString(aQuotedText, quotedStuff);
+  if (!NS_SUCCEEDED(rv))
+    return rv;
+
+  // It's best to put a blank line after the quoted text so that mails
+  // written without thinking won't be so ugly.
+  quotedStuff.Append(PRUnichar('\n'));
+
+  nsCOMPtr preNode;
+  // get selection
+  nsCOMPtr selection;
+  rv = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(rv)) return rv;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+  else
+  {
+    nsAutoEditBatch beginBatching(this);
+    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+
+    // give rules a chance to handle or cancel
+    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
+    PRBool cancel, handled;
+    rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+    if (NS_FAILED(rv)) return rv;
+    if (cancel) return NS_OK; // rules canceled the operation
+    if (!handled)
+    {
+      // Wrap the inserted quote in a 
 so it won't be wrapped:
+      nsAutoString tag; tag.AssignWithConversion("pre");
+      rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
+      
+      // If this succeeded, then set selection inside the pre
+      // so the inserted text will end up there.
+      // If it failed, we don't care what the return value was,
+      // but we'll fall through and try to insert the text anyway.
+      if (NS_SUCCEEDED(rv) && preNode)
+      {
+        // Add an attribute on the pre node so we'll know it's a quotation.
+        // Do this after the insertion, so that 
+        nsCOMPtr preElement (do_QueryInterface(preNode));
+        if (preElement)
+        {
+          preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"),
+                                   NS_LITERAL_STRING("true"));
+          // set style to not have unwanted vertical margins
+          preElement->SetAttribute(NS_LITERAL_STRING("style"),
+                                   NS_LITERAL_STRING("margin: 0 0 0 0px;"));
+        }
+
+        // and set the selection inside it:
+        selection->Collapse(preNode, 0);
+      }
+
+      rv = InsertText(quotedStuff.GetUnicode());
+
+      if (aNodeInserted && NS_SUCCEEDED(rv))
+      {
+        *aNodeInserted = preNode;
+        NS_IF_ADDREF(*aNodeInserted);
+      }
+    }
+  }
+    
+  // Set the selection to just after the inserted node:
+  if (NS_SUCCEEDED(rv) && preNode)
+  {
+    nsCOMPtr parent;
+    PRInt32 offset;
+    if (NS_SUCCEEDED(GetNodeLocation(preNode, address_of(parent), &offset)) && parent)
+      selection->Collapse(parent, offset+1);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
+                                     const nsString& aCitation,
+                                     PRBool aInsertHTML,
+                                     const nsString& aCharset,
+                                     nsIDOMNode **aNodeInserted)
+{
+  nsCOMPtr newNode;
+  nsresult res = NS_OK;
+
+  // get selection
+  nsCOMPtr selection;
+  res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+  else
+  {
+    nsAutoEditBatch beginBatching(this);
+    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+
+    // give rules a chance to handle or cancel
+    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
+    PRBool cancel, handled;
+    res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+    if (NS_FAILED(res)) return res;
+    if (cancel) return NS_OK; // rules canceled the operation
+    if (!handled)
+    {
+      nsAutoString tag; tag.AssignWithConversion("blockquote");
+      res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+      if (NS_FAILED(res)) return res;
+      if (!newNode) return NS_ERROR_NULL_POINTER;
+
+      // Try to set type=cite.  Ignore it if this fails.
+      nsCOMPtr newElement (do_QueryInterface(newNode));
+      if (newElement)
+      {
+        nsAutoString type; type.AssignWithConversion("type");
+        nsAutoString cite; cite.AssignWithConversion("cite");
+        newElement->SetAttribute(type, cite);
+
+        if (aCitation.Length() > 0)
+          newElement->SetAttribute(cite, aCitation);
+
+        // Set the selection inside the blockquote so aQuotedText will go there:
+        selection->Collapse(newNode, 0);
+      }
+
+      if (aInsertHTML)
+        res = InsertHTMLWithCharset(aQuotedText, aCharset);
+
+      else
+        res = InsertText(aQuotedText.GetUnicode());  // XXX ignore charset
+
+      if (aNodeInserted)
+      {
+        if (NS_SUCCEEDED(res))
+        {
+          *aNodeInserted = newNode;
+          NS_IF_ADDREF(*aNodeInserted);
+        }
+      }
+    }
+  }
+
+  // Set the selection to just after the inserted node:
+  if (NS_SUCCEEDED(res) && newNode)
+  {
+    nsCOMPtr parent;
+    PRInt32 offset;
+    if (NS_SUCCEEDED(GetNodeLocation(newNode, address_of(parent), &offset)) && parent)
+      selection->Collapse(parent, offset+1);
+  }
+  return res;
+}
+
+nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *nsrange,
+                                                  const nsString& aInputString,
+                                                  const nsString& aContextStr,
+                                                  const nsString& aInfoStr,
+                                                  nsCOMPtr *outFragNode,
+                                                  PRInt32 *outRangeStartHint,
+                                                  PRInt32 *outRangeEndHint)
+{
+  if (!outFragNode || !outRangeStartHint || !outRangeEndHint) 
+    return NS_ERROR_NULL_POINTER;
+  nsCOMPtr docfrag;
+  nsresult res = nsrange->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
+  NS_ENSURE_SUCCESS(res, res);
+  *outFragNode = do_QueryInterface(docfrag);
+
+  res = StripFormattingNodes(*outFragNode);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // if we have context info, create a fragment for that too
+  nsCOMPtr contextfrag;
+  nsCOMPtr contextLeaf;
+  PRInt32 contextDepth = 0;
+  if (aContextStr.Length())
+  {
+    res = nsrange->CreateContextualFragment(aContextStr, getter_AddRefs(contextfrag));
+    NS_ENSURE_SUCCESS(res, res);
+    nsCOMPtr contextAsNode (do_QueryInterface(contextfrag));
+    res = StripFormattingNodes(contextAsNode);
+    NS_ENSURE_SUCCESS(res, res);
+    // cache the deepest leaf in the context
+    nsCOMPtr junk, child, tmp = contextAsNode;
+    while (tmp)
+    {
+      contextDepth++;
+      contextLeaf = tmp;
+      contextLeaf->GetFirstChild(getter_AddRefs(tmp));
+    }
+    // unite the two trees
+    contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk));
+    *outFragNode = contextAsNode;
+    // no longer have fragmentAsNode in tree
+    contextDepth--;
+  }
+  
+  // get the infoString contents
+  nsAutoString numstr1, numstr2;
+  if (aInfoStr.Length())
+  {
+    PRInt32 err, sep;
+    sep = aInfoStr.FindChar((PRUnichar)',');
+    aInfoStr.Left(numstr1, sep);
+    aInfoStr.Mid(numstr2, sep+1, -1);
+    *outRangeStartHint = numstr1.ToInteger(&err) + contextDepth;
+    *outRangeEndHint   = numstr2.ToInteger(&err) + contextDepth;
+  }
+  else
+  {
+    *outRangeStartHint = contextDepth;
+    *outRangeEndHint = contextDepth;
+  }
+  return res;
+}
+
+nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode  *aFragmentAsNode,
+                                                nsCOMPtr *outNodeList,
+                                                PRInt32 aRangeStartHint,
+                                                PRInt32 aRangeEndHint)
+{
+  if (!outNodeList || !aFragmentAsNode) 
+    return NS_ERROR_NULL_POINTER;
+
+  // First off create a range over the portion of docFrag indicated by
+  // the range hints.
+  nsCOMPtr docFragRange;
+  docFragRange = do_CreateInstance(kCRangeCID);
+  nsCOMPtr startParent, endParent, tmp;
+  PRInt32 endOffset;
+  startParent = aFragmentAsNode;
+  while (aRangeStartHint > 0)
+  {
+    startParent->GetFirstChild(getter_AddRefs(tmp));
+    startParent = tmp;
+    aRangeStartHint--;
+    NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
+  }
+  endParent = aFragmentAsNode;
+  while (aRangeEndHint > 0)
+  {
+    endParent->GetLastChild(getter_AddRefs(tmp));
+    endParent = tmp;
+    aRangeEndHint--;
+    NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE);
+  }
+  nsresult res = GetLengthOfDOMNode(endParent, (PRUint32&)endOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  res = docFragRange->SetStart(startParent, 0);
+  NS_ENSURE_SUCCESS(res, res);
+  res = docFragRange->SetEnd(endParent, endOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // now use a subtree iterator over the range to create a list of nodes
+  nsTrivialFunctor functor;
+  nsDOMSubtreeIterator iter;
+  res = NS_NewISupportsArray(getter_AddRefs(*outNodeList));
+  NS_ENSURE_SUCCESS(res, res);
+  res = iter.Init(docFragRange);
+  NS_ENSURE_SUCCESS(res, res);
+  res = iter.AppendList(functor, *outNodeList);
+
+  return res;
+}
+
+
diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp
index f84cd25c849..c188cd3a2e5 100644
--- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -94,11 +94,11 @@ class nsBRNodeFunctor : public nsBoolDomIterFunctor
 class nsEmptyFunctor : public nsBoolDomIterFunctor
 {
   public:
-    nsEmptyFunctor(nsHTMLEditor* editor) : mEditor(editor) {}
+    nsEmptyFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
     virtual PRBool operator()(nsIDOMNode* aNode)  // used to build list of empty li's and td's
     {
       PRBool bIsEmptyNode;
-      nsresult res = mEditor->IsEmptyNode(aNode, &bIsEmptyNode, PR_FALSE, PR_FALSE);
+      nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, PR_FALSE, PR_FALSE);
       if (NS_FAILED(res)) return PR_FALSE;
       if (bIsEmptyNode
         && (nsHTMLEditUtils::IsListItem(aNode) || nsHTMLEditUtils::IsTableCellOrCaption(aNode)))
@@ -108,23 +108,23 @@ class nsEmptyFunctor : public nsBoolDomIterFunctor
       return PR_FALSE;
     }
   protected:
-    nsHTMLEditor* mEditor;
+    nsHTMLEditor* mHTMLEditor;
 };
 
 class nsEditableTextFunctor : public nsBoolDomIterFunctor
 {
   public:
-    nsEditableTextFunctor(nsHTMLEditor* editor) : mEditor(editor) {}
+    nsEditableTextFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
     virtual PRBool operator()(nsIDOMNode* aNode)  // used to build list of empty li's and td's
     {
-      if (nsEditor::IsTextNode(aNode) && mEditor->IsEditable(aNode)) 
+      if (nsEditor::IsTextNode(aNode) && mHTMLEditor->IsEditable(aNode)) 
       {
         return PR_TRUE;
       }
       return PR_FALSE;
     }
   protected:
-    nsHTMLEditor* mEditor;
+    nsHTMLEditor* mHTMLEditor;
 };
 
 
@@ -161,7 +161,7 @@ nsHTMLEditRules::~nsHTMLEditRules()
   // ~nsHTMLEditor, in which case we will get an error here
   // which we ignore.  But this allows us to add the ability to
   // switch rule sets on the fly if we want.
-  mEditor->RemoveEditActionListener(this);
+  mHTMLEditor->RemoveEditActionListener(this);
 }
 
 /********************************************************
@@ -178,10 +178,13 @@ NS_IMPL_QUERY_INTERFACE3(nsHTMLEditRules, nsIHTMLEditRules, nsIEditRules, nsIEdi
  ********************************************************/
 
 NS_IMETHODIMP
-nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
+nsHTMLEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)
 {
-  // call through to base class Init first
-  nsresult res = nsTextEditRules::Init(aEditor, aFlags);
+  mHTMLEditor = NS_STATIC_CAST(nsHTMLEditor*, aEditor);
+  nsresult res;
+  
+  // call through to base class Init 
+  res = nsTextEditRules::Init(aEditor, aFlags);
   if (NS_FAILED(res)) return res;
 
   // cache any prefs we care about
@@ -207,15 +210,11 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
   // make a utility range for use by the listenter
   mUtilRange = do_CreateInstance(kRangeCID);
   if (!mUtilRange) return NS_ERROR_NULL_POINTER;
-  
-  // pass over document and add any needed mozBRs
-  // first turn off undo
-  mEditor->EnableUndo(PR_FALSE);
-  
+   
   // set up mDocChangeRange to be whole doc
   nsCOMPtr bodyElem;
   nsCOMPtr bodyNode;
-  mEditor->GetRootElement(getter_AddRefs(bodyElem));
+  mHTMLEditor->GetRootElement(getter_AddRefs(bodyElem));
   bodyNode = do_QueryInterface(bodyElem);
   if (bodyNode)
   {
@@ -227,17 +226,12 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
       if (!mDocChangeRange) return NS_ERROR_NULL_POINTER;
     }
     mDocChangeRange->SelectNode(bodyNode);
-    res = ReplaceNewlines(mDocChangeRange);
-    if (NS_FAILED(res)) return res;
     res = AdjustSpecialBreaks();
     if (NS_FAILED(res)) return res;
   }
-  
-  // turn on undo
-  mEditor->EnableUndo(PR_TRUE);
-  
+
   // add ourselves as a listener to edit actions
-  res = mEditor->AddEditActionListener(this);
+  res = mHTMLEditor->AddEditActionListener(this);
 
   return res;
 }
@@ -270,7 +264,7 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     }
     // turn off caret
     nsCOMPtr selCon;
-    mEditor->GetSelectionController(getter_AddRefs(selCon));
+    mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
     if (selCon) selCon->SetCaretEnabled(PR_FALSE);
     // check that selection is in subtree defined by body node
     ConfirmSelectionInBody();
@@ -297,7 +291,7 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     res = AfterEditInner(action, aDirection);
     // turn on caret
     nsCOMPtr selCon;
-    mEditor->GetSelectionController(getter_AddRefs(selCon));
+    mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
     if (selCon) selCon->SetCaretEnabled(PR_TRUE);
   }
 
@@ -312,7 +306,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
   if (action == nsEditor::kOpIgnore) return NS_OK;
   
   nsCOMPtrselection;
-  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
   
   // do we have a real range to act on?
@@ -330,7 +324,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
   {
     // dont let any txns in here move the selection around behind our back.
     // Note that this won't prevent explicit selection setting from working.
-    nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
+    nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    
     // expand the "changed doc range" as needed
     res = PromoteRange(mDocChangeRange, action);
@@ -344,7 +338,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
     if ( (action != nsEditor::kOpInsertText &&
          action != nsEditor::kOpInsertIMEText) )
     {
-      res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange);
+      res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
       if (NS_FAILED(res)) return res;
     }
     
@@ -377,7 +371,7 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
     if ((action == nsEditor::kOpInsertText) || 
         (action == nsEditor::kOpInsertIMEText) ||
         (action == nsEditor::kOpDeleteSelection) ||
-        (action == nsEditor::kOpHTMLPaste))
+        (action == nsHTMLEditor::kOpHTMLPaste))
     {
       res = AdjustSelection(selection, aDirection);
       if (NS_FAILED(res)) return res;
@@ -451,6 +445,8 @@ nsHTMLEditRules::DidDoAction(nsISelection *aSelection,
   nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo);
   switch (info->action)
   {
+    case kInsertBreak:
+      return DidInsertBreak(aSelection, aResult);
     case kMakeBasicBlock:
     case kIndent:
     case kOutdent:
@@ -488,11 +484,11 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL, PRBool &
     nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
     nsCOMPtr curNode( do_QueryInterface(isupports) );
     
-    if (mEditor->NodeIsType(curNode,nsIEditProperty::ul))
+    if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ul))
       aUL = PR_TRUE;
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::ol))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ol))
       aOL = PR_TRUE;
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::li))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::li))
     {
       nsCOMPtr parent;
       PRInt32 offset;
@@ -503,9 +499,9 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL, PRBool &
       else if (nsHTMLEditUtils::IsOrderedList(parent))
         aOL = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dl) ||
-             mEditor->NodeIsType(curNode,nsIEditProperty::dt) ||
-             mEditor->NodeIsType(curNode,nsIEditProperty::dd) )
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dl) ||
+             mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dt) ||
+             mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dd) )
     {
       aDL = PR_TRUE;
     }
@@ -540,21 +536,21 @@ nsHTMLEditRules::GetListItemState(PRBool &aMixed, PRBool &aLI, PRBool &aDT, PRBo
     nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
     nsCOMPtr curNode( do_QueryInterface(isupports) );
     
-    if (mEditor->NodeIsType(curNode,nsIEditProperty::ul) ||
-        mEditor->NodeIsType(curNode,nsIEditProperty::ol) ||
-        mEditor->NodeIsType(curNode,nsIEditProperty::li) )
+    if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ul) ||
+        mHTMLEditor->NodeIsType(curNode,nsIEditProperty::ol) ||
+        mHTMLEditor->NodeIsType(curNode,nsIEditProperty::li) )
     {
       aLI = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dt))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dt))
     {
       aDT = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dd))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dd))
     {
       aDD = PR_TRUE;
     }
-    else if (mEditor->NodeIsType(curNode,nsIEditProperty::dl))
+    else if (mHTMLEditor->NodeIsType(curNode,nsIEditProperty::dl))
     {
       // need to look inside dl and see which types of items it has
       PRBool bDT, bDD;
@@ -589,13 +585,13 @@ nsHTMLEditRules::GetAlignment(PRBool &aMixed, nsIHTMLEditor::EAlignment &aAlign)
   
   // get selection
   nsCOMPtrselection;
-  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
 
   // get selection location
   nsCOMPtr parent;
   PRInt32 offset;
-  res = mEditor->GetStartNodeAndOffset(selection, address_of(parent), &offset);
+  res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(parent), &offset);
   if (NS_FAILED(res)) return res;
   
   // is the selection collapsed?
@@ -609,7 +605,7 @@ nsHTMLEditRules::GetAlignment(PRBool &aMixed, nsIHTMLEditor::EAlignment &aAlign)
     // for divs with alignment on them
     nodeToExamine = parent;
   }
-  else if (mEditor->IsTextNode(parent)) 
+  else if (mHTMLEditor->IsTextNode(parent)) 
   {
     // if we are in a text node, then that is the node of interest
     nodeToExamine = parent;
@@ -618,7 +614,7 @@ nsHTMLEditRules::GetAlignment(PRBool &aMixed, nsIHTMLEditor::EAlignment &aAlign)
   {
     // otherwise we want to look at the first editable node after
     // {parent,offset} and it's ancestors for divs with alignment on them
-    mEditor->GetNextNode(parent, offset, PR_TRUE, getter_AddRefs(nodeToExamine));
+    mHTMLEditor->GetNextNode(parent, offset, PR_TRUE, getter_AddRefs(nodeToExamine));
   }
   
   if (!nodeToExamine) return NS_ERROR_NULL_POINTER;
@@ -719,9 +715,9 @@ nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat)
     nsCOMPtr selNode;
     PRInt32 selOffset;
     nsCOMPtrselection;
-    res = mEditor->GetSelection(getter_AddRefs(selection));
+    res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
     if (NS_FAILED(res)) return res;
-    res = mEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
+    res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
     if (NS_FAILED(res)) return res;
     isupports = do_QueryInterface(selNode);
     if (!isupports) return NS_ERROR_NULL_POINTER;
@@ -737,14 +733,14 @@ nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat)
     nsCOMPtr curNode( do_QueryInterface(isupports) );
 
     nsAutoString format;
-    nsCOMPtr atom = mEditor->GetTag(curNode);
+    nsCOMPtr atom = mHTMLEditor->GetTag(curNode);
     
-    if (mEditor->IsInlineNode(curNode))
+    if (mHTMLEditor->IsInlineNode(curNode))
     {
-      nsCOMPtr block = mEditor->GetBlockNodeParent(curNode);
+      nsCOMPtr block = mHTMLEditor->GetBlockNodeParent(curNode);
       if (block)
       {
-        nsCOMPtr blockAtom = mEditor->GetTag(block);
+        nsCOMPtr blockAtom = mHTMLEditor->GetTag(block);
         if ( nsIEditProperty::p == blockAtom.get()           ||
              nsIEditProperty::blockquote == blockAtom.get()  ||
              nsIEditProperty::address == blockAtom.get()     ||
@@ -811,9 +807,45 @@ nsHTMLEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel)
   nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel);
   if (NS_FAILED(res)) return res; 
   
+  // Adjust selection to prevent insertion after a moz-BR.
+  // this next only works for collapsed selections right now,
+  // because selection is a pain to work with when not collapsed.
+  // (no good way to extend start or end of selection)
+  PRBool bCollapsed;
+  res = aSelection->GetIsCollapsed(&bCollapsed);
+  if (NS_FAILED(res)) return res;
+  if (!bCollapsed) return NS_OK;
+
+  // if we are after a mozBR in the same block, then move selection
+  // to be before it
+  nsCOMPtr selNode, priorNode;
+  PRInt32 selOffset;
+  // get the (collapsed) selection location
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
+  if (NS_FAILED(res)) return res;
+  // get prior node
+  res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, &priorNode);
+  if (NS_SUCCEEDED(res) && priorNode && nsHTMLEditUtils::IsMozBR(priorNode))
+  {
+    nsCOMPtr block1, block2;
+    if (mHTMLEditor->IsBlockNode(selNode)) block1 = selNode;
+    else block1 = mHTMLEditor->GetBlockNodeParent(selNode);
+    block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
+  
+    if (block1 != block2) return NS_OK; 
+  
+    // if we are here then the selection is right after a mozBR
+    // that is in the same block as the selection.  We need to move
+    // the selection start to be before the mozBR.
+    res = nsEditor::GetNodeLocation(priorNode, &selNode, &selOffset);
+    if (NS_FAILED(res)) return res;
+    res = aSelection->Collapse(selNode,selOffset);
+    if (NS_FAILED(res)) return res;
+  }
+
   // we need to get the doc
   nsCOMPtrdoc;
-  res = mEditor->GetDocument(getter_AddRefs(doc));
+  res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
   if (NS_FAILED(res)) return res;
   if (!doc) return NS_ERROR_NULL_POINTER;
     
@@ -864,7 +896,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
   if (NS_FAILED(res)) return res;
   if (!bCollapsed)
   {
-    res = mEditor->DeleteSelection(nsIEditor::eNone);
+    res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
     if (NS_FAILED(res)) return res;
   }
 
@@ -875,23 +907,23 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
   *aCancel = PR_FALSE;
   
   // get the (collapsed) selection location
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
   if (NS_FAILED(res)) return res;
 
   // dont put text in places that cant have it
   nsAutoString textTag; textTag.AssignWithConversion("__moz_text");
-  if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, textTag))
+  if (!mHTMLEditor->IsTextNode(selNode) && !mHTMLEditor->CanContainTag(selNode, textTag))
     return NS_ERROR_FAILURE;
 
   // we need to get the doc
   nsCOMPtrdoc;
-  res = mEditor->GetDocument(getter_AddRefs(doc));
+  res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
   if (NS_FAILED(res)) return res;
   if (!doc) return NS_ERROR_NULL_POINTER;
     
   if (aAction == kInsertTextIME) 
   { 
-    res = mEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
+    res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
     if (NS_FAILED(res)) return res;
   }
   else // aAction == kInsertText
@@ -903,7 +935,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
     // is our text going to be PREformatted?  
     // We remember this so that we know how to handle tabs.
     PRBool isPRE;
-    res = mEditor->IsPreformatted(selNode, &isPRE);
+    res = mHTMLEditor->IsPreformatted(selNode, &isPRE);
     if (NS_FAILED(res)) return res;    
     
     // turn off the edit listener: we know how to
@@ -913,7 +945,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
     nsAutoLockListener lockit(&mListenerEnabled); 
     
     // dont spaz my selection in subtransactions
-    nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
+    nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
     nsSubsumeStr subStr;
     const PRUnichar *unicodeBuf = inString->GetUnicode();
     nsCOMPtr unused;
@@ -949,12 +981,12 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         // is it a return?
         if (subStr.EqualsWithConversion("\n"))
         {
-          res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
+          res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
           pos++;
         }
         else
         {
-          res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
+          res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
         }
         if (NS_FAILED(res)) return res;
       }
@@ -987,18 +1019,18 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         // is it a tab?
         if (subStr.EqualsWithConversion("\t"))
         {
-          res = mEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
+          res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
           pos++;
         }
         // is it a return?
         else if (subStr.EqualsWithConversion("\n"))
         {
-          res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
+          res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
           pos++;
         }
         else
         {
-          res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
+          res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
         }
         if (NS_FAILED(res)) return res;
       }
@@ -1039,7 +1071,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   if (NS_FAILED(res)) return res;
   if (!bCollapsed)
   {
-    res = mEditor->DeleteSelection(nsIEditor::eNone);
+    res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
     if (NS_FAILED(res)) return res;
   }
   
@@ -1049,26 +1081,26 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = PR_FALSE;
-
-
-  // split any mailcites in the way
-  if (mFlags & nsIHTMLEditor::eEditorMailMask)
+  
+  // split any mailcites in the way.
+  // should we abort this if we encounter table cell boundaries?
+  if (mFlags & nsIPlaintextEditor::eEditorMailMask)
   {
     nsCOMPtr citeNode, selNode;
     PRInt32 selOffset, newOffset;
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
     if (NS_FAILED(res)) return res;
-    res = GetTopEnclosingMailCite(selNode, address_of(citeNode));
+    PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
+    res = GetTopEnclosingMailCite(selNode, address_of(citeNode), bPlaintext);
     if (NS_FAILED(res)) return res;
-    
     if (citeNode)
     {
       nsCOMPtr brNode;
-      res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
+      res = mHTMLEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
       if (NS_FAILED(res)) return res;
       res = citeNode->GetParentNode(getter_AddRefs(selNode));
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateBR(selNode, newOffset, address_of(brNode));
+      res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
       if (NS_FAILED(res)) return res;
       // want selection before the break, and on same line
       selPriv->SetInterlinePosition(PR_TRUE);
@@ -1083,7 +1115,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   nsCOMPtr node;
   PRInt32 offset;
   
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
   if (NS_FAILED(res)) return res;
   if (!node) return NS_ERROR_FAILURE;
     
@@ -1093,7 +1125,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   if (nsEditor::IsBlockNode(node)) 
     blockParent = node;
   else 
-    blockParent = mEditor->GetBlockNodeParent(node);
+    blockParent = mHTMLEditor->GetBlockNodeParent(node);
     
   if (!blockParent) return NS_ERROR_FAILURE;
   
@@ -1123,7 +1155,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
   else
   {
     nsCOMPtr brNode;
-    res = mEditor->CreateBR(node, offset, address_of(brNode));
+    res = mHTMLEditor->CreateBR(node, offset, address_of(brNode));
     if (NS_FAILED(res)) return res;
     res = nsEditor::GetNodeLocation(brNode, address_of(node), &offset);
     if (NS_FAILED(res)) return res;
@@ -1136,7 +1168,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
     // Then we stick to the left to aviod an uber caret.
     nsCOMPtr siblingNode;
     brNode->GetNextSibling(getter_AddRefs(siblingNode));
-    if (siblingNode && mEditor->IsBlockNode(siblingNode))
+    if (siblingNode && mHTMLEditor->IsBlockNode(siblingNode))
       selPriv->SetInterlinePosition(PR_FALSE);
     else 
       selPriv->SetInterlinePosition(PR_TRUE);
@@ -1149,6 +1181,12 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
 }
 
 
+nsresult
+nsHTMLEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
+{
+  return NS_OK;
+}
+
 
 nsresult
 nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, 
@@ -1182,22 +1220,22 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
   // first check for table selection mode.  If so,
   // hand off to table editor.
   nsCOMPtr cell;
-  res = mEditor->GetFirstSelectedCell(getter_AddRefs(cell), nsnull);
+  res = mHTMLEditor->GetFirstSelectedCell(getter_AddRefs(cell), nsnull);
   if (NS_SUCCEEDED(res) && cell)
   {
-    res = mEditor->DeleteTableCellContents();
+    res = mHTMLEditor->DeleteTableCellContents();
     *aHandled = PR_TRUE;
     return res;
   }
   
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
   if (NS_FAILED(res)) return res;
   if (!startNode) return NS_ERROR_FAILURE;
     
   if (bCollapsed)
   {
     // easy case, in a text node:
-    if (mEditor->IsTextNode(startNode))
+    if (mHTMLEditor->IsTextNode(startNode))
     {
       nsCOMPtr textNode = do_QueryInterface(startNode);
       PRUint32 strLength;
@@ -1208,7 +1246,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       if (!startOffset && (aAction == nsIEditor::ePrevious))
       {
         nsCOMPtr priorNode;
-        res = mEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
+        res = mHTMLEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
         if (NS_FAILED(res)) return res;
         
         // if there is no prior node then cancel the deletion
@@ -1219,24 +1257,24 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
         
         // block parents the same?  
-        if (mEditor->HasSameBlockNodeParent(startNode, priorNode)) 
+        if (mHTMLEditor->HasSameBlockNodeParent(startNode, priorNode)) 
         {
           // is prior node not a container?  (ie, a br, hr, image...)
-          if (!mEditor->IsContainer(priorNode))   // MOOSE: anchors not handled
+          if (!mHTMLEditor->IsContainer(priorNode))   // MOOSE: anchors not handled
           {
             // delete the break, and join like nodes if appropriate
-            res = mEditor->DeleteNode(priorNode);
+            res = mHTMLEditor->DeleteNode(priorNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
             // get new prior node
-            res = mEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
+            res = mHTMLEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
             if (NS_FAILED(res)) return res;
             // are they in same block?
-            if (mEditor->HasSameBlockNodeParent(startNode, priorNode)) 
+            if (mHTMLEditor->HasSameBlockNodeParent(startNode, priorNode)) 
             {
               // are they same type?
-              if (mEditor->IsTextNode(priorNode))
+              if (mHTMLEditor->IsTextNode(priorNode))
               {
                 // if so, join them!
                 nsCOMPtr topParent;
@@ -1250,7 +1288,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             return res;
           }
           // is prior node a text node?
-          else if ( mEditor->IsTextNode(priorNode) )
+          else if ( mHTMLEditor->IsTextNode(priorNode) )
           {
             // delete last character
             PRUint32 offset;
@@ -1262,15 +1300,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             // default code will take care of actual deletion
             return res;
           }
-          else if ( mEditor->IsInlineNode(priorNode) )
+          else if ( mHTMLEditor->IsInlineNode(priorNode) )
           {
             // remember where we are
             PRInt32 offset;
             nsCOMPtr node;
-            res = mEditor->GetNodeLocation(priorNode, address_of(node), &offset);
+            res = mHTMLEditor->GetNodeLocation(priorNode, address_of(node), &offset);
             if (NS_FAILED(res)) return res;
             // delete it
-            res = mEditor->DeleteNode(priorNode);
+            res = mHTMLEditor->DeleteNode(priorNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
@@ -1282,8 +1320,8 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
         
         // deleting across blocks
-        nsCOMPtr leftParent = mEditor->GetBlockNodeParent(priorNode);
-        nsCOMPtr rightParent = mEditor->GetBlockNodeParent(startNode);
+        nsCOMPtr leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
+        nsCOMPtr rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
         
         // if leftParent or rightParent is null, it's because the
         // corresponding selection endpoint is in the body node.
@@ -1298,7 +1336,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
                 
         // are the blocks of same type?
-        if (mEditor->NodesSameType(leftParent, rightParent))
+        if (mHTMLEditor->NodesSameType(leftParent, rightParent))
         {
           nsCOMPtr topParent;
           leftParent->GetParentNode(getter_AddRefs(topParent));
@@ -1321,35 +1359,35 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
           && (aAction == nsIEditor::eNext))
       {
         nsCOMPtr nextNode;
-        res = mEditor->GetNextHTMLNode(startNode, address_of(nextNode));
+        res = mHTMLEditor->GetNextHTMLNode(startNode, address_of(nextNode));
         if (NS_FAILED(res)) return res;
          
         // if there is no next node, or it's not in the body, then cancel the deletion
-        if (!nextNode || !nsHTMLEditUtils::InBody(nextNode, mEditor))
+        if (!nextNode || !nsHTMLEditUtils::InBody(nextNode, mHTMLEditor))
         {
           *aCancel = PR_TRUE;
           return res;
         }
        
         // block parents the same?  
-        if (mEditor->HasSameBlockNodeParent(startNode, nextNode)) 
+        if (mHTMLEditor->HasSameBlockNodeParent(startNode, nextNode)) 
         {
           // is next node not a container?  (ie, a br, hr, image...)
-          if (!mEditor->IsContainer(nextNode))  // MOOSE: anchors not handled
+          if (!mHTMLEditor->IsContainer(nextNode))  // MOOSE: anchors not handled
           {
             // delete the break, and join like nodes if appropriate
-            res = mEditor->DeleteNode(nextNode);
+            res = mHTMLEditor->DeleteNode(nextNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
             // get new next node
-            res = mEditor->GetNextHTMLNode(startNode, address_of(nextNode));
+            res = mHTMLEditor->GetNextHTMLNode(startNode, address_of(nextNode));
             if (NS_FAILED(res)) return res;
             // are they in same block?
-            if (mEditor->HasSameBlockNodeParent(startNode, nextNode)) 
+            if (mHTMLEditor->HasSameBlockNodeParent(startNode, nextNode)) 
             {
               // are they same type?
-              if ( mEditor->IsTextNode(nextNode) )
+              if ( mHTMLEditor->IsTextNode(nextNode) )
               {
                 // if so, join them!
                 nsCOMPtr topParent;
@@ -1363,7 +1401,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             return res;
           }
           // is next node a text node?
-          else if ( mEditor->IsTextNode(nextNode) )
+          else if ( mHTMLEditor->IsTextNode(nextNode) )
           {
             // delete first character
             nsCOMPtrnodeAsText;
@@ -1373,15 +1411,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             // default code will take care of actual deletion
             return res;
           }
-          else if ( mEditor->IsInlineNode(nextNode) )
+          else if ( mHTMLEditor->IsInlineNode(nextNode) )
           {
             PRInt32 offset;
             nsCOMPtr node;
             // remember where we are
-            res = mEditor->GetNodeLocation(nextNode, address_of(node), &offset);
+            res = mHTMLEditor->GetNodeLocation(nextNode, address_of(node), &offset);
             if (NS_FAILED(res)) return res;
             // delete it
-            res = mEditor->DeleteNode(nextNode);
+            res = mHTMLEditor->DeleteNode(nextNode);
             if (NS_FAILED(res)) return res;
             // we did something, so lets say so.
             *aHandled = PR_TRUE;
@@ -1393,8 +1431,8 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
                 
         // deleting across blocks
-        nsCOMPtr leftParent = mEditor->GetBlockNodeParent(startNode);
-        nsCOMPtr rightParent = mEditor->GetBlockNodeParent(nextNode);
+        nsCOMPtr leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
+        nsCOMPtr rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
 
         // if leftParent or rightParent is null, it's because the
         // corresponding selection endpoint is in the body node.
@@ -1409,7 +1447,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         }
                 
         // are the blocks of same type?
-        if (mEditor->NodesSameType(leftParent, rightParent))
+        if (mHTMLEditor->NodesSameType(leftParent, rightParent))
         {
           nsCOMPtr topParent;
           leftParent->GetParentNode(getter_AddRefs(topParent));
@@ -1439,7 +1477,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       // see if we are in an "empty" node.
       // Note: do NOT delete table elements this way.
       PRBool bIsEmptyNode;
-      res = mEditor->IsEmptyNode(startNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
+      res = mHTMLEditor->IsEmptyNode(startNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
       if (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(startNode))
         nodeToDelete = startNode;
       else 
@@ -1455,16 +1493,16 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         
         if (aAction == nsIEditor::ePrevious)
         {
-          res = mEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBlock));
+          res = mHTMLEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBlock));
           if (NS_FAILED(res)) return res;
-          res = mEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBreak));
+          res = mHTMLEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBreak));
           if (NS_FAILED(res)) return res;
         }
         else if (aAction == nsIEditor::eNext)
         {
-          res = mEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBreak));
+          res = mHTMLEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBreak));
           if (NS_FAILED(res)) return res;
-          res = mEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBlock));
+          res = mHTMLEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBlock));
           if (NS_FAILED(res)) return res;
         }
         
@@ -1472,9 +1510,9 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
             nsHTMLEditUtils::IsBreak(maybeBreak) && nsEditor::IsBlockNode(maybeBlock))
           nodeToDelete = maybeBreak;
         else if (aAction == nsIEditor::ePrevious)
-          res = mEditor->GetPriorHTMLNode(startNode, startOffset, address_of(nodeToDelete));
+          res = mHTMLEditor->GetPriorHTMLNode(startNode, startOffset, address_of(nodeToDelete));
         else if (aAction == nsIEditor::eNext)
-          res = mEditor->GetNextHTMLNode(startNode, startOffset, address_of(nodeToDelete));
+          res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(nodeToDelete));
         else
           return NS_OK;
       } 
@@ -1513,19 +1551,19 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         if (nsHTMLEditUtils::IsMozBR(nodeToDelete))
         {
           nsCOMPtr brNode;
-          res = mEditor->GetPriorHTMLNode(nodeToDelete, address_of(brNode));
+          res = mHTMLEditor->GetPriorHTMLNode(nodeToDelete, address_of(brNode));
           if (nsHTMLEditUtils::IsBreak(brNode))
           {
             // is brNode also a descendant of same block?
             nsCOMPtr block, brBlock;
-            block = mEditor->GetBlockNodeParent(nodeToDelete);
-            brBlock = mEditor->GetBlockNodeParent(brNode);
+            block = mHTMLEditor->GetBlockNodeParent(nodeToDelete);
+            brBlock = mHTMLEditor->GetBlockNodeParent(brNode);
             if (block == brBlock)
             {
               // delete both breaks
-              res = mEditor->DeleteNode(brNode);
+              res = mHTMLEditor->DeleteNode(brNode);
               if (NS_FAILED(res)) return res;
-              res = mEditor->DeleteNode(nodeToDelete);
+              res = mHTMLEditor->DeleteNode(nodeToDelete);
               *aHandled = PR_TRUE;
               return res;
             }
@@ -1536,7 +1574,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
         // adjust selection to be right after it
         res = aSelection->Collapse(node, offset+1);
         if (NS_FAILED(res)) return res;
-        res = mEditor->DeleteNode(nodeToDelete);
+        res = mHTMLEditor->DeleteNode(nodeToDelete);
         *aHandled = PR_TRUE;
         return res;
       }
@@ -1549,7 +1587,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
   // figure out if the enpoints are in nodes that can be merged
   nsCOMPtr endNode;
   PRInt32 endOffset;
-  res = mEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
+  res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
   if (NS_FAILED(res)) 
   { 
     return res; 
@@ -1557,7 +1595,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
   if (endNode.get() != startNode.get())
   {
     // block parents the same?  use default deletion
-    if (mEditor->HasSameBlockNodeParent(startNode, endNode)) return NS_OK;
+    if (mHTMLEditor->HasSameBlockNodeParent(startNode, endNode)) return NS_OK;
     
     // deleting across blocks
     // are the blocks of same type?
@@ -1574,12 +1612,12 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
     if (nsHTMLEditUtils::IsBody(startNode))
       leftParent = startNode;
     else
-      leftParent = mEditor->GetBlockNodeParent(startNode);
+      leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
 
     if (nsHTMLEditUtils::IsBody(endNode))
       rightParent = endNode;
     else
-      rightParent = mEditor->GetBlockNodeParent(endNode);
+      rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
     
     // are the blocks siblings?
     nsCOMPtr leftBlockParent;
@@ -1589,7 +1627,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
 
     // MOOSE: this could conceivably screw up a table.. fix me.
     if (   (leftBlockParent.get() == rightBlockParent.get())
-        && (mEditor->NodesSameType(leftParent, rightParent))  )
+        && (mHTMLEditor->NodesSameType(leftParent, rightParent))  )
     {
       nsCOMPtr topParent;
       leftParent->GetParentNode(getter_AddRefs(topParent));
@@ -1598,10 +1636,10 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       {
         // first delete the selection
         *aHandled = PR_TRUE;
-        res = mEditor->DeleteSelectionImpl(aAction);
+        res = mHTMLEditor->DeleteSelectionImpl(aAction);
         if (NS_FAILED(res)) return res;
         // then join para's, insert break
-        res = mEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
+        res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
         if (NS_FAILED(res)) return res;
         // fix up selection
         res = aSelection->Collapse(selNode,selOffset);
@@ -1612,10 +1650,10 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       {
         // first delete the selection
         *aHandled = PR_TRUE;
-        res = mEditor->DeleteSelectionImpl(aAction);
+        res = mHTMLEditor->DeleteSelectionImpl(aAction);
         if (NS_FAILED(res)) return res;
         // join blocks
-        res = mEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
+        res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
         if (NS_FAILED(res)) return res;
         // fix up selection
         res = aSelection->Collapse(selNode,selOffset);
@@ -1669,7 +1707,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
     // delete to end or to begining as appropriate,
     // since the case where both sel endpoints in same
     // text node was already handled (we wouldn't be here)
-    if ( mEditor->IsTextNode(startNode) )
+    if ( mHTMLEditor->IsTextNode(startNode) )
     {
       // delete to last character
       nsCOMPtrnodeAsText;
@@ -1678,18 +1716,18 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
       nodeAsText->GetLength(&len);
       if (len > (PRUint32)startOffset)
       {
-        res = mEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
+        res = mHTMLEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
         if (NS_FAILED(res)) return res;
       }
     }
-    if ( mEditor->IsTextNode(endNode) )
+    if ( mHTMLEditor->IsTextNode(endNode) )
     {
       // delete to first character
       nsCOMPtrnodeAsText;
       nodeAsText = do_QueryInterface(endNode);
       if (endOffset)
       {
-        res = mEditor->DeleteText(nodeAsText,0,endOffset);
+        res = mHTMLEditor->DeleteText(nodeAsText,0,endOffset);
         if (NS_FAILED(res)) return res;
       }
     }
@@ -1733,7 +1771,7 @@ nsHTMLEditRules::DeleteNonTableElements(nsIDOMNode *aNode)
   }
   else
   {
-    res = mEditor->DeleteNode(aNode);
+    res = mHTMLEditor->DeleteNode(aNode);
     if (NS_FAILED(res)) return res;
   }
   return res;
@@ -1773,7 +1811,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
   
   *aHandled = PR_TRUE;
 
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   nsCOMPtr arrayOfNodes;
   res = GetListActionNodes(address_of(arrayOfNodes), aEntireList);
@@ -1810,21 +1848,21 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
       {
         nsCOMPtr isupports  = (dont_AddRef)(arrayOfNodes->ElementAt(j));
         nsCOMPtr curNode( do_QueryInterface(isupports ) );
-        res = mEditor->DeleteNode(curNode);
+        res = mHTMLEditor->DeleteNode(curNode);
         if (NS_FAILED(res)) return res;
       }
     }
     
     // get selection location
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     
     // make sure we can put a list here
     res = SplitAsNeeded(aListType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
+    res = mHTMLEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
+    res = mHTMLEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
     if (NS_FAILED(res)) return res;
     // put selection in new list item
     res = aSelection->Collapse(theListItem,0);
@@ -1846,7 +1884,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     {
       // dive as long as there is only one child, and it is a list, div, blockquote
       PRUint32 numChildren;
-      res = mEditor->CountEditableChildren(curNode, numChildren);
+      res = mHTMLEditor->CountEditableChildren(curNode, numChildren);
       if (NS_FAILED(res)) return res;
       
       if (numChildren == 1)
@@ -1899,20 +1937,20 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     // if curNode is a Break, delete it, and quit remembering prev list item
     if (nsHTMLEditUtils::IsBreak(curNode)) 
     {
-      res = mEditor->DeleteNode(curNode);
+      res = mHTMLEditor->DeleteNode(curNode);
       if (NS_FAILED(res)) return res;
       prevListItem = 0;
       continue;
     }
     // if curNode is an empty inline container, delete it
-    else if (mEditor->IsInlineNode(curNode) && mEditor->IsContainer(curNode)) 
+    else if (mHTMLEditor->IsInlineNode(curNode) && mHTMLEditor->IsContainer(curNode)) 
     {
       PRBool bEmpty;
-      res = mEditor->IsEmptyNode(curNode, &bEmpty);
+      res = mHTMLEditor->IsEmptyNode(curNode, &bEmpty);
       if (NS_FAILED(res)) return res;
       if (bEmpty)
       {
-        res = mEditor->DeleteNode(curNode);
+        res = mHTMLEditor->DeleteNode(curNode);
         if (NS_FAILED(res)) return res;
         continue;
       }
@@ -1921,7 +1959,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     if (nsHTMLEditUtils::IsList(curNode))
     {
       nsAutoString existingListStr;
-      res = mEditor->GetTagString(curNode, existingListStr);
+      res = mHTMLEditor->GetTagString(curNode, existingListStr);
       existingListStr.ToLowerCase();
       // do we have a curList already?
       if (curList && !nsHTMLEditUtils::IsDescendantOf(curNode, curList))
@@ -1931,11 +1969,11 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // RemoveContainer() on the list.
         // ConvertListType first: that routine
         // handles converting the list item types, if needed
-        res = mEditor->MoveNode(curNode, curList, -1);
+        res = mHTMLEditor->MoveNode(curNode, curList, -1);
         if (NS_FAILED(res)) return res;
         res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
         if (NS_FAILED(res)) return res;
-        res = mEditor->RemoveContainer(newBlock);
+        res = mHTMLEditor->RemoveContainer(newBlock);
         if (NS_FAILED(res)) return res;
       }
       else
@@ -1951,7 +1989,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     if (nsHTMLEditUtils::IsListItem(curNode))
     {
       nsAutoString existingListStr;
-      res = mEditor->GetTagString(curParent, existingListStr);
+      res = mHTMLEditor->GetTagString(curParent, existingListStr);
       existingListStr.ToLowerCase();
       if ( existingListStr != *aListType )
       {
@@ -1960,22 +1998,22 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // and make a new list of correct type.
         if (!curList || nsHTMLEditUtils::IsDescendantOf(curNode, curList))
         {
-          res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
+          res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
           if (NS_FAILED(res)) return res;
           nsCOMPtr p;
           PRInt32 o;
           res = nsEditor::GetNodeLocation(curParent, address_of(p), &o);
           if (NS_FAILED(res)) return res;
-          res = mEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
+          res = mHTMLEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
           if (NS_FAILED(res)) return res;
         }
         // move list item to new list
-        res = mEditor->MoveNode(curNode, curList, -1);
+        res = mHTMLEditor->MoveNode(curNode, curList, -1);
         if (NS_FAILED(res)) return res;
         // convert list item type if needed
-        if (!mEditor->NodeIsType(curNode,itemType))
+        if (!mHTMLEditor->NodeIsType(curNode,itemType))
         {
-          res = mEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
+          res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
           if (NS_FAILED(res)) return res;
         }
       }
@@ -1990,13 +2028,13 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
           if (curParent != curList)
           {
             // move list item to new list
-            res = mEditor->MoveNode(curNode, curList, -1);
+            res = mHTMLEditor->MoveNode(curNode, curList, -1);
             if (NS_FAILED(res)) return res;
           }
         }
-        if (!mEditor->NodeIsType(curNode,itemType))
+        if (!mHTMLEditor->NodeIsType(curNode,itemType))
         {
-          res = mEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
+          res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
           if (NS_FAILED(res)) return res;
         }
       }
@@ -2009,7 +2047,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     {
       res = SplitAsNeeded(aListType, address_of(curParent), &offset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
+      res = mHTMLEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
       if (NS_FAILED(res)) return res;
       // curList is now the correct thing to put curNode in
       prevListItem = 0;
@@ -2024,9 +2062,9 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // this is a continuation of some inline nodes that belong together in
         // the same list item.  use prevListItem
         PRUint32 listItemLen;
-        res = mEditor->GetLengthOfDOMNode(prevListItem, listItemLen);
+        res = mHTMLEditor->GetLengthOfDOMNode(prevListItem, listItemLen);
         if (NS_FAILED(res)) return res;
-        res = mEditor->MoveNode(curNode, prevListItem, listItemLen);
+        res = mHTMLEditor->MoveNode(curNode, prevListItem, listItemLen);
         if (NS_FAILED(res)) return res;
       }
       else
@@ -2034,11 +2072,11 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         // don't wrap li around a paragraph.  instead replace paragraph with li
         if (nsHTMLEditUtils::IsParagraph(curNode))
         {
-          res = mEditor->ReplaceContainer(curNode, address_of(listItem), itemType);
+          res = mHTMLEditor->ReplaceContainer(curNode, address_of(listItem), itemType);
         }
         else
         {
-          res = mEditor->InsertContainerAbove(curNode, address_of(listItem), itemType);
+          res = mHTMLEditor->InsertContainerAbove(curNode, address_of(listItem), itemType);
         }
         if (NS_FAILED(res)) return res;
         if (nsEditor::IsInlineNode(curNode)) 
@@ -2054,9 +2092,9 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
     {
       // tuck the listItem into the end of the active list
       PRUint32 listLen;
-      res = mEditor->GetLengthOfDOMNode(curList, listLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(listItem, curList, listLen);
+      res = mHTMLEditor->MoveNode(listItem, curList, listLen);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2076,7 +2114,7 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
   *aCancel = PR_FALSE;
   *aHandled = PR_TRUE;
   
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   
   nsCOMPtr arrayOfRanges;
   nsresult res = GetPromotedRanges(aSelection, address_of(arrayOfRanges), kMakeList);
@@ -2096,7 +2134,7 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
   {
     nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
     nsCOMPtr testNode( do_QueryInterface(isupports ) );
-    if (!mEditor->IsEditable(testNode))
+    if (!mHTMLEditor->IsEditable(testNode))
     {
       arrayOfNodes->RemoveElementAt(i);
     }
@@ -2141,13 +2179,13 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
         else
         {
           // delete any non- list items for now
-          res = mEditor->DeleteNode(child);
+          res = mHTMLEditor->DeleteNode(child);
           if (NS_FAILED(res)) return res;
         }
         curNode->GetLastChild(getter_AddRefs(child));
       }
       // delete the now-empty list
-      res = mEditor->DeleteNode(curNode);
+      res = mHTMLEditor->DeleteNode(curNode);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2184,8 +2222,8 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = PR_FALSE;
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
-  nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
+  nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
   *aHandled = PR_TRUE;
 
   nsCOMPtr arrayOfRanges;
@@ -2206,13 +2244,13 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
     PRInt32 offset;
     
     // get selection location
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     
     // make sure we can put a block here
     res = SplitAsNeeded(aBlockType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
+    res = mHTMLEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
     if (NS_FAILED(res)) return res;
     // put selection in new block
     res = aSelection->Collapse(theBlock,0);
@@ -2266,7 +2304,7 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
   *aCancel = PR_FALSE;
   *aHandled = PR_TRUE;
 
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
@@ -2292,13 +2330,13 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
     nsAutoString quoteType; quoteType.AssignWithConversion("blockquote");
     
     // get selection location
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     
     // make sure we can put a block here
     res = SplitAsNeeded("eType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
+    res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
     if (NS_FAILED(res)) return res;
     // put selection in new block
     res = aSelection->Collapse(theBlock,0);
@@ -2341,15 +2379,15 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         // create a new nested list of correct type
         res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
         if (NS_FAILED(res)) return res;
-        res = mEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
+        res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
         if (NS_FAILED(res)) return res;
         // curList is now the correct thing to put curNode in
       }
       // tuck the node into the end of the active list
       PRUint32 listLen;
-      res = mEditor->GetLengthOfDOMNode(curList, listLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(curNode, curList, listLen);
+      res = mHTMLEditor->MoveNode(curNode, curList, listLen);
       if (NS_FAILED(res)) return res;
     }
     
@@ -2362,13 +2400,13 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         nsAutoString quoteType; quoteType.AssignWithConversion("blockquote");
         res = SplitAsNeeded("eType, address_of(curParent), &offset);
         if (NS_FAILED(res)) return res;
-        res = mEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
+        res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
         if (NS_FAILED(res)) return res;
         
 /* !!!!!!!!!!!!!!!  TURNED OFF PER BUG 33213 !!!!!!!!!!!!!!!!!!!!
         // set style to not have unwanted vertical margins
         nsCOMPtr quoteElem = do_QueryInterface(curQuote);
-        res = mEditor->SetAttribute(quoteElem, NS_ConvertASCIItoUCS2("style"), NS_ConvertASCIItoUCS2("margin: 0 0 0 40px;"));
+        res = mHTMLEditor->SetAttribute(quoteElem, NS_ConvertASCIItoUCS2("style"), NS_ConvertASCIItoUCS2("margin: 0 0 0 40px;"));
         if (NS_FAILED(res)) return res;
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
 
@@ -2377,9 +2415,9 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         
       // tuck the node into the end of the active blockquote
       PRUint32 quoteLen;
-      res = mEditor->GetLengthOfDOMNode(curQuote, quoteLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curQuote, quoteLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(curNode, curQuote, quoteLen);
+      res = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2395,7 +2433,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
   *aCancel = PR_FALSE;
   *aHandled = PR_TRUE;
   
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   nsresult res = NS_OK;
   
   // convert the selection ranges into "promoted" selection ranges:
@@ -2439,7 +2477,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
     {
       if (nsHTMLEditUtils::IsList(curNode))  // just unwrap this sublist
       {
-        res = mEditor->RemoveContainer(curNode);
+        res = mHTMLEditor->RemoveContainer(curNode);
         if (NS_FAILED(res)) return res;
       }
       else  // we are moving a list item, but not whole list
@@ -2464,13 +2502,13 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         else
         {
           // delete any non- list items for now
-          res = mEditor->DeleteNode(child);
+          res = mHTMLEditor->DeleteNode(child);
           if (NS_FAILED(res)) return res;
         }
         curNode->GetLastChild(getter_AddRefs(child));
       }
       // delete the now-empty list
-      res = mEditor->DeleteNode(curNode);
+      res = mHTMLEditor->DeleteNode(curNode);
       if (NS_FAILED(res)) return res;
     }
     else if (transitionList[i])  // not list related - look for enclosing blockquotes and remove
@@ -2485,7 +2523,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
         {
           res = AddTerminatingBR(n);
           if (NS_FAILED(res)) return res;
-          mEditor->RemoveContainer(n);
+          mHTMLEditor->RemoveContainer(n);
           break;
         }
         n->GetParentNode(getter_AddRefs(tmp));
@@ -2515,24 +2553,24 @@ nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
   aList->GetFirstChild(getter_AddRefs(child));
   while (child)
   {
-    if (nsHTMLEditUtils::IsListItem(child) && !mEditor->NodeIsType(child, aItemType))
+    if (nsHTMLEditUtils::IsListItem(child) && !mHTMLEditor->NodeIsType(child, aItemType))
     {
-      res = mEditor->ReplaceContainer(child, address_of(temp), aItemType);
+      res = mHTMLEditor->ReplaceContainer(child, address_of(temp), aItemType);
       if (NS_FAILED(res)) return res;
       child = temp;
     }
-    else if (nsHTMLEditUtils::IsList(child) && !mEditor->NodeIsType(child, aListType))
+    else if (nsHTMLEditUtils::IsList(child) && !mHTMLEditor->NodeIsType(child, aListType))
     {
-      res = mEditor->ReplaceContainer(child, address_of(temp), aListType);
+      res = mHTMLEditor->ReplaceContainer(child, address_of(temp), aListType);
       if (NS_FAILED(res)) return res;
       child = temp;
     }
     child->GetNextSibling(getter_AddRefs(temp));
     child = temp;
   }
-  if (!mEditor->NodeIsType(aList, aListType))
+  if (!mHTMLEditor->NodeIsType(aList, aListType))
   {
-    res = mEditor->ReplaceContainer(aList, outList, aListType);
+    res = mHTMLEditor->ReplaceContainer(aList, outList, aListType);
   }
   return res;
 }
@@ -2547,32 +2585,32 @@ nsresult
 nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc) 
 {
   if (!aSelection || !aDoc) return NS_ERROR_NULL_POINTER;
-  if (!mEditor->mTypeInState) return NS_ERROR_NULL_POINTER;
+  if (!mHTMLEditor->mTypeInState) return NS_ERROR_NULL_POINTER;
   
   PRBool weDidSometing = PR_FALSE;
   nsCOMPtr node;
   PRInt32 offset;
-  nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
+  nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
   if (NS_FAILED(res)) return res;
   PropItem *item = nsnull;
   
   // process clearing any styles first
-  mEditor->mTypeInState->TakeClearProperty(&item);
+  mHTMLEditor->mTypeInState->TakeClearProperty(&item);
   while (item)
   {
     nsCOMPtr leftNode, rightNode, secondSplitParent, newSelParent;
-    res = mEditor->SplitStyleAbovePoint(address_of(node), &offset, item->tag, &item->attr, address_of(leftNode), address_of(rightNode));
+    res = mHTMLEditor->SplitStyleAbovePoint(address_of(node), &offset, item->tag, &item->attr, address_of(leftNode), address_of(rightNode));
     if (NS_FAILED(res)) return res;
     if (rightNode)
     {
-      res = mEditor->GetLeftmostChild(rightNode, getter_AddRefs(secondSplitParent));
+      res = mHTMLEditor->GetLeftmostChild(rightNode, getter_AddRefs(secondSplitParent));
       if (NS_FAILED(res)) return res;
       offset = 0;
-      res = mEditor->SplitStyleAbovePoint(address_of(secondSplitParent), &offset, item->tag, &(item->attr), address_of(leftNode), address_of(rightNode));
+      res = mHTMLEditor->SplitStyleAbovePoint(address_of(secondSplitParent), &offset, item->tag, &(item->attr), address_of(leftNode), address_of(rightNode));
       if (NS_FAILED(res)) return res;
       // should be impossible to not get a new leftnode here
       if (!leftNode) return NS_ERROR_FAILURE;
-      res = mEditor->GetLeftmostChild(leftNode, getter_AddRefs(newSelParent));
+      res = mHTMLEditor->GetLeftmostChild(leftNode, getter_AddRefs(newSelParent));
       if (NS_FAILED(res)) return res;
       // register a rangeStore item that points at the new heirarchy.
       // This is so we can know where to put the selection after we call
@@ -2584,36 +2622,36 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
       rangeItem->endNode = newSelParent;
       rangeItem->startOffset = 0;
       rangeItem->endOffset = 0;
-      mEditor->mRangeUpdater.RegisterRangeItem(rangeItem);
+      mHTMLEditor->mRangeUpdater.RegisterRangeItem(rangeItem);
       // remove the style on this new heirarchy
-      res = mEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
+      res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
       if (NS_FAILED(res)) return res;
       // reset our node offset values to the resulting new sel point
-      mEditor->mRangeUpdater.DropRangeItem(rangeItem);
+      mHTMLEditor->mRangeUpdater.DropRangeItem(rangeItem);
       node  = rangeItem->startNode;
       offset = rangeItem->startOffset;
       delete rangeItem;
     }
     // we own item now (TakeClearProperty hands ownership to us)
     delete item;
-    mEditor->mTypeInState->TakeClearProperty(&item);
+    mHTMLEditor->mTypeInState->TakeClearProperty(&item);
     weDidSometing = PR_TRUE;
   }
   
   // then process setting any styles
   PRInt32 relFontSize;
   
-  res = mEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
+  res = mHTMLEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
   if (NS_FAILED(res)) return res;
-  res = mEditor->mTypeInState->TakeSetProperty(&item);
+  res = mHTMLEditor->mTypeInState->TakeSetProperty(&item);
   if (NS_FAILED(res)) return res;
   
   if (item || relFontSize) // we have at least one style to add; make a
   {                        // new text node to insert style nodes above.
-    if (mEditor->IsTextNode(node))
+    if (mHTMLEditor->IsTextNode(node))
     {
       // if we are in a text node, split it
-      res = mEditor->SplitNodeDeep(node, node, offset, &offset);
+      res = mHTMLEditor->SplitNodeDeep(node, node, offset, &offset);
       if (NS_FAILED(res)) return res;
       nsCOMPtr tmp;
       node->GetParentNode(getter_AddRefs(tmp));
@@ -2625,7 +2663,7 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
     if (NS_FAILED(res)) return res;
     if (!nodeAsText) return NS_ERROR_NULL_POINTER;
     newNode = do_QueryInterface(nodeAsText);
-    res = mEditor->InsertNode(newNode, node, offset);
+    res = mHTMLEditor->InsertNode(newNode, node, offset);
     if (NS_FAILED(res)) return res;
     node = newNode;
     offset = 0;
@@ -2639,18 +2677,18 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
       else dir = -1;
       for (j=0; jRelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
+        res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
         if (NS_FAILED(res)) return res;
       }
     }
     
     while (item)
     {
-      res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
+      res = mHTMLEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
       if (NS_FAILED(res)) return res;
       // we own item now (TakeSetProperty hands ownership to us)
       delete item;
-      mEditor->mTypeInState->TakeSetProperty(&item);
+      mHTMLEditor->mTypeInState->TakeSetProperty(&item);
     }
   }
   if (weDidSometing)
@@ -2681,7 +2719,7 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
 //  looks like I forgot to finish this.  Wonder what I was going to do?
 
   if (!nodeToTest) return NS_ERROR_NULL_POINTER;
-  return mEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
+  return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
                      aMozBRDoesntCount, aListItemsNotEmpty);
 }
 
@@ -2702,7 +2740,7 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
   *aCancel = PR_FALSE;
   *aHandled = PR_FALSE;
   
-  nsAutoSelectionReset selectionResetter(aSelection, mEditor);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
@@ -2739,33 +2777,33 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
     PRInt32 offset;
     nsCOMPtr brNode, parent, theDiv, sib;
     nsAutoString divType; divType.AssignWithConversion("div");
-    res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     res = SplitAsNeeded(&divType, address_of(parent), &offset);
     if (NS_FAILED(res)) return res;
     // consume a trailing br, if any.  This is to keep an alignment from
     // creating extra lines, if possible.
-    res = mEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
+    res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
     if (NS_FAILED(res)) return res;
     if (brNode && nsHTMLEditUtils::IsBreak(brNode))
     {
       // making use of html structure... if next node after where
       // we are putting our div is not a block, then the br we 
       // found is in same block we are, so its safe to consume it.
-      res = mEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
+      res = mHTMLEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
       if (NS_FAILED(res)) return res;
       if (!nsEditor::IsBlockNode(sib))
       {
-        res = mEditor->DeleteNode(brNode);
+        res = mHTMLEditor->DeleteNode(brNode);
         if (NS_FAILED(res)) return res;
       }
     }
-    res = mEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
+    res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
     if (NS_FAILED(res)) return res;
     // set up the alignment on the div
     nsCOMPtr divElem = do_QueryInterface(theDiv);
     nsAutoString attr; attr.AssignWithConversion("align");
-    res = mEditor->SetAttribute(divElem, attr, *alignType);
+    res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
     if (NS_FAILED(res)) return res;
     *aHandled = PR_TRUE;
     // put in a moz-br so that it won't get deleted
@@ -2803,7 +2841,7 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
     {
       nsCOMPtr divElem = do_QueryInterface(curNode);
       nsAutoString attr; attr.AssignWithConversion("align");
-      res = mEditor->SetAttribute(divElem, attr, *alignType);
+      res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
       if (NS_FAILED(res)) return res;
       // clear out curDiv so that we don't put nodes after this one into it
       curDiv = 0;
@@ -2829,21 +2867,21 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
       nsAutoString divType; divType.AssignWithConversion("div");
       res = SplitAsNeeded(&divType, address_of(curParent), &offset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
+      res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
       if (NS_FAILED(res)) return res;
       // set up the alignment on the div
       nsCOMPtr divElem = do_QueryInterface(curDiv);
       nsAutoString attr; attr.AssignWithConversion("align");
-      res = mEditor->SetAttribute(divElem, attr, *alignType);
+      res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
       if (NS_FAILED(res)) return res;
       // curDiv is now the correct thing to put curNode in
     }
         
     // tuck the node into the end of the active div
     PRUint32 listLen;
-    res = mEditor->GetLengthOfDOMNode(curDiv, listLen);
+    res = mHTMLEditor->GetLengthOfDOMNode(curDiv, listLen);
     if (NS_FAILED(res)) return res;
-    res = mEditor->MoveNode(curNode, curDiv, listLen);
+    res = mHTMLEditor->MoveNode(curNode, curDiv, listLen);
     if (NS_FAILED(res)) return res;
   }
 
@@ -2899,9 +2937,9 @@ nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType
   nsresult res;
   nsCOMPtr  firstChild, lastChild, divNode;
   
-  res = mEditor->GetFirstEditableChild(aNode, address_of(firstChild));
+  res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(firstChild));
   if (NS_FAILED(res)) return res;
-  res = mEditor->GetLastEditableChild(aNode, address_of(lastChild));
+  res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
   if (NS_FAILED(res)) return res;
   if (!firstChild)
   {
@@ -2913,26 +2951,26 @@ nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType
     // act on this div.
     nsCOMPtr divElem = do_QueryInterface(firstChild);
     nsAutoString attr; attr.AssignWithConversion("align");
-    res = mEditor->SetAttribute(divElem, attr, *alignType);
+    res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
     if (NS_FAILED(res)) return res;
   }
   else
   {
     // else we need to put in a div, set the alignment, and toss in all the children
     nsAutoString divType; divType.AssignWithConversion("div");
-    res = mEditor->CreateNode(divType, aNode, 0, getter_AddRefs(divNode));
+    res = mHTMLEditor->CreateNode(divType, aNode, 0, getter_AddRefs(divNode));
     if (NS_FAILED(res)) return res;
     // set up the alignment on the div
     nsCOMPtr divElem = do_QueryInterface(divNode);
     nsAutoString attr; attr.AssignWithConversion("align");
-    res = mEditor->SetAttribute(divElem, attr, *alignType);
+    res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
     if (NS_FAILED(res)) return res;
     // tuck the children into the end of the active div
     while (lastChild && (lastChild != divNode))
     {
-      res = mEditor->MoveNode(lastChild, divNode, 0);
+      res = mHTMLEditor->MoveNode(lastChild, divNode, 0);
       if (NS_FAILED(res)) return res;
-      res = mEditor->GetLastEditableChild(aNode, address_of(lastChild));
+      res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
       if (NS_FAILED(res)) return res;
     }
   }
@@ -2955,7 +2993,7 @@ nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOf
   nsCOMPtr node;
   nsCOMPtr isupports;
   
-  nsresult res = mEditor->GetFirstEditableChild(aNode, address_of(node));
+  nsresult res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(node));
   while (NS_SUCCEEDED(res) && node)
   {
     if (  ( aList && (nsHTMLEditUtils::IsList(node)     || 
@@ -3010,7 +3048,7 @@ nsHTMLEditRules::IsFirstNode(nsIDOMNode *aNode)
   while (j < offset)
   {
     childList->Item(j, getter_AddRefs(child));
-    if (mEditor->IsEditable(child)) 
+    if (mHTMLEditor->IsEditable(child)) 
       return PR_FALSE;
     j++;
   }
@@ -3052,7 +3090,7 @@ nsHTMLEditRules::IsLastNode(nsIDOMNode *aNode)
   while (j < (PRInt32)numChildren)
   {
     childList->Item(j, getter_AddRefs(child));
-    if (mEditor->IsEditable(child)) 
+    if (mHTMLEditor->IsEditable(child)) 
       return PR_FALSE;
     j++;
   }
@@ -3070,10 +3108,10 @@ nsHTMLEditRules::AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *
   if (nodeAsText && aOffset) return PR_FALSE;  // there are chars in front of us
   
   nsCOMPtr priorNode;
-  nsresult  res = mEditor->GetPriorHTMLNode(aNode, aOffset, address_of(priorNode));
+  nsresult  res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(priorNode));
   if (NS_FAILED(res)) return PR_TRUE;
   if (!priorNode) return PR_TRUE;
-  nsCOMPtr blockParent = mEditor->GetBlockNodeParent(priorNode);
+  nsCOMPtr blockParent = mHTMLEditor->GetBlockNodeParent(priorNode);
   if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
   return PR_TRUE;
 }
@@ -3093,10 +3131,10 @@ nsHTMLEditRules::AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aB
     if ((PRInt32)strLength > aOffset) return PR_FALSE;  // there are chars in after us
   }
   nsCOMPtr nextNode;
-  nsresult  res = mEditor->GetNextHTMLNode(aNode, aOffset, address_of(nextNode));
+  nsresult  res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nextNode));
   if (NS_FAILED(res)) return PR_TRUE;
   if (!nextNode) return PR_TRUE;
-  nsCOMPtr blockParent = mEditor->GetBlockNodeParent(nextNode);
+  nsCOMPtr blockParent = mHTMLEditor->GetBlockNodeParent(nextNode);
   if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
   return PR_TRUE;
 }
@@ -3113,11 +3151,11 @@ nsHTMLEditRules::CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtrCreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
+  nsresult res = mHTMLEditor->CreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
   if (NS_FAILED(res)) return res;
   // give it special moz attr
   nsCOMPtr mozDivElem = do_QueryInterface(*outDiv);
-  res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
+  res = mHTMLEditor->SetAttribute(mozDivElem, "type", "_moz");
   if (NS_FAILED(res)) return res;
   res = AddTrailerBR(*outDiv);
   return res;
@@ -3153,7 +3191,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       do
       {
-        res = mEditor->IsPrevCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
+        res = mHTMLEditor->IsPrevCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
         if (NS_FAILED(res)) return res;
         if (isSpace || isNBSP) node = temp;
         else break;
@@ -3166,7 +3204,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       do
       {
-        res = mEditor->IsNextCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
+        res = mHTMLEditor->IsNextCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
         if (NS_FAILED(res)) return res;
         if (isSpace || isNBSP) node = temp;
         else break;
@@ -3203,7 +3241,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       nsCOMPtr block = nsEditor::GetBlockNodeParent(node);
       nsCOMPtr prevNode, prevNodeBlock;
-      res = mEditor->GetPriorHTMLNode(node, address_of(prevNode));
+      res = mHTMLEditor->GetPriorHTMLNode(node, address_of(prevNode));
       
       while (prevNode && NS_SUCCEEDED(res))
       {
@@ -3215,7 +3253,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
         if (nsEditor::IsBlockNode(prevNode))
           break;
         node = prevNode;
-        res = mEditor->GetPriorHTMLNode(node, address_of(prevNode));
+        res = mHTMLEditor->GetPriorHTMLNode(node, address_of(prevNode));
       }
     }
     
@@ -3269,7 +3307,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
     {
       nsCOMPtr block = nsEditor::GetBlockNodeParent(node);
       nsCOMPtr nextNode, nextNodeBlock;
-      res = mEditor->GetNextHTMLNode(node, address_of(nextNode));
+      res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode));
       
       while (nextNode && NS_SUCCEEDED(res))
       {
@@ -3284,7 +3322,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
         if (nsEditor::IsBlockNode(nextNode))
           break;
         node = nextNode;
-        res = mEditor->GetNextHTMLNode(node, address_of(nextNode));
+        res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode));
       }
     }
     
@@ -3436,7 +3474,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
       nsRangeStore *item = new nsRangeStore();
       if (!item) return NS_ERROR_NULL_POINTER;
       item->StoreRange(opRange);
-      mEditor->mRangeUpdater.RegisterRangeItem(item);
+      mHTMLEditor->mRangeUpdater.RegisterRangeItem(item);
       rangeItemArray.AppendElement((void*)item);
       inArrayOfRanges->RemoveElementAt(0);
     }    
@@ -3453,7 +3491,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
       nsRangeStore *item = (nsRangeStore*)rangeItemArray.ElementAt(0);
       if (!item) return NS_ERROR_NULL_POINTER;
       rangeItemArray.RemoveElementAt(0);
-      mEditor->mRangeUpdater.DropRangeItem(item);
+      mHTMLEditor->mRangeUpdater.DropRangeItem(item);
       res = item->GetRange(address_of(opRange));
       if (NS_FAILED(res)) return res;
       delete item;
@@ -3530,8 +3568,8 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
     {
       isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i));
       nsCOMPtr node( do_QueryInterface(isupports) );
-      if (!aDontTouchContent && mEditor->IsInlineNode(node) 
-           && mEditor->IsContainer(node) && !mEditor->IsTextNode(node))
+      if (!aDontTouchContent && mHTMLEditor->IsInlineNode(node) 
+           && mHTMLEditor->IsContainer(node) && !mHTMLEditor->IsTextNode(node))
       {
         nsCOMPtr arrayOfInlines;
         res = BustUpInlinesAtBRs(node, address_of(arrayOfInlines));
@@ -3605,7 +3643,7 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes,
   nsresult res = NS_OK;
   
   nsCOMPtrselection;
-  res = mEditor->GetSelection(getter_AddRefs(selection));
+  res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
   nsCOMPtr selPriv(do_QueryInterface(selection));
   if (!selPriv)
@@ -3675,7 +3713,7 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes,
     nsCOMPtr testNode( do_QueryInterface(isupports ) );
 
     // Remove all non-editable nodes.  Leave them be.
-    if (!mEditor->IsEditable(testNode))
+    if (!mHTMLEditor->IsEditable(testNode))
     {
       (*outArrayOfNodes)->RemoveElementAt(i);
     }
@@ -3706,8 +3744,8 @@ nsHTMLEditRules::GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBo
   res = aNode->GetFirstChild(getter_AddRefs(child));
   while (child && NS_SUCCEEDED(res))
   {
-    if (mEditor->NodeIsType(child,nsIEditProperty::dt)) aDT = PR_TRUE;
-    else if (mEditor->NodeIsType(child,nsIEditProperty::dd)) aDD = PR_TRUE;
+    if (mHTMLEditor->NodeIsType(child,nsIEditProperty::dt)) aDT = PR_TRUE;
+    else if (mHTMLEditor->NodeIsType(child,nsIEditProperty::dd)) aDD = PR_TRUE;
     res = child->GetNextSibling(getter_AddRefs(temp));
     child = temp;
   }
@@ -3724,7 +3762,7 @@ nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfN
   if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER;
   
   nsCOMPtrselection;
-  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(res)) return res;
 
   nsCOMPtr arrayOfRanges;
@@ -3745,7 +3783,7 @@ nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfN
     nsCOMPtr testNode( do_QueryInterface(isupports ) );
 
     // Remove all non-editable nodes.  Leave them be.
-    if (!mEditor->IsEditable(testNode))
+    if (!mHTMLEditor->IsEditable(testNode))
     {
       (*outArrayOfNodes)->RemoveElementAt(i);
     }
@@ -3782,7 +3820,7 @@ nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
     nsCOMPtr resultEndNode;
     PRInt32 resultEndOffset;
     item.endNode->GetParentNode(getter_AddRefs(resultEndNode));
-    res = mEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
+    res = mHTMLEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
                           &resultEndOffset, PR_TRUE);
     if (NS_FAILED(res)) return res;
     // reset range
@@ -3796,7 +3834,7 @@ nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
     nsCOMPtr resultStartNode;
     PRInt32 resultStartOffset;
     item.startNode->GetParentNode(getter_AddRefs(resultStartNode));
-    res = mEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
+    res = mHTMLEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
                           &resultStartOffset, PR_TRUE);
     if (NS_FAILED(res)) return res;
     // reset range
@@ -3860,7 +3898,7 @@ nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode,
       if (!splitDeepNode) return NS_ERROR_NULL_POINTER;
       res = nsEditor::GetNodeLocation(breakNode, address_of(splitParentNode), &splitOffset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
+      res = mHTMLEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
                           &resultOffset, PR_FALSE, address_of(leftNode), address_of(rightNode));
       if (NS_FAILED(res)) return res;
       // put left node in node list
@@ -3874,7 +3912,7 @@ nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode,
         if (NS_FAILED(res)) return res;
       }
       // move break outside of container and also put in node list
-      res = mEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
+      res = mHTMLEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
       if (NS_FAILED(res)) return res;
       isupports = do_QueryInterface(breakNode);
       (*outArrayOfNodes)->AppendElement(isupports);
@@ -3901,7 +3939,7 @@ nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode)
   if (nsEditor::IsBlockNode(aNode)) return nsnull;
   nsCOMPtr inlineNode, node=aNode;
 
-  while (node && mEditor->IsInlineNode(node))
+  while (node && mHTMLEditor->IsInlineNode(node))
   {
     inlineNode = node;
     inlineNode->GetParentNode(getter_AddRefs(node));
@@ -3964,12 +4002,12 @@ nsHTMLEditRules::InsertTab(nsISelection *aSelection,
   PRInt32 offset;
   PRBool isPRE;
   
-  nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(parentNode), &offset);
+  nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parentNode), &offset);
   if (NS_FAILED(res)) return res;
   
   if (!parentNode) return NS_ERROR_FAILURE;
     
-  res = mEditor->IsPreformatted(parentNode, &isPRE);
+  res = mHTMLEditor->IsPreformatted(parentNode, &isPRE);
   if (NS_FAILED(res)) return res;
     
   if (isPRE)
@@ -4007,16 +4045,16 @@ nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
 
   // split the header
   PRInt32 newOffset;
-  res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
+  res = mHTMLEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
   if (NS_FAILED(res)) return res;
 
   // if the leftand heading is empty, put a mozbr in it
   nsCOMPtr prevItem;
-  mEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
+  mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
   if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
   {
     PRBool bIsEmptyNode;
-    res = mEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
+    res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
     if (NS_FAILED(res)) return res;
     if (bIsEmptyNode)
     {
@@ -4032,12 +4070,12 @@ nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
   if (NS_FAILED(res)) return res;
   if (isEmpty)
   {
-    res = mEditor->DeleteNode(aHeader);
+    res = mHTMLEditor->DeleteNode(aHeader);
     if (NS_FAILED(res)) return res;
     // layout tells the caret to blink in a weird place
     // if we dont place a break after the header.
     nsCOMPtr sibling;
-    res = mEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
+    res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
     if (NS_FAILED(res)) return res;
     if (!sibling || !nsHTMLEditUtils::IsBreak(sibling))
     {
@@ -4078,7 +4116,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
   nsresult res = NS_OK;
 
   // easy case, in a text node:
-  if (mEditor->IsTextNode(aNode))
+  if (mHTMLEditor->IsTextNode(aNode))
   {
     nsCOMPtr textNode = do_QueryInterface(aNode);
     PRUint32 strLength;
@@ -4089,7 +4127,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     if (!aOffset)
     {
       // is there a BR prior to it?
-      mEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
+      mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
       if (!sibling) 
       {
         // no previous sib, so
@@ -4102,15 +4140,15 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
         PRInt32 newOffset;
         *aCancel = PR_TRUE;
         // split the paragraph
-        res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
+        res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
         if (NS_FAILED(res)) return res;
         // get rid of the break
-        res = mEditor->DeleteNode(sibling);  
+        res = mHTMLEditor->DeleteNode(sibling);  
         if (NS_FAILED(res)) return res;
         // check both halves of para to see if we need mozBR
         res = InsertMozBRIfNeeded(aPara);
         if (NS_FAILED(res)) return res;
-        res = mEditor->GetPriorHTMLSibling(aPara, address_of(sibling));
+        res = mHTMLEditor->GetPriorHTMLSibling(aPara, address_of(sibling));
         if (NS_FAILED(res)) return res;
         if (sibling && nsHTMLEditUtils::IsParagraph(sibling))
         {
@@ -4127,7 +4165,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     if (aOffset == (PRInt32)strLength)
     {
       // is there a BR after to it?
-      res = mEditor->GetNextHTMLSibling(aNode, address_of(sibling));
+      res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
       if (!sibling) 
       {
         // no next sib, so
@@ -4140,10 +4178,10 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
         PRInt32 newOffset;
         *aCancel = PR_TRUE;
         // split the paragraph
-        res = mEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
+        res = mHTMLEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
         if (NS_FAILED(res)) return res;
         // get rid of the break
-        res = mEditor->DeleteNode(sibling);  
+        res = mHTMLEditor->DeleteNode(sibling);  
         if (NS_FAILED(res)) return res;
         // position selection inside right hand para
         res = aSelection->Collapse(aPara,0);
@@ -4160,13 +4198,13 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     // not in a text node.  
     // is there a BR prior to it?
     nsCOMPtr nearNode;
-    res = mEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
+    res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
     if (NS_FAILED(res)) return res;
     if (!nearNode || !nsHTMLEditUtils::IsBreak(nearNode)
         || nsHTMLEditUtils::HasMozAttr(nearNode)) 
     {
       // is there a BR after to it?
-      res = mEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
+      res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
       if (NS_FAILED(res)) return res;
       if (!nearNode || !nsHTMLEditUtils::IsBreak(nearNode)
           || nsHTMLEditUtils::HasMozAttr(nearNode)) 
@@ -4179,10 +4217,10 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
     PRInt32 newOffset;
     *aCancel = PR_TRUE;
     // split the paragraph
-    res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
+    res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
     if (NS_FAILED(res)) return res;
     // get rid of the break
-    res = mEditor->DeleteNode(nearNode);  
+    res = mHTMLEditor->DeleteNode(nearNode);  
     if (NS_FAILED(res)) return res;
     // selection to beginning of right hand para
     aSelection->Collapse(aPara,0);
@@ -4228,27 +4266,27 @@ nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
     
     // are we the last list item in the list?
     PRBool bIsLast;
-    res = mEditor->IsLastEditableChild(aListItem, &bIsLast);
+    res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
     if (NS_FAILED(res)) return res;
     if (!bIsLast)
     {
       // we need to split the list!
       nsCOMPtr tempNode;
-      res = mEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
+      res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
       if (NS_FAILED(res)) return res;
     }
     // are we in a sublist?
     if (nsHTMLEditUtils::IsList(listparent))  //in a sublist
     {
       // if so, move this list item out of this list and into the grandparent list
-      res = mEditor->MoveNode(aListItem,listparent,offset+1);
+      res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
       if (NS_FAILED(res)) return res;
       res = aSelection->Collapse(aListItem,0);
     }
     else
     {
       // otherwise kill this listitem
-      res = mEditor->DeleteNode(aListItem);
+      res = mHTMLEditor->DeleteNode(aListItem);
       if (NS_FAILED(res)) return res;
       
       // time to insert a break
@@ -4265,17 +4303,17 @@ nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
   
   // else we want a new list item at the same list level
   PRInt32 newOffset;
-  res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
+  res = mHTMLEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
   if (NS_FAILED(res)) return res;
   // hack: until I can change the damaged doc range code back to being
   // extra inclusive, I have to manually detect certain list items that
   // may be left empty.
   nsCOMPtr prevItem;
-  mEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
+  mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
   if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
   {
     PRBool bIsEmptyNode;
-    res = mEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
+    res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
     if (NS_FAILED(res)) return res;
     if (bIsEmptyNode)
     {
@@ -4355,14 +4393,14 @@ nsHTMLEditRules::MakeBlockquote(nsISupportsArray *arrayOfNodes)
       nsAutoString quoteType; quoteType.AssignWithConversion("blockquote");
       res = SplitAsNeeded("eType, address_of(curParent), &offset);
       if (NS_FAILED(res)) return res;
-      res = mEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
+      res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
       if (NS_FAILED(res)) return res;
     }
       
     PRUint32 blockLen;
-    res = mEditor->GetLengthOfDOMNode(curBlock, blockLen);
+    res = mHTMLEditor->GetLengthOfDOMNode(curBlock, blockLen);
     if (NS_FAILED(res)) return res;
-    res = mEditor->MoveNode(curNode, curBlock, blockLen);
+    res = mHTMLEditor->MoveNode(curNode, curBlock, blockLen);
     if (NS_FAILED(res)) return res;
   }
   return res;
@@ -4434,11 +4472,11 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
         // make sure we have a normal br at end of block
         res = AddTerminatingBR(curNode);
         if (NS_FAILED(res)) return res;
-        res = mEditor->RemoveContainer(curNode); 
+        res = mHTMLEditor->RemoveContainer(curNode); 
       }
       else
       {
-        res = mEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
+        res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
       }
       if (NS_FAILED(res)) return res;
     }
@@ -4467,7 +4505,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       curBlock = 0;  // forget any previous block used for previous inline nodes
       if (!bNoParent)
       {
-        res = mEditor->DeleteNode(curNode);
+        res = mHTMLEditor->DeleteNode(curNode);
         if (NS_FAILED(res)) return res;
       }
     }
@@ -4483,7 +4521,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
     else if (nsEditor::IsInlineNode(curNode) && !bNoParent)
     {
       // if curNode is a non editable, drop it if we are going to 
-      if ((aBlockTag->EqualsWithConversion("pre")) && (!mEditor->IsEditable(curNode)))
+      if ((aBlockTag->EqualsWithConversion("pre")) && (!mHTMLEditor->IsEditable(curNode)))
         continue; // do nothing to this block
       
       // if no curBlock, make one
@@ -4491,7 +4529,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       {
         res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
         if (NS_FAILED(res)) return res;
-        res = mEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
+        res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
         if (NS_FAILED(res)) return res;
       }
       
@@ -4501,9 +4539,9 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       // this is a continuation of some inline nodes that belong together in
       // the same block item.  use curBlock
       PRUint32 blockLen;
-      res = mEditor->GetLengthOfDOMNode(curBlock, blockLen);
+      res = mHTMLEditor->GetLengthOfDOMNode(curBlock, blockLen);
       if (NS_FAILED(res)) return res;
-      res = mEditor->MoveNode(curNode, curBlock, blockLen);
+      res = mHTMLEditor->MoveNode(curNode, curBlock, blockLen);
       if (NS_FAILED(res)) return res;
     }
   }
@@ -4531,7 +4569,7 @@ nsHTMLEditRules::SplitAsNeeded(const nsString *aTag,
     // sniffing up the parent tree until we find 
     // a legal place for the block
     if (!parent) break;
-    if (mEditor->CanContainTag(parent, *aTag))
+    if (mHTMLEditor->CanContainTag(parent, *aTag))
     {
       tagParent = parent;
       break;
@@ -4548,7 +4586,7 @@ nsHTMLEditRules::SplitAsNeeded(const nsString *aTag,
   if (splitNode)
   {
     // we found a place for block, but above inOutParent.  We need to split nodes.
-    res = mEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
+    res = mHTMLEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
     if (NS_FAILED(res)) return res;
     *inOutParent = tagParent;
   }
@@ -4564,14 +4602,14 @@ nsHTMLEditRules::AddTerminatingBR(nsIDOMNode *aBlock)
 {
   if (!aBlock) return NS_ERROR_NULL_POINTER;
   nsCOMPtr last;
-  nsresult res = mEditor->GetLastEditableLeaf(aBlock, address_of(last));
+  nsresult res = mHTMLEditor->GetLastEditableLeaf(aBlock, address_of(last));
   if (last && nsHTMLEditUtils::IsBreak(last))
   {
     if (nsHTMLEditUtils::IsMozBR(last))
     {
       // need to convert a br
       nsCOMPtr elem = do_QueryInterface(last);
-      res = mEditor->RemoveAttribute(elem, NS_ConvertASCIItoUCS2("type")); 
+      res = mHTMLEditor->RemoveAttribute(elem, NS_ConvertASCIItoUCS2("type")); 
       if (NS_FAILED(res)) return res;
     }
     else // we have what we want, we're done
@@ -4583,9 +4621,9 @@ nsHTMLEditRules::AddTerminatingBR(nsIDOMNode *aBlock)
   {
     PRUint32 len;
     nsCOMPtr brNode;
-    res = mEditor->GetLengthOfDOMNode(aBlock, len);
+    res = mHTMLEditor->GetLengthOfDOMNode(aBlock, len);
     if (NS_FAILED(res)) return res;
-    res = mEditor->CreateBR(aBlock, len, address_of(brNode));
+    res = mHTMLEditor->CreateBR(aBlock, len, address_of(brNode));
     if (NS_FAILED(res)) return res;
   }
   return res;
@@ -4622,24 +4660,24 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
   // to after the 'left' one
   if (parent != rightParent)
   {
-    res = mEditor->MoveNode(aNodeRight, parent, parOffset);
+    res = mHTMLEditor->MoveNode(aNodeRight, parent, parOffset);
     if (NS_FAILED(res)) return res;
   }
   
   // defaults for outParams
   *aOutMergeParent = aNodeRight;
-  res = mEditor->GetLengthOfDOMNode(aNodeLeft, *((PRUint32*)aOutMergeOffset));
+  res = mHTMLEditor->GetLengthOfDOMNode(aNodeLeft, *((PRUint32*)aOutMergeOffset));
   if (NS_FAILED(res)) return res;
 
   // seperate join rules for differing blocks
   if (nsHTMLEditUtils::IsParagraph(aNodeLeft))
   {
     // for para's, merge deep & add a 
after merging - res = mEditor->JoinNodeDeep(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset); + res = mHTMLEditor->JoinNodeDeep(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset); if (NS_FAILED(res)) return res; // now we need to insert a br. nsCOMPtr brNode; - res = mEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, address_of(brNode)); + res = mHTMLEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, address_of(brNode)); if (NS_FAILED(res)) return res; res = nsEditor::GetNodeLocation(brNode, aOutMergeParent, aOutMergeOffset); if (NS_FAILED(res)) return res; @@ -4647,10 +4685,10 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, return res; } else if (nsHTMLEditUtils::IsList(aNodeLeft) - || mEditor->IsTextNode(aNodeLeft)) + || mHTMLEditor->IsTextNode(aNodeLeft)) { // for list's, merge shallow (wouldn't want to combine list items) - res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent); + res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent); if (NS_FAILED(res)) return res; return res; } @@ -4658,16 +4696,16 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, { // remember the last left child, and firt right child nsCOMPtr lastLeft, firstRight; - res = mEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft)); + res = mHTMLEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft)); if (NS_FAILED(res)) return res; - res = mEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight)); + res = mHTMLEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight)); if (NS_FAILED(res)) return res; // for list items, divs, etc, merge smart - res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent); + res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent); if (NS_FAILED(res)) return res; - if (lastLeft && firstRight && mEditor->NodesSameType(lastLeft, firstRight)) + if (lastLeft && firstRight && mHTMLEditor->NodesSameType(lastLeft, firstRight)) { return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset); } @@ -4677,7 +4715,9 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, nsresult -nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode) +nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, + nsCOMPtr *aOutCiteNode, + PRBool aPlainText) { // check parms if (!aNode || !aOutCiteNode) @@ -4689,7 +4729,9 @@ nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr while (node) { - if (nsHTMLEditUtils::IsMailCite(node)) *aOutCiteNode = node; + if ( (aPlainText && nsHTMLEditUtils::IsPre(node)) || + (!aPlainText && nsHTMLEditUtils::IsMailCite(node)) ) + *aOutCiteNode = node; if (nsHTMLEditUtils::IsBody(node)) break; res = node->GetParentNode(getter_AddRefs(parentNode)); @@ -4709,7 +4751,7 @@ nsHTMLEditRules::AdjustSpecialBreaks(PRBool aSafeToAskFrames) PRUint32 nodeCount,j; // gather list of empty nodes - nsEmptyFunctor functor(mEditor); + nsEmptyFunctor functor(mHTMLEditor); nsDOMIterator iter; nsresult res = iter.Init(mDocChangeRange); if (NS_FAILED(res)) return res; @@ -4740,7 +4782,7 @@ nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection) PRUint32 nodeCount,j; nsresult res; - nsAutoSelectionReset selectionResetter(aSelection, mEditor); + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); // special case for mDocChangeRange entirely in one text node. // This is an efficiency hack for normal typing in the editor. @@ -4766,7 +4808,7 @@ nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection) } // gather up a list of text nodes - nsEditableTextFunctor functor(mEditor); + nsEditableTextFunctor functor(mHTMLEditor); nsDOMIterator iter; res = iter.Init(mDocChangeRange); if (NS_FAILED(res)) return res; @@ -4807,12 +4849,12 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection // get the (collapsed) selection location nsCOMPtr selNode, temp; PRInt32 selOffset; - res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; temp = selNode; // are we in an editable node? - while (!mEditor->IsEditable(selNode)) + while (!mHTMLEditor->IsEditable(selNode)) { // scan up the tree until we find an editable place to be res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset); @@ -4833,22 +4875,22 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection // 4) that br is the last editable node in it's block nsCOMPtr nearNode; - res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; if (!nearNode) return res; // is nearNode also a descendant of same block? nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); - nearBlock = mEditor->GetBlockNodeParent(nearNode); + if (mHTMLEditor->IsBlockNode(selNode)) block = selNode; + else block = mHTMLEditor->GetBlockNodeParent(selNode); + nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode); if (block == nearBlock) { if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) && !nsHTMLEditUtils::IsMozBR(nearNode)) { PRBool bIsLast; - res = mEditor->IsLastEditableChild(nearNode, &bIsLast); + res = mHTMLEditor->IsLastEditableChild(nearNode, &bIsLast); if (NS_FAILED(res)) return res; if (bIsLast) { @@ -4872,11 +4914,11 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection // text
  1. list item
) // in this case we also need moz-br. nsCOMPtr nextNode; - res = mEditor->GetNextHTMLNode(nearNode, address_of(nextNode)); + res = mHTMLEditor->GetNextHTMLNode(nearNode, address_of(nextNode)); if (NS_FAILED(res)) return res; - res = mEditor->GetNextHTMLSibling(nearNode, address_of(nextNode)); + res = mHTMLEditor->GetNextHTMLSibling(nearNode, address_of(nextNode)); if (NS_FAILED(res)) return res; - if (nextNode && mEditor->IsBlockNode(nextNode)) + if (nextNode && mHTMLEditor->IsBlockNode(nextNode)) { // need to insert special moz BR. Why? Because if we don't // the user will see no new line for the break. @@ -4895,12 +4937,12 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection } // we aren't in a textnode: are we adjacent to a break or an image? - res = mEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; if (nearNode && (nsHTMLEditUtils::IsBreak(nearNode) || nsHTMLEditUtils::IsImage(nearNode))) return NS_OK; // this is a good place for the caret to be - res = mEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nearNode)); + res = mHTMLEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; if (nearNode && (nsHTMLEditUtils::IsBreak(nearNode) || nsHTMLEditUtils::IsImage(nearNode))) @@ -4946,22 +4988,22 @@ nsHTMLEditRules::FindNearSelectableNode(nsIDOMNode *aSelNode, nsCOMPtr nearNode, curNode; if (aDirection == nsIEditor::ePrevious) - res = mEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); else - res = mEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); + res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); if (NS_FAILED(res)) return res; // scan in the right direction until we find an eligible text node, // but dont cross any breaks, images, or table elements. - while (nearNode && !(mEditor->IsTextNode(nearNode) + while (nearNode && !(mHTMLEditor->IsTextNode(nearNode) || nsHTMLEditUtils::IsBreak(nearNode) || nsHTMLEditUtils::IsImage(nearNode))) { curNode = nearNode; if (aDirection == nsIEditor::ePrevious) - res = mEditor->GetPriorHTMLNode(curNode, address_of(nearNode)); + res = mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode)); else - res = mEditor->GetNextHTMLNode(curNode, address_of(nearNode)); + res = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode)); if (NS_FAILED(res)) return res; } @@ -5042,7 +5084,7 @@ nsHTMLEditRules::RemoveEmptyNodes() if (!node) return NS_ERROR_FAILURE; PRBool bIsEmptyNode; - res = mEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE); + res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE); if (NS_FAILED(res)) return res; if (bIsEmptyNode && !nsHTMLEditUtils::IsBody(node)) { @@ -5085,7 +5127,7 @@ nsHTMLEditRules::RemoveEmptyNodes() isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); nsCOMPtr delNode( do_QueryInterface(isupports ) ); arrayOfNodes->RemoveElementAt(0); - res = mEditor->DeleteNode(delNode); + res = mHTMLEditor->DeleteNode(delNode); if (NS_FAILED(res)) return res; } @@ -5102,7 +5144,7 @@ nsHTMLEditRules::SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult) *aResult = PR_FALSE; nsCOMPtrselection; - nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; nsCOMPtrselPriv(do_QueryInterface(selection)); @@ -5170,7 +5212,7 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS PRBool isPRE; nsCOMPtr node = do_QueryInterface(aTextNode); - res = mEditor->IsPreformatted(node,&isPRE); + res = mHTMLEditor->IsPreformatted(node,&isPRE); if (NS_FAILED(res)) return res; if (isPRE) @@ -5222,19 +5264,19 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS EditTxn *txn; // note 1: we are not telling edit listeners about these because they don't care // note 2: we are not wrapping these in a placeholder because we know they already are - res = mEditor->CreateTxnForDeleteText(aTextNode, aStart+runStart, runEnd-runStart, (DeleteTextTxn**)&txn); + res = mHTMLEditor->CreateTxnForDeleteText(aTextNode, aStart+runStart, runEnd-runStart, (DeleteTextTxn**)&txn); if (NS_FAILED(res)) return res; if (!txn) return NS_ERROR_OUT_OF_MEMORY; - res = mEditor->Do(txn); + res = mHTMLEditor->Do(txn); if (NS_FAILED(res)) return res; // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); // insert the new run - res = mEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); + res = mHTMLEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); if (NS_FAILED(res)) return res; if (!txn) return NS_ERROR_OUT_OF_MEMORY; - res = mEditor->Do(txn); + res = mHTMLEditor->Do(txn); // The transaction system (if any) has taken ownwership of txns. NS_IF_RELEASE(txn); } @@ -5322,24 +5364,24 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) if (NS_FAILED(res)) return res; PRBool bIsFirstListItem; - res = mEditor->IsFirstEditableChild(curNode, &bIsFirstListItem); + res = mHTMLEditor->IsFirstEditableChild(curNode, &bIsFirstListItem); if (NS_FAILED(res)) return res; PRBool bIsLastListItem; - res = mEditor->IsLastEditableChild(curNode, &bIsLastListItem); + res = mHTMLEditor->IsLastEditableChild(curNode, &bIsLastListItem); if (NS_FAILED(res)) return res; if (!bIsFirstListItem && !bIsLastListItem) { // split the list nsCOMPtr newBlock; - res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock)); + res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock)); if (NS_FAILED(res)) return res; } if (!bIsFirstListItem) parOffset++; - res = mEditor->MoveNode(curNode, curParPar, parOffset); + res = mHTMLEditor->MoveNode(curNode, curParPar, parOffset); if (NS_FAILED(res)) return res; // unwrap list item contents if they are no longer in a list @@ -5348,7 +5390,7 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) { res = AddTerminatingBR(curNode); if (NS_FAILED(res)) return res; - res = mEditor->RemoveContainer(curNode); + res = mHTMLEditor->RemoveContainer(curNode); if (NS_FAILED(res)) return res; *aOutOfList = PR_TRUE; } @@ -5364,20 +5406,20 @@ nsHTMLEditRules::ConfirmSelectionInBody() nsCOMPtr bodyNode; // get the body - res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); + res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement)); if (NS_FAILED(res)) return res; if (!bodyElement) return NS_ERROR_UNEXPECTED; bodyNode = do_QueryInterface(bodyElement); // get the selection nsCOMPtrselection; - res = mEditor->GetSelection(getter_AddRefs(selection)); + res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; // get the selection start location nsCOMPtr selNode, temp, parent; PRInt32 selOffset; - res = mEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; temp = selNode; @@ -5397,7 +5439,7 @@ nsHTMLEditRules::ConfirmSelectionInBody() } // get the selection end location - res = mEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; temp = selNode; @@ -5472,11 +5514,11 @@ nsresult nsHTMLEditRules::InsertMozBRIfNeeded(nsIDOMNode *aNode) { if (!aNode) return NS_ERROR_NULL_POINTER; - if (!mEditor->IsBlockNode(aNode)) return NS_OK; + if (!mHTMLEditor->IsBlockNode(aNode)) return NS_OK; PRBool isEmpty; nsCOMPtr brNode; - nsresult res = mEditor->IsEmptyNode(aNode, &isEmpty); + nsresult res = mHTMLEditor->IsEmptyNode(aNode, &isEmpty); if (NS_FAILED(res)) return res; if (isEmpty) { @@ -5658,11 +5700,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection) nsCOMPtr selNode; PRInt32 selOffset; - nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; res = mUtilRange->SetStart(selNode, selOffset); if (NS_FAILED(res)) return res; - res = mEditor->GetEndNodeAndOffset(aSelection, address_of(selNode), &selOffset); + res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(selNode), &selOffset); if (NS_FAILED(res)) return res; res = mUtilRange->SetEnd(selNode, selOffset); if (NS_FAILED(res)) return res; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h index d827487b3c5..88fe713cd8d 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h @@ -34,6 +34,7 @@ class nsISupportsArray; class nsVoidArray; class nsIDOMElement; class nsIEditor; +class nsHTMLEditor; class nsHTMLEditRules : public nsIHTMLEditRules, public nsTextEditRules, public nsIEditActionListener { @@ -46,7 +47,7 @@ public: // nsIEditRules methods - NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); + NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags); NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); @@ -98,10 +99,10 @@ protected: nsString *outString, PRInt32 aMaxLength); nsresult WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled); + nsresult DidInsertBreak(nsISelection *aSelection, nsresult aResult); nsresult WillDeleteSelection(nsISelection *aSelection, nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled); nsresult DeleteNonTableElements(nsIDOMNode *aNode); - nsresult WillMakeList(nsISelection *aSelection, const nsString *aListType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull); nsresult WillRemoveList(nsISelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); nsresult WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled); @@ -110,17 +111,13 @@ protected: nsresult WillMakeDefListItem(nsISelection *aSelection, const nsString *aBlockType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled); nsresult WillMakeBasicBlock(nsISelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled); nsresult DidMakeBasicBlock(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); - nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsString *alignType); nsresult AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType); nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE); - nsresult InsertTab(nsISelection *aSelection, nsString *outString); - nsresult ReturnInHeader(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset); nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled); nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset); - nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection); nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr *outList, const nsString& aListType, const nsString& aItemType); nsresult CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc); @@ -132,7 +129,6 @@ protected: PRBool IsLastNode(nsIDOMNode *aNode); PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock); PRBool AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock); - nsresult GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset, PRInt32 actionID, nsCOMPtr *outNode, PRInt32 *outOffset); nsresult GetPromotedRanges(nsISelection *inSelection, @@ -154,20 +150,16 @@ protected: nsCOMPtr GetHighestInlineParent(nsIDOMNode* aNode); nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, nsVoidArray *inTransitionArray); - nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag); nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes); nsresult SplitAsNeeded(const nsString *aTag, nsCOMPtr *inOutParent, PRInt32 *inOutOffset); nsresult AddTerminatingBR(nsIDOMNode *aBlock); - nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft, nsIDOMNode *aNodeRight, nsCOMPtr *aOutMergeParent, PRInt32 *aOutMergeOffset); - - nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode); + nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode, PRBool aPlaintext); nsresult PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList); - nsresult AdjustSpecialBreaks(PRBool aSafeToAskFrames = PR_FALSE); nsresult AdjustWhitespace(nsISelection *aSelection); nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction); @@ -180,14 +172,13 @@ protected: nsresult SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult); nsresult DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aStart, PRInt32 aEnd); nsresult UpdateDocChangeRange(nsIDOMRange *aRange); - nsresult ConvertWhitespace(const nsString & inString, nsString & outString); nsresult ConfirmSelectionInBody(); - nsresult InsertMozBRIfNeeded(nsIDOMNode *aNode); // data members protected: + nsHTMLEditor *mHTMLEditor; nsCOMPtr mDocChangeRange; PRBool mListenerEnabled; PRBool mReturnInEmptyLIKillsList; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp b/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp index e7057287acd..787aa3044b2 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp @@ -26,6 +26,7 @@ #include "nsEditor.h" #include "nsIDOMNode.h" #include "nsIDOMNodeList.h" +#include "nsIDOMHTMLAnchorElement.h" /******************************************************** * helper methods from nsTextEditRules @@ -487,6 +488,34 @@ nsHTMLEditUtils::IsImage(nsIDOMNode *node) return PR_FALSE; } +PRBool +nsHTMLEditUtils::IsLink(nsIDOMNode *aNode) +{ + if (!aNode) return PR_FALSE; + nsCOMPtr anchor = do_QueryInterface(aNode); + if (anchor) + { + nsAutoString tmpText; + if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) + return PR_TRUE; + } + return PR_FALSE; +} + +PRBool +nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode) +{ + if (!aNode) return PR_FALSE; + nsCOMPtr anchor = do_QueryInterface(aNode); + if (anchor) + { + nsAutoString tmpText; + if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) + return PR_TRUE; + } + return PR_FALSE; +} + /////////////////////////////////////////////////////////////////////////// // IsDiv: true if node an html div node diff --git a/mozilla/editor/libeditor/html/nsHTMLEditUtils.h b/mozilla/editor/libeditor/html/nsHTMLEditUtils.h index 4904f271157..da1228289b6 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditUtils.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditUtils.h @@ -58,6 +58,8 @@ public: static PRBool IsAddress(nsIDOMNode *aNode); static PRBool IsAnchor(nsIDOMNode *aNode); static PRBool IsImage(nsIDOMNode *aNode); + static PRBool IsLink(nsIDOMNode *aNode); + static PRBool IsNamedAnchor(nsIDOMNode *aNode); static PRBool IsDiv(nsIDOMNode *aNode); static PRBool IsNormalDiv(nsIDOMNode *aNode); static PRBool IsMozDiv(nsIDOMNode *aNode); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 505c5656869..b841561a8ee 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -119,16 +119,6 @@ static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); -// Drag & Drop, Clipboard Support -static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); -static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); -static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); -static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); -// private clipboard data flavors for html copy/paste -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" - - #if defined(NS_DEBUG) && defined(DEBUG_buster) static PRBool gNoisy = PR_FALSE; #else @@ -139,105 +129,18 @@ static const PRBool gNoisy = PR_FALSE; static char hrefText[] = "href"; static char anchorTxt[] = "anchor"; static char namedanchorText[] = "namedanchor"; -nsIAtom *nsHTMLEditor::gTypingTxnName; -nsIAtom *nsHTMLEditor::gIMETxnName; -nsIAtom *nsHTMLEditor::gDeleteTxnName; // some prototypes for rules creation shortcuts nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); -#define IsLink(s) (s.EqualsIgnoreCase(hrefText)) -#define IsNamedAnchor(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) - -static PRBool IsLinkNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtr anchor = do_QueryInterface(aNode); - if (anchor) - { - nsAutoString tmpText; - if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) - return PR_TRUE; - } - } - return PR_FALSE; -} - -static PRBool IsNamedAnchorNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtr anchor = do_QueryInterface(aNode); - if (anchor) - { - nsAutoString tmpText; - if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) - return PR_TRUE; - } - } - return PR_FALSE; -} - -static PRBool IsListNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtrelement; - element = do_QueryInterface(aNode); - if (element) - { - nsAutoString tagName; - if (NS_SUCCEEDED(element->GetTagName(tagName))) - { - tagName.ToLowerCase(); - // With only 3 tests, it doesn't - // seem worth using nsAtoms - if (tagName.EqualsWithConversion("ol") || - tagName.EqualsWithConversion("ul") || - tagName.EqualsWithConversion("dl")) - { - return PR_TRUE; - } - } - } - } - return PR_FALSE; -} - -static PRBool IsCellNode(nsIDOMNode *aNode) -{ - if (aNode) - { - nsCOMPtrelement; - element = do_QueryInterface(aNode); - if (element) - { - nsAutoString tagName; - if (NS_SUCCEEDED(element->GetTagName(tagName))) - { - // With only 2 tests, it doesn't - // seem worth using nsAtoms - if (tagName.EqualsIgnoreCase("td") || - tagName.EqualsIgnoreCase("th")) - { - return PR_TRUE; - } - } - } - } - return PR_FALSE; -} - +#define IsLinkTag(s) (s.EqualsIgnoreCase(hrefText)) +#define IsNamedAnchorTag(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) nsHTMLEditor::nsHTMLEditor() -: nsEditor() +: nsPlaintextEditor() , mIgnoreSpuriousDragEvent(PR_FALSE) , mTypeInState(nsnull) -, mRules(nsnull) -, mIsComposing(PR_FALSE) -, mMaxTextLength(-1) , mSelectedCellIndex(0) { // Done in nsEditor @@ -247,58 +150,10 @@ nsHTMLEditor::nsHTMLEditor() mUnderlineAtom = getter_AddRefs(NS_NewAtom("u")); mFontAtom = getter_AddRefs(NS_NewAtom("font")); mLinkAtom = getter_AddRefs(NS_NewAtom("a")); - - if (!gTypingTxnName) - gTypingTxnName = NS_NewAtom("Typing"); - else - NS_ADDREF(gTypingTxnName); - if (!gIMETxnName) - gIMETxnName = NS_NewAtom("IME"); - else - NS_ADDREF(gIMETxnName); - if (!gDeleteTxnName) - gDeleteTxnName = NS_NewAtom("Deleting"); - else - NS_ADDREF(gDeleteTxnName); } nsHTMLEditor::~nsHTMLEditor() { - /* first, delete the transaction manager if there is one. - this will release any remaining transactions. - this is important because transactions can hold onto the atoms (gTypingTxnName, ...) - and to make the optimization (holding refcounted statics) work correctly, - the editor instance needs to hold the last refcount. - If you get this wrong, expect to deref a garbage gTypingTxnName pointer if you bring up a second editor. - */ - if (mTxnMgr) { - mTxnMgr = 0; - } - nsrefcnt refCount=0; - if (gTypingTxnName) // we addref'd in the constructor - { // want to release it without nulling out the pointer. - refCount = gTypingTxnName->Release(); - if (0==refCount) { - gTypingTxnName = nsnull; - } - } - - if (gIMETxnName) // we addref'd in the constructor - { // want to release it without nulling out the pointer. - refCount = gIMETxnName->Release(); - if (0==refCount) { - gIMETxnName = nsnull; - } - } - - if (gDeleteTxnName) // we addref'd in the constructor - { // want to release it without nulling out the pointer. - refCount = gDeleteTxnName->Release(); - if (0==refCount) { - gDeleteTxnName = nsnull; - } - } - // remove the rules as an action listener. Else we get a bad ownership loop later on. // it's ok if the rules aren't a listener; we ignore the error. nsCOMPtr mListener = do_QueryInterface(mRules); @@ -318,31 +173,6 @@ nsHTMLEditor::~nsHTMLEditor() selPriv->RemoveSelectionListener(listener); } } - nsCOMPtr erP; - result = GetDOMEventReceiver(getter_AddRefs(erP)); - if (NS_SUCCEEDED(result) && erP) - { - if (mKeyListenerP) { - erP->RemoveEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener)); - } - if (mMouseListenerP) { - erP->RemoveEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener)); - } - if (mTextListenerP) { - erP->RemoveEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener)); - } - if (mCompositionListenerP) { - erP->RemoveEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener)); - } - if (mFocusListenerP) { - erP->RemoveEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener)); - } - if (mDragListenerP) { - erP->RemoveEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener)); - } - } - else - NS_NOTREACHED("~nsTextEditor"); NS_IF_RELEASE(mTypeInState); } @@ -400,148 +230,56 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, if (!aDoc || !aPresShell) return NS_ERROR_NULL_POINTER; - nsresult result = NS_ERROR_NULL_POINTER; - // Init the base editor - result = nsEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags); - if (NS_FAILED(result)) { return result; } - - // disable links - nsCOMPtr context; - aPresShell->GetPresContext(getter_AddRefs(context)); - if (!context) return NS_ERROR_NULL_POINTER; - if (!(mFlags & eEditorPlaintextMask)) - context->SetLinkHandler(0); - - nsCOMPtr bodyElement; - result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(result)) { return result; } - if (!bodyElement) { return NS_ERROR_NULL_POINTER; } - - // init the type-in state - mTypeInState = new TypeInState(); - if (!mTypeInState) {return NS_ERROR_NULL_POINTER;} - NS_ADDREF(mTypeInState); - - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) { return result; } - if (selection) + nsresult result = NS_OK, rulesRes = NS_OK; + + if (1) { - nsCOMPtr selPriv(do_QueryInterface(selection)); - nsCOMPtrlistener; - listener = do_QueryInterface(mTypeInState); - if (listener) { - selPriv->AddSelectionListener(listener); + // block to scope nsAutoEditInitRulesTrigger + nsAutoEditInitRulesTrigger rulesTrigger(NS_STATIC_CAST(nsPlaintextEditor*,this), rulesRes); + + // Init the plaintext editor + result = nsPlaintextEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags); + if (NS_FAILED(result)) { return result; } + + // disable links + nsCOMPtr context; + aPresShell->GetPresContext(getter_AddRefs(context)); + if (!context) return NS_ERROR_NULL_POINTER; + if (!(mFlags & eEditorPlaintextMask)) + context->SetLinkHandler(0); + + nsCOMPtr bodyElement; + result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(result)) { return result; } + if (!bodyElement) { return NS_ERROR_NULL_POINTER; } + + // init the type-in state + mTypeInState = new TypeInState(); + if (!mTypeInState) {return NS_ERROR_NULL_POINTER;} + NS_ADDREF(mTypeInState); + + nsCOMPtrselection; + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { return result; } + if (selection) + { + nsCOMPtr selPriv(do_QueryInterface(selection)); + nsCOMPtrlistener; + listener = do_QueryInterface(mTypeInState); + if (listener) { + selPriv->AddSelectionListener(listener); + } } + + // Set up a DTD + mDTD = do_CreateInstance(kCTransitionalDTDCID); + if (!mDTD) result = NS_ERROR_FAILURE; } - // Set up a DTD XXX XXX - // HACK: This should have happened in a document specific way - // in nsEditor::Init(), but we dont' have a way to do that yet - result = nsComponentManager::CreateInstance(kCTransitionalDTDCID, nsnull, - NS_GET_IID(nsIDTD), getter_AddRefs(mDTD)); - if (!mDTD) result = NS_ERROR_FAILURE; - if (NS_FAILED(result)) return result; - - // Init the rules system - result = InitRules(); - if (NS_FAILED(result)) return result; - - EnableUndo(PR_TRUE); - + if (NS_FAILED(rulesRes)) return rulesRes; return result; } -NS_IMETHODIMP -nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) -{ - nsresult result; - - result = nsEditor::SetDocumentCharacterSet(characterSet); - - // update META charset tag - if (NS_SUCCEEDED(result)) { - nsCOMPtrdomdoc; - result = GetDocument(getter_AddRefs(domdoc)); - if (NS_SUCCEEDED(result) && domdoc) { - nsAutoString newMetaString; - nsCOMPtrmetaList; - nsCOMPtrmetaNode; - nsCOMPtrmetaElement; - PRBool newMetaCharset = PR_TRUE; - - // get a list of META tags - result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("meta"), getter_AddRefs(metaList)); - if (NS_SUCCEEDED(result) && metaList) { - PRUint32 listLength = 0; - (void) metaList->GetLength(&listLength); - - for (PRUint32 i = 0; i < listLength; i++) { - metaList->Item(i, getter_AddRefs(metaNode)); - if (!metaNode) continue; - metaElement = do_QueryInterface(metaNode); - if (!metaElement) continue; - - const NS_ConvertASCIItoUCS2 content("charset="); - nsString currentValue; - - if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("http-equiv"), currentValue))) continue; - - if (kNotFound != currentValue.Find("content-type", PR_TRUE)) { - if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("content"), currentValue))) continue; - - PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE); - if (kNotFound != offset) { - currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html) - newMetaString.Append(content); - newMetaString.Append(characterSet); - result = nsEditor::SetAttribute(metaElement, NS_ConvertASCIItoUCS2("content"), newMetaString); - if (NS_SUCCEEDED(result)) - newMetaCharset = PR_FALSE; - break; - } - } - } - } - - if (newMetaCharset) { - nsCOMPtrheadList; - nsCOMPtrheadNode; - nsCOMPtrresultNode; - - result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"),getter_AddRefs(headList)); - if (NS_SUCCEEDED(result) && headList) { - headList->Item(0, getter_AddRefs(headNode)); - if (headNode) { - // Create a new meta charset tag - // can't use |NS_LITERAL_STRING| here until |CreateNode| is fixed to accept readables - result = CreateNode(NS_ConvertASCIItoUCS2("meta"), headNode, 0, getter_AddRefs(resultNode)); - if (NS_FAILED(result)) - return NS_ERROR_FAILURE; - - // Set attributes to the created element - if (resultNode && nsCRT::strlen(characterSet) > 0) { - metaElement = do_QueryInterface(resultNode); - if (metaElement) { - // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_LITERAL_STRING("http-equiv"), NS_LITERAL_STRING("Content-Type")); - if (NS_SUCCEEDED(result)) { - newMetaString.AssignWithConversion("text/html;charset="); - newMetaString.Append(characterSet); - // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_LITERAL_STRING("content"), newMetaString); - } - } - } - } - } - } - } - } - - return result; -} - NS_IMETHODIMP nsHTMLEditor::PostCreate() { @@ -668,17 +406,11 @@ nsHTMLEditor::SetFlags(PRUint32 aFlags) NS_IMETHODIMP nsHTMLEditor::InitRules() { -// instantiate the rules for this text editor -// XXX: we should be told which set of rules to instantiate - nsresult res = NS_ERROR_FAILURE; - if (mFlags & eEditorPlaintextMask) - res = NS_NewTextEditRules(getter_AddRefs(mRules)); - else - res = NS_NewHTMLEditRules(getter_AddRefs(mRules)); - + // instantiate the rules for the html editor + nsresult res = NS_NewHTMLEditRules(getter_AddRefs(mRules)); if (NS_FAILED(res)) return res; if (!mRules) return NS_ERROR_UNEXPECTED; - res = mRules->Init(this, mFlags); + res = mRules->Init(NS_STATIC_CAST(nsPlaintextEditor*,this), mFlags); return res; } @@ -797,18 +529,15 @@ NS_IMETHODIMP nsHTMLEditor::TypedText(const PRUnichar* aString, switch (aAction) { case eTypedText: + case eTypedBreak: { - return InsertText(aString); + return nsPlaintextEditor::TypedText(aString, aAction); } case eTypedBR: { nsCOMPtr brNode; return InsertBR(address_of(brNode)); // only inserts a br node } - case eTypedBreak: - { - return InsertLineBreak(); // uses rules to figure out what to insert - } } return NS_ERROR_FAILURE; } @@ -1020,1179 +749,6 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) return res; } -NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aProperty) { return NS_ERROR_NULL_POINTER; } - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - ForceCompositionEnd(); - - nsresult res; - nsCOMPtrselection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - if (!selection) return NS_ERROR_NULL_POINTER; - nsCOMPtr selPriv(do_QueryInterface(selection)); - - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - if (isCollapsed) - { - // manipulating text attributes on a collapsed selection only sets state for the next text insertion - return mTypeInState->SetProp(aProperty, *aAttribute, *aValue); - } - - nsAutoEditBatch batchIt(this); - nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); - nsAutoSelectionReset selectionResetter(selection, this); - nsAutoTxnsConserveSelection dontSpazMySelection(this); - - PRBool cancel, handled; - nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (!cancel && !handled) - { - // get selection range enumerator - nsCOMPtr enumerator; - res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(res)) return res; - if (!enumerator) return NS_ERROR_FAILURE; - - // loop thru the ranges in the selection - enumerator->First(); - nsCOMPtr currentItem; - while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) - { - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(res)) return res; - if (!currentItem) return NS_ERROR_FAILURE; - - nsCOMPtr range( do_QueryInterface(currentItem) ); - - // adjust range to include any ancestors who's children are entirely selected - res = PromoteInlineRange(range); - if (NS_FAILED(res)) return res; - - // check for easy case: both range endpoints in same text node - nsCOMPtr startNode, endNode; - res = range->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = range->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - if ((startNode == endNode) && IsTextNode(startNode)) - { - PRInt32 startOffset, endOffset; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - nsCOMPtr nodeAsText = do_QueryInterface(startNode); - res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - } - else - { - // not the easy case. range not contained in single text node. - // there are up to three phases here. There are all the nodes - // reported by the subtree iterator to be processed. And there - // are potentially a starting textnode and an ending textnode - // which are only partially contained by the range. - - // lets handle the nodes reported by the iterator. These nodes - // are entirely contained in the selection range. We build up - // a list of them (since doing operations on the document during - // iteration would perturb the iterator). - - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; - if (!iter) return NS_ERROR_FAILURE; - - nsCOMPtr arrayOfNodes; - nsCOMPtr content; - nsCOMPtr node; - nsCOMPtr isupports; - - // make a array - res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); - if (NS_FAILED(res)) return res; - - // iterate range and build up array - res = iter->Init(range); - // init returns an error if no nodes in range. - // this can easily happen with the subtree - // iterator if the selection doesn't contain - // any *whole* nodes. - if (NS_SUCCEEDED(res)) - { - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - res = iter->CurrentNode(getter_AddRefs(content)); - if (NS_FAILED(res)) return res; - node = do_QueryInterface(content); - if (!node) return NS_ERROR_FAILURE; - if (IsEditable(node)) - { - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); - } - res = iter->Next(); - if (NS_FAILED(res)) return res; - } - } - // first check the start parent of the range to see if it needs to - // be seperately handled (it does if it's a text node, due to how the - // subtree iterator works - it will not have reported it). - if (IsTextNode(startNode) && IsEditable(startNode)) - { - nsCOMPtr nodeAsText = do_QueryInterface(startNode); - PRInt32 startOffset; - PRUint32 textLen; - range->GetStartOffset(&startOffset); - nodeAsText->GetLength(&textLen); - res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - } - - // then loop through the list, set the property on each node - PRUint32 listCount; - PRUint32 j; - arrayOfNodes->Count(&listCount); - for (j = 0; j < listCount; j++) - { - isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); - node = do_QueryInterface(isupports); - res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - arrayOfNodes->RemoveElementAt(0); - } - - // last check the end parent of the range to see if it needs to - // be seperately handled (it does if it's a text node, due to how the - // subtree iterator works - it will not have reported it). - if (IsTextNode(endNode) && IsEditable(endNode)) - { - nsCOMPtr nodeAsText = do_QueryInterface(endNode); - PRInt32 endOffset; - range->GetEndOffset(&endOffset); - res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - } - } - enumerator->Next(); - } - } - if (!cancel) - { - // post-process - res = mRules->DidDoAction(selection, &ruleInfo, res); - } - return res; -} - - - -nsresult -nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, - PRInt32 aStartOffset, - PRInt32 aEndOffset, - nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aTextNode) return NS_ERROR_NULL_POINTER; - - // dont need to do anything if no characters actually selected - if (aStartOffset == aEndOffset) return NS_OK; - - nsresult res = NS_OK; - nsCOMPtr tmp, node = do_QueryInterface(aTextNode); - - // dont need to do anything if property already set on node - PRBool bHasProp; - nsCOMPtr styleNode; - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); - if (bHasProp) return NS_OK; - - // do we need to split the text node? - PRUint32 textLen; - aTextNode->GetLength(&textLen); - - if ( (PRUint32)aEndOffset != textLen ) - { - // we need to split off back of text node - res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); - if (NS_FAILED(res)) return res; - node = tmp; // remember left node - } - if ( aStartOffset ) - { - // we need to split off front of text node - res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); - if (NS_FAILED(res)) return res; - } - - // reparent the node inside inline node with appropriate {attribute,value} - res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); - return res; -} - - -nsresult -nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode, - nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; - - nsresult res = NS_OK; - nsCOMPtr tmp; - nsAutoString tag; - aProperty->ToString(tag); - tag.ToLowerCase(); - - // dont need to do anything if property already set on node - PRBool bHasProp; - nsCOMPtr styleNode; - IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); - if (bHasProp) return NS_OK; - - // is it already the right kind of node, but with wrong attribute? - if (NodeIsType(aNode, aProperty)) - { - // just set the attribute on it. - // but first remove any contrary style in it's children. - res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE); - if (NS_FAILED(res)) return res; - nsCOMPtr elem = do_QueryInterface(aNode); - return SetAttribute(elem, *aAttribute, *aValue); - } - - // can it be put inside inline node? - if (TagCanContain(tag, aNode)) - { - nsCOMPtr priorNode, nextNode; - // is either of it's neighbors the right kind of node? - GetPriorHTMLSibling(aNode, address_of(priorNode)); - GetNextHTMLSibling(aNode, address_of(nextNode)); - if (priorNode && NodeIsType(priorNode, aProperty) && - HasAttrVal(priorNode, aAttribute, aValue) && - IsOnlyAttribute(priorNode, aAttribute) ) - { - // previous sib is already right kind of inline node; slide this over into it - res = MoveNode(aNode, priorNode, -1); - } - else if (nextNode && NodeIsType(nextNode, aProperty) && - HasAttrVal(nextNode, aAttribute, aValue) && - IsOnlyAttribute(priorNode, aAttribute) ) - { - // following sib is already right kind of inline node; slide this over into it - res = MoveNode(aNode, nextNode, 0); - } - else - { - // ok, chuck it in it's very own container - res = InsertContainerAbove(aNode, address_of(tmp), tag, aAttribute, aValue); - } - if (NS_FAILED(res)) return res; - return RemoveStyleInside(aNode, aProperty, aAttribute); - } - // none of the above? then cycle through the children. - nsCOMPtr childNodes; - res = aNode->GetChildNodes(getter_AddRefs(childNodes)); - if (NS_FAILED(res)) return res; - if (childNodes) - { - PRInt32 j; - PRUint32 childCount; - childNodes->GetLength(&childCount); - if (childCount) - { - nsCOMPtr arrayOfNodes; - nsCOMPtr node; - nsCOMPtr isupports; - - // make a array - res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); - if (NS_FAILED(res)) return res; - - // populate the list - for (j=0 ; j < (PRInt32)childCount; j++) - { - nsCOMPtr childNode; - res = childNodes->Item(j, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode)) - { - isupports = do_QueryInterface(childNode); - arrayOfNodes->AppendElement(isupports); - } - } - - // then loop through the list, set the property on each node - PRUint32 listCount; - arrayOfNodes->Count(&listCount); - for (j = 0; j < (PRInt32)listCount; j++) - { - isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); - node = do_QueryInterface(isupports); - res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); - if (NS_FAILED(res)) return res; - arrayOfNodes->RemoveElementAt(0); - } - } - } - return res; -} - - -nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, - nsIAtom *aProperty, - const nsString *aAttribute) -{ - if (!inRange) return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr startNode, endNode, origStartNode; - PRInt32 startOffset, endOffset, origStartOffset; - - res = inRange->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetStartOffset(&startOffset); - if (NS_FAILED(res)) return res; - res = inRange->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetEndOffset(&endOffset); - if (NS_FAILED(res)) return res; - - origStartNode = startNode; - origStartOffset = startOffset; - PRBool sameNode = (startNode==endNode); - - // split any matching style nodes above the start of range - res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - - if (sameNode && (startNode != origStartNode)) - { - // our startNode got split. This changes the offset of the end of our range. - endOffset -= origStartOffset; - } - - // second verse, same as the first... - res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - - // reset the range - res = inRange->SetStart(startNode, startOffset); - if (NS_FAILED(res)) return res; - res = inRange->SetEnd(endNode, endOffset); - return res; -} - -nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr *aNode, - PRInt32 *aOffset, - nsIAtom *aProperty, // null here means we split all properties - const nsString *aAttribute, - nsCOMPtr *outLeftNode, - nsCOMPtr *outRightNode) -{ - if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER; - if (outLeftNode) *outLeftNode = nsnull; - if (outRightNode) *outRightNode = nsnull; - // split any matching style nodes above the node/offset - nsCOMPtr parent, tmp = *aNode; - PRInt32 offset; - while (tmp && !nsHTMLEditUtils::IsBody(tmp)) - { - if ( (aProperty && NodeIsType(tmp, aProperty)) || // node is the correct inline prop - (aProperty == nsIEditProperty::href && IsLinkNode(tmp)) || // node is href - test if really GetParentNode(getter_AddRefs(*aNode)); - *aOffset = offset; - } - tmp->GetParentNode(getter_AddRefs(parent)); - tmp = parent; - } - return NS_OK; -} - -PRBool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode) -{ - if (!aNode) return PR_FALSE; - if (!IsContainer(aNode)) return PR_FALSE; - if (!IsEditable(aNode)) return PR_FALSE; - if (!IsInlineNode(aNode)) return PR_FALSE; - if (NodeIsType(aNode, nsIEditProperty::a)) return PR_FALSE; - return PR_TRUE; -} - -nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, - nsIAtom *aProperty, // null here means remove all properties - const nsString *aAttribute, - PRBool aChildrenOnly) -{ - if (!aNode) return NS_ERROR_NULL_POINTER; - if (IsTextNode(aNode)) return NS_OK; - nsresult res = NS_OK; - - // first process the children - nsCOMPtr child, tmp; - aNode->GetFirstChild(getter_AddRefs(child)); - while (child) - { - // cache next sibling since we might remove child - child->GetNextSibling(getter_AddRefs(tmp)); - res = RemoveStyleInside(child, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - child = tmp; - } - - // then process the node itself - if ( !aChildrenOnly && - ((aProperty && NodeIsType(aNode, aProperty)) || // node is prop we asked for - (aProperty == nsIEditProperty::href && IsLinkNode(aNode))) || // but check for link (IsEmpty()) - { - res = RemoveContainer(aNode); - } - // otherwise we just want to eliminate the attribute - else - { - if (HasAttr(aNode, aAttribute)) - { - // if this matching attribute is the ONLY one on the node, - // then remove the whole node. Otherwise just nix the attribute. - if (IsOnlyAttribute(aNode, aAttribute)) - { - res = RemoveContainer(aNode); - } - else - { - nsCOMPtr elem = do_QueryInterface(aNode); - if (!elem) return NS_ERROR_NULL_POINTER; - res = RemoveAttribute(elem, *aAttribute); - } - } - } - } - return res; -} - -PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, - const nsString *aAttribute) -{ - if (!aNode || !aAttribute) return PR_FALSE; // ooops - nsCOMPtr content = do_QueryInterface(aNode); - if (!content) return PR_FALSE; // ooops - - PRInt32 attrCount, i, nameSpaceID; - nsCOMPtr attrName, prefix; - content->GetAttributeCount(attrCount); - - for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), - *getter_AddRefs(prefix)); - nsAutoString attrString, tmp; - if (!attrName) continue; // ooops - attrName->ToString(attrString); - // if it's the attribute we know about, keep looking - if (attrString.EqualsIgnoreCase(*aAttribute)) continue; - // if it's a special _moz... attribute, keep looking - attrString.Left(tmp,4); - if (tmp.EqualsWithConversion("_moz")) continue; - // otherwise, it's another attribute, so return false - return PR_FALSE; - } - // if we made it through all of them without finding a real attribute - // other than aAttribute, then return PR_TRUE - return PR_TRUE; -} - -PRBool -nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1, - nsIDOMNode *aNode2) -{ - if (!aNode1 || !aNode2) return PR_FALSE; // ooops - nsCOMPtr content1 = do_QueryInterface(aNode1); - if (!content1) return PR_FALSE; // ooops - nsCOMPtr content2 = do_QueryInterface(aNode2); - if (!content2) return PR_FALSE; // ooops - - PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0; - nsCOMPtr attrName, prefix; - nsresult res, res2; - content1->GetAttributeCount(attrCount); - nsAutoString attrString, tmp, attrVal1, attrVal2; - - for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), - *getter_AddRefs(prefix)); - if (!attrName) continue; // ooops - attrName->ToString(attrString); - // if it's a special _moz... attribute, keep going - attrString.Left(tmp,4); - if (tmp.EqualsWithConversion("_moz")) continue; - // otherwise, it's another attribute, so count it - realCount1++; - // and compare it to element2's attributes - res = content1->GetAttribute(nameSpaceID, attrName, attrVal1); - res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2); - if (res != res2) return PR_FALSE; - if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE; - } - - content2->GetAttributeCount(attrCount); - for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), - *getter_AddRefs(prefix)); - if (!attrName) continue; // ooops - attrName->ToString(attrString); - // if it's a special _moz... attribute, keep going - attrString.Left(tmp,4); - if (tmp.EqualsWithConversion("_moz")) continue; - // otherwise, it's another attribute, so count it - realCount2++; - } - - if (realCount1 != realCount2) return PR_FALSE; - // otherwise, attribute counts match, and we already compared them - // when going through the first list, so we're done. - return PR_TRUE; -} - -PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, - const nsString *aAttribute) -{ - if (!aNode) return PR_FALSE; - if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute - - // get element - nsCOMPtr elem = do_QueryInterface(aNode); - if (!elem) return PR_FALSE; - - // get attribute node - nsCOMPtr attNode; - nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); - if ((NS_FAILED(res)) || !attNode) return PR_FALSE; - return PR_TRUE; -} - - -PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, - const nsString *aAttribute, - const nsString *aValue) -{ - if (!aNode) return PR_FALSE; - if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute - - // get element - nsCOMPtr elem = do_QueryInterface(aNode); - if (!elem) return PR_FALSE; - - // get attribute node - nsCOMPtr attNode; - nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); - if ((NS_FAILED(res)) || !attNode) return PR_FALSE; - - // check if attribute has a value - PRBool isSet; - attNode->GetSpecified(&isSet); - // if no value, and that's what we wanted, then return true - if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; - - // get attribute value - nsAutoString attrVal; - attNode->GetValue(attrVal); - - // do values match? - if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE; - return PR_FALSE; -} - - -nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange) -{ - if (!inRange) return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr startNode, endNode, parent; - PRInt32 startOffset, endOffset; - - res = inRange->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetStartOffset(&startOffset); - if (NS_FAILED(res)) return res; - res = inRange->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - res = inRange->GetEndOffset(&endOffset); - if (NS_FAILED(res)) return res; - - while ( startNode && - !nsHTMLEditUtils::IsBody(startNode) && - IsAtFrontOfNode(startNode, startOffset) ) - { - res = GetNodeLocation(startNode, address_of(parent), &startOffset); - if (NS_FAILED(res)) return res; - startNode = parent; - } - if (!startNode) return NS_ERROR_NULL_POINTER; - - while ( endNode && - !nsHTMLEditUtils::IsBody(endNode) && - IsAtEndOfNode(endNode, endOffset) ) - { - res = GetNodeLocation(endNode, address_of(parent), &endOffset); - if (NS_FAILED(res)) return res; - endNode = parent; - endOffset++; // we are AFTER this node - } - if (!endNode) return NS_ERROR_NULL_POINTER; - - res = inRange->SetStart(startNode, startOffset); - if (NS_FAILED(res)) return res; - res = inRange->SetEnd(endNode, endOffset); - return res; -} - -PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset) -{ - if (!aNode) return PR_FALSE; // oops - if (!aOffset) return PR_TRUE; - - if (IsTextNode(aNode)) - { - return PR_FALSE; - } - else - { - nsCOMPtr firstNode; - GetFirstEditableChild(aNode, address_of(firstNode)); - if (!firstNode) return PR_TRUE; - PRInt32 offset; - nsEditor::GetChildOffset(firstNode, aNode, offset); - if (offset < aOffset) return PR_FALSE; - return PR_TRUE; - } -} - -PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) -{ - if (!aNode) return PR_FALSE; // oops - PRUint32 len; - GetLengthOfDOMNode(aNode, len); - if (aOffset == (PRInt32)len) return PR_TRUE; - - if (IsTextNode(aNode)) - { - return PR_FALSE; - } - else - { - nsCOMPtr lastNode; - GetLastEditableChild(aNode, address_of(lastNode)); - if (!lastNode) return PR_TRUE; - PRInt32 offset; - nsEditor::GetChildOffset(lastNode, aNode, offset); - if (offset < aOffset) return PR_TRUE; - return PR_FALSE; - } -} - -NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue, - PRBool &aFirst, - PRBool &aAny, - PRBool &aAll) -{ - return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull); -} - -NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue, - PRBool &aFirst, - PRBool &aAny, - PRBool &aAll, - nsString *outValue) -{ - if (!aProperty) - return NS_ERROR_NULL_POINTER; -/* - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); } - nsCRT::free(propCString); - } -*/ - nsresult result; - aAny=PR_FALSE; - aAll=PR_TRUE; - aFirst=PR_FALSE; - PRBool first=PR_TRUE; - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - nsCOMPtr selPriv(do_QueryInterface(selection)); - - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - nsCOMPtr collapsedNode; - nsCOMPtr enumerator; - result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; - - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - // XXX: should be a while loop, to get each separate range - // XXX: ERROR_HANDLING can currentItem be null? - if ((NS_SUCCEEDED(result)) && currentItem) - { - PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag - nsCOMPtr range(do_QueryInterface(currentItem)); - - if (isCollapsed) - { - // efficiency hack. we cache prior results for being collapsed in a given text node. - // this speeds up typing. Note that other parts of the editor code have to clear out - // this cache after certain actions. - range->GetStartContainer(getter_AddRefs(collapsedNode)); - if (!collapsedNode) return NS_ERROR_FAILURE; - // refresh the cache if we need to - if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode); - // cache now current, use it! But override it with typeInState results if any... - PRBool isSet, theSetting; - if (aAttribute) - mTypeInState->GetTypingState(isSet, theSetting, aProperty, *aAttribute, outValue); - else - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - return NS_OK; - } - /* - if (aProperty == mBoldAtom.get()) - { - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - } - else - { - aFirst = aAny = aAll = mCachedBoldStyle; - } - return NS_OK; - } - else if (aProperty == mItalicAtom.get()) - { - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - } - else - { - aFirst = aAny = aAll = mCachedItalicStyle; - } - return NS_OK; - } - else if (aProperty == mUnderlineAtom.get()) - { - mTypeInState->GetTypingState(isSet, theSetting, aProperty); - if (isSet) - { - aFirst = aAny = aAll = theSetting; - } - else - { - aFirst = aAny = aAll = mCachedUnderlineStyle; - } - return NS_OK; - } */ - } - - // either non-collapsed selection or no cached value: do it the hard way - nsCOMPtr iter; - result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(result)) return result; - if (!iter) return NS_ERROR_NULL_POINTER; - - iter->Init(range); - nsCOMPtr content; - nsAutoString firstValue, theValue; - iter->CurrentNode(getter_AddRefs(content)); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - //if (gNoisy) { printf(" checking node %p\n", content.get()); } - nsCOMPtrtext; - text = do_QueryInterface(content); - PRBool skipNode = PR_FALSE; - if (text) - { - if (!isCollapsed && first && firstNodeInRange) - { - firstNodeInRange = PR_FALSE; - PRInt32 startOffset; - range->GetStartOffset(&startOffset); - PRUint32 count; - text->GetLength(&count); - if (startOffset==(PRInt32)count) - { - //if (gNoisy) { printf(" skipping node %p\n", content.get()); } - skipNode = PR_TRUE; - } - } - } - else - { // handle non-text leaf nodes here - PRBool canContainChildren; - content->CanContainChildren(canContainChildren); - if (canContainChildren) - { - //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } - skipNode = PR_TRUE; - } - else { - //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } - } - } - if (!skipNode) - { - nsCOMPtrnode; - node = do_QueryInterface(content); - if (node) - { - PRBool isSet; - nsCOMPtrresultNode; - if (first) - { - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue); - aFirst = isSet; - first = PR_FALSE; - if (outValue) *outValue = firstValue; - } - else - { - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue); - if (firstValue != theValue) - aAll = PR_FALSE; - } - - if (isSet) { - aAny = PR_TRUE; - } - else { - aAll = PR_FALSE; - } - } - } - result = iter->Next(); - if (NS_FAILED(result)) - break; - iter->CurrentNode(getter_AddRefs(content)); - } - } - if (!aAny) - { // make sure that if none of the selection is set, we don't report all is set - aAll = PR_FALSE; - } - //if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); } - return result; -} - - -NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties() -{ - return RemoveInlinePropertyImpl(nsnull, nsnull); -} - -NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) -{ - return RemoveInlinePropertyImpl(aProperty, aAttribute); -} - -nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsString *aAttribute) -{ - if (!mRules) return NS_ERROR_NOT_INITIALIZED; - ForceCompositionEnd(); - - nsresult res; - nsCOMPtrselection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - if (!selection) return NS_ERROR_NULL_POINTER; - nsCOMPtr selPriv(do_QueryInterface(selection)); - - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - if (isCollapsed) - { - // manipulating text attributes on a collapsed selection only sets state for the next text insertion - - // For links, aProperty uses "href", use "a" instead - if (aProperty == nsIEditProperty::href) - aProperty = nsIEditProperty::a; - - if (aProperty) return mTypeInState->ClearProp(aProperty, *aAttribute); - else return mTypeInState->ClearAllProps(); - } - nsAutoEditBatch batchIt(this); - nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); - nsAutoSelectionReset selectionResetter(selection, this); - nsAutoTxnsConserveSelection dontSpazMySelection(this); - - PRBool cancel, handled; - nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (!cancel && !handled) - { - // get selection range enumerator - nsCOMPtr enumerator; - res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(res)) return res; - if (!enumerator) return NS_ERROR_FAILURE; - - // loop thru the ranges in the selection - enumerator->First(); - nsCOMPtr currentItem; - while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) - { - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(res)) return res; - if (!currentItem) return NS_ERROR_FAILURE; - - nsCOMPtr range( do_QueryInterface(currentItem) ); - - // adjust range to include any ancestors who's children are entirely selected - res = PromoteInlineRange(range); - if (NS_FAILED(res)) return res; - - // remove this style from ancestors of our range endpoints, - // splitting them as appropriate - res = SplitStyleAboveRange(range, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - - // check for easy case: both range endpoints in same text node - nsCOMPtr startNode, endNode; - res = range->GetStartContainer(getter_AddRefs(startNode)); - if (NS_FAILED(res)) return res; - res = range->GetEndContainer(getter_AddRefs(endNode)); - if (NS_FAILED(res)) return res; - if ((startNode == endNode) && IsTextNode(startNode)) - { - // we're done with this range! - } - else - { - // not the easy case. range not contained in single text node. - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; - if (!iter) return NS_ERROR_FAILURE; - - nsCOMPtr arrayOfNodes; - nsCOMPtr content; - nsCOMPtr node; - nsCOMPtr isupports; - - // make a array - res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); - if (NS_FAILED(res)) return res; - - // iterate range and build up array - iter->Init(range); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - res = iter->CurrentNode(getter_AddRefs(content)); - if (NS_FAILED(res)) return res; - node = do_QueryInterface(content); - if (!node) return NS_ERROR_FAILURE; - if (IsEditable(node)) - { - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); - } - res = iter->Next(); - if (NS_FAILED(res)) return res; - } - - // loop through the list, remove the property on each node - PRUint32 listCount; - PRUint32 j; - arrayOfNodes->Count(&listCount); - for (j = 0; j < listCount; j++) - { - isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); - node = do_QueryInterface(isupports); - res = RemoveStyleInside(node, aProperty, aAttribute); - if (NS_FAILED(res)) return res; - arrayOfNodes->RemoveElementAt(0); - } - } - enumerator->Next(); - } - } - if (!cancel) - { - // post-process - res = mRules->DidDoAction(selection, &ruleInfo, res); - } - return res; -} - -NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize() -{ - return RelativeFontChange(1); -} - -NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize() -{ - return RelativeFontChange(-1); -} - -nsresult nsHTMLEditor::GetTextSelectionOffsets(nsISelection *aSelection, - PRInt32 &aOutStartOffset, - PRInt32 &aOutEndOffset) -{ - if(!aSelection) { return NS_ERROR_NULL_POINTER; } - nsresult result; - // initialize out params - aOutStartOffset = 0; // default to first char in selection - aOutEndOffset = -1; // default to total length of text in selection - - nsCOMPtr startNode, endNode, parentNode; - PRInt32 startOffset, endOffset; - aSelection->GetAnchorNode(getter_AddRefs(startNode)); - aSelection->GetAnchorOffset(&startOffset); - aSelection->GetFocusNode(getter_AddRefs(endNode)); - aSelection->GetFocusOffset(&endOffset); - - nsCOMPtr enumerator; - nsCOMPtr selection(aSelection); - nsCOMPtr selPriv(do_QueryInterface(selection)); - result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; - - // don't use "result" in this block - enumerator->First(); - nsCOMPtr currentItem; - nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(findParentResult)) && (currentItem)) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - range->GetCommonAncestorContainer(getter_AddRefs(parentNode)); - } - else - { - parentNode = do_QueryInterface(startNode); - } - - - return GetAbsoluteOffsetsForPoints(startNode, startOffset, - endNode, endOffset, - parentNode, - aOutStartOffset, aOutEndOffset); -} - -nsresult -nsHTMLEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, - PRInt32 aInStartOffset, - nsIDOMNode *aInEndNode, - PRInt32 aInEndOffset, - nsIDOMNode *aInCommonParentNode, - PRInt32 &aOutStartOffset, - PRInt32 &aOutEndOffset) -{ - if(!aInStartNode || !aInEndNode || !aInCommonParentNode) - return NS_ERROR_NULL_POINTER; - - nsresult result; - // initialize out params - aOutStartOffset = 0; // default to first char in selection - aOutEndOffset = -1; // default to total length of text in selection - - nsCOMPtr iter; - result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(result)) return result; - if (!iter) return NS_ERROR_NULL_POINTER; - - PRUint32 totalLength=0; - nsCOMPtrtextNode; - nsCOMPtrblockParentContent = do_QueryInterface(aInCommonParentNode); - iter->Init(blockParentContent); - // loop through the content iterator for each content node - nsCOMPtr content; - result = iter->CurrentNode(getter_AddRefs(content)); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - textNode = do_QueryInterface(content); - if (textNode) - { - nsCOMPtrcurrentNode = do_QueryInterface(textNode); - if (!currentNode) {return NS_ERROR_NO_INTERFACE;} - if (IsEditable(currentNode)) - { - if (currentNode.get() == aInStartNode) - { - aOutStartOffset = totalLength + aInStartOffset; - } - if (currentNode.get() == aInEndNode) - { - aOutEndOffset = totalLength + aInEndOffset; - break; - } - PRUint32 length; - textNode->GetLength(&length); - totalLength += length; - } - } - iter->Next(); - iter->CurrentNode(getter_AddRefs(content)); - } - if (-1==aOutEndOffset) { - aOutEndOffset = totalLength; - } - - // guarantee that aOutStartOffset <= aOutEndOffset - if (aOutEndOffset end"); - return result; -} - nsresult nsHTMLEditor::GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver) { @@ -2299,692 +855,6 @@ nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsISelection *aSelect return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::EDirection aAction) -{ - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - PRBool cancel, handled; - nsresult result; - - // delete placeholder txns merge. - nsAutoPlaceHolderBatch batch(this, gDeleteTxnName); - nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); - - // If it's one of these modes, - // we have to extend the selection first. - // This needs to happen inside selection batching, - // otherwise the deleted text is autocopied to the clipboard. - if (aAction == eNextWord || aAction == ePreviousWord - || aAction == eToBeginningOfLine || aAction == eToEndOfLine) - { - if (!mSelConWeak) return NS_ERROR_NOT_INITIALIZED; - nsCOMPtr selCont (do_QueryReferent(mSelConWeak)); - if (!selCont) - return NS_ERROR_NO_INTERFACE; - - switch (aAction) - { - case eNextWord: - result = selCont->WordMove(PR_TRUE, PR_TRUE); - // DeleteSelectionImpl doesn't handle these actions - // because it's inside batching, so don't confuse it: - aAction = eNone; - break; - case ePreviousWord: - result = selCont->WordMove(PR_FALSE, PR_TRUE); - aAction = eNone; - break; - case eToBeginningOfLine: - selCont->IntraLineMove(PR_TRUE, PR_FALSE); // try to move to end - result = selCont->IntraLineMove(PR_FALSE, PR_TRUE); // select to beginning - aAction = eNone; - break; - case eToEndOfLine: - result = selCont->IntraLineMove(PR_TRUE, PR_TRUE); - // Bugs 54449/54452: the selection jumps to the wrong place - // when deleting past a
and action is eNext or ePrev, - // so setting action to eNone makes delete-to-end marginally usable. - // aAction should really be set to eNext - aAction = eNone; - break; - default: // avoid several compiler warnings - result = NS_OK; - break; - } - if (NS_FAILED(result)) - { -#ifdef DEBUG - printf("Selection controller interface didn't work!\n"); -#endif - return result; - } - } - - // pre-process - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - - nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection); - ruleInfo.collapsedAction = aAction; - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; - if (!cancel && !handled) - { - result = DeleteSelectionImpl(aAction); - } - if (!cancel) - { - // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); - } - - return result; -} - -NS_IMETHODIMP nsHTMLEditor::InsertText(const PRUnichar* aStringToInsert) -{ - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - PRBool cancel, handled; - PRInt32 theAction = nsTextEditRules::kInsertText; - PRInt32 opID = kOpInsertText; - if (mInIMEMode) - { - theAction = nsTextEditRules::kInsertTextIME; - opID = kOpInsertIMEText; - } - nsAutoPlaceHolderBatch batch(this, nsnull); - nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); - - // pre-process - nsresult result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoString resultString; - // XXX can we trust instring to outlive ruleInfo, - // XXX and ruleInfo not to refer to instring in its dtor? - nsAutoString instring(aStringToInsert); - nsTextRulesInfo ruleInfo(theAction); - ruleInfo.inString = &instring; - ruleInfo.outString = &resultString; - ruleInfo.maxLength = mMaxTextLength; - - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; - if (!cancel && !handled) - { - // we rely on rules code for now - no default implementation - } - if (!cancel) - { - // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); - } - return result; -} - - -static nsCOMPtr GetListParent(nsIDOMNode* aNode) -{ - if (!aNode) return nsnull; - nsCOMPtr parent, tmp; - aNode->GetParentNode(getter_AddRefs(parent)); - while (parent) - { - if (nsHTMLEditUtils::IsList(parent)) return parent; - parent->GetParentNode(getter_AddRefs(tmp)); - parent = tmp; - } - return nsnull; -} - -static nsCOMPtr GetTableParent(nsIDOMNode* aNode) -{ - if (!aNode) return nsnull; - nsCOMPtr parent, tmp; - aNode->GetParentNode(getter_AddRefs(parent)); - while (parent) - { - if (nsHTMLEditUtils::IsTable(parent)) return parent; - parent->GetParentNode(getter_AddRefs(tmp)); - parent = tmp; - } - return nsnull; -} - - -NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) -{ - nsAutoString charset; - return InsertHTMLWithCharset(aInputString, charset); -} - -nsresult nsHTMLEditor::InsertHTMLWithContext(const nsString& aInputString, const nsString& aContextStr, const nsString& aInfoStr) -{ - nsAutoString charset; - return InsertHTMLWithCharsetAndContext(aInputString, charset, aContextStr, aInfoStr); -} - - -NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset) -{ - return InsertHTMLWithCharsetAndContext(aInputString, aCharset, nsAutoString(), nsAutoString()); -} - -nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *nsrange, - const nsString& aInputString, - const nsString& aContextStr, - const nsString& aInfoStr, - nsCOMPtr *outFragNode, - PRInt32 *outRangeStartHint, - PRInt32 *outRangeEndHint) -{ - if (!outFragNode || !outRangeStartHint || !outRangeEndHint) - return NS_ERROR_NULL_POINTER; - nsCOMPtr docfrag; - nsresult res = nsrange->CreateContextualFragment(aInputString, getter_AddRefs(docfrag)); - NS_ENSURE_SUCCESS(res, res); - *outFragNode = do_QueryInterface(docfrag); - - res = StripFormattingNodes(*outFragNode); - NS_ENSURE_SUCCESS(res, res); - - // if we have context info, create a fragment for that too - nsCOMPtr contextfrag; - nsCOMPtr contextLeaf; - PRInt32 contextDepth = 0; - if (aContextStr.Length()) - { - res = nsrange->CreateContextualFragment(aContextStr, getter_AddRefs(contextfrag)); - NS_ENSURE_SUCCESS(res, res); - nsCOMPtr contextAsNode (do_QueryInterface(contextfrag)); - res = StripFormattingNodes(contextAsNode); - NS_ENSURE_SUCCESS(res, res); - // cache the deepest leaf in the context - nsCOMPtr junk, child, tmp = contextAsNode; - while (tmp) - { - contextDepth++; - contextLeaf = tmp; - contextLeaf->GetFirstChild(getter_AddRefs(tmp)); - } - // unite the two trees - contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk)); - *outFragNode = contextAsNode; - // no longer have fragmentAsNode in tree - contextDepth--; - } - - // get the infoString contents - nsAutoString numstr1, numstr2; - if (aInfoStr.Length()) - { - PRInt32 err, sep; - sep = aInfoStr.FindChar((PRUnichar)','); - aInfoStr.Left(numstr1, sep); - aInfoStr.Mid(numstr2, sep+1, -1); - *outRangeStartHint = numstr1.ToInteger(&err) + contextDepth; - *outRangeEndHint = numstr2.ToInteger(&err) + contextDepth; - } - else - { - *outRangeStartHint = contextDepth; - *outRangeEndHint = contextDepth; - } - return res; -} - -nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, - nsCOMPtr *outNodeList, - PRInt32 aRangeStartHint, - PRInt32 aRangeEndHint) -{ - if (!outNodeList || !aFragmentAsNode) - return NS_ERROR_NULL_POINTER; - - // First off create a range over the portion of docFrag indicated by - // the range hints. - nsCOMPtr docFragRange; - docFragRange = do_CreateInstance(kCRangeCID); - nsCOMPtr startParent, endParent, tmp; - PRInt32 endOffset; - startParent = aFragmentAsNode; - while (aRangeStartHint > 0) - { - startParent->GetFirstChild(getter_AddRefs(tmp)); - startParent = tmp; - aRangeStartHint--; - NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); - } - endParent = aFragmentAsNode; - while (aRangeEndHint > 0) - { - endParent->GetLastChild(getter_AddRefs(tmp)); - endParent = tmp; - aRangeEndHint--; - NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE); - } - nsresult res = GetLengthOfDOMNode(endParent, (PRUint32&)endOffset); - NS_ENSURE_SUCCESS(res, res); - - res = docFragRange->SetStart(startParent, 0); - NS_ENSURE_SUCCESS(res, res); - res = docFragRange->SetEnd(endParent, endOffset); - NS_ENSURE_SUCCESS(res, res); - - // now use a subtree iterator over the range to create a list of nodes - nsTrivialFunctor functor; - nsDOMSubtreeIterator iter; - res = NS_NewISupportsArray(getter_AddRefs(*outNodeList)); - NS_ENSURE_SUCCESS(res, res); - res = iter.Init(docFragRange); - NS_ENSURE_SUCCESS(res, res); - res = iter.AppendList(functor, *outNodeList); - - return res; -} - - -nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsString& aInputString, - const nsString& aCharset, - const nsString& aContextStr, - const nsString& aInfoStr) -{ - if (!mRules) return NS_ERROR_NOT_INITIALIZED; - - // First, make sure there are no return chars in the document. - // Bad things happen if you insert returns (instead of dom newlines, \n) - // into an editor document. - nsAutoString inputString (aInputString); // hope this does copy-on-write - - // Windows linebreaks: Map CRLF to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r\n"), - NS_ConvertASCIItoUCS2("\n")); - - // Mac linebreaks: Map any remaining CR to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r"), - NS_ConvertASCIItoUCS2("\n")); - - // force IME commit; set up rules sniffing and batching - ForceCompositionEnd(); - nsAutoEditBatch beginBatching(this); - nsAutoRules beginRulesSniffing(this, kOpHTMLPaste, nsIEditor::eNext); - - // Get selection - nsresult res; - nsCOMPtrselection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - - // Get the first range in the selection, for context: - nsCOMPtr range; - res = selection->GetRangeAt(0, getter_AddRefs(range)); - if (NS_FAILED(res)) - return res; - - nsCOMPtr nsrange (do_QueryInterface(range)); - if (!nsrange) - return NS_ERROR_NO_INTERFACE; - - // create a dom document fragment that represents the structure to paste - nsCOMPtr fragmentAsNode; - PRInt32 rangeStartHint, rangeEndHint; - res = CreateDOMFragmentFromPaste(nsrange, inputString, aContextStr, aInfoStr, - address_of(fragmentAsNode), - &rangeStartHint, &rangeEndHint); - NS_ENSURE_SUCCESS(res, res); - - - // make a list of what nodes in docFrag we need to move - nsCOMPtr nodeList; - res = CreateListOfNodesToPaste(fragmentAsNode, address_of(nodeList), rangeStartHint, rangeEndHint); - NS_ENSURE_SUCCESS(res, res); - - // are there any table elements in the list? - // node and offset for insertion - nsCOMPtr parentNode; - PRInt32 offsetOfNewNode; - - // check for table cell selection mode - PRBool cellSelectionMode = PR_FALSE; - nsCOMPtr cell; - res = GetFirstSelectedCell(getter_AddRefs(cell), nsnull); - if (NS_SUCCEEDED(res) && cell) - { - cellSelectionMode = PR_TRUE; - } - - if (cellSelectionMode) - { - // do we have table content to paste? If so, we want to delete - // the selected table cells and replace with new table elements; - // but if not we want to delete _contents_ of cells and replace - // with non-table elements. Use cellSelectionMode bool to - // indicate results. - nsCOMPtr isupports = nodeList->ElementAt(0); - nsCOMPtr firstNode( do_QueryInterface(isupports) ); - if (!nsHTMLEditUtils::IsTableElement(firstNode)) - cellSelectionMode = PR_FALSE; - } - - if (!cellSelectionMode) - { - res = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode); - NS_ENSURE_SUCCESS(res, res); - - // pasting does not inherit local inline styles - res = RemoveAllInlineProperties(); - NS_ENSURE_SUCCESS(res, res); - } - else - { - // delete whole cells: we will replace with new table content - if (1) - { - // Save current selection since DeleteTableCell perturbs it - nsAutoSelectionReset selectionResetter(selection, this); - res = DeleteTableCell(1); - NS_ENSURE_SUCCESS(res, res); - } - // colapse selection to beginning of deleted table content - selection->CollapseToStart(); - } - - // give rules a chance to handle or cancel - nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement); - PRBool cancel, handled; - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (cancel) return NS_OK; // rules canceled the operation - if (!handled) - { - // The rules code (WillDoAction above) might have changed the selection. - // refresh our memory... - res = GetStartNodeAndOffset(selection, address_of(parentNode), &offsetOfNewNode); - if (!parentNode) res = NS_ERROR_FAILURE; - if (NS_FAILED(res)) return res; - - // are we in a text node? If so, split it. - if (IsTextNode(parentNode)) - { - nsCOMPtr temp; - res = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode); - if (NS_FAILED(res)) return res; - res = parentNode->GetParentNode(getter_AddRefs(temp)); - if (NS_FAILED(res)) return res; - parentNode = temp; - } - - // build up list of parents of first node in lst that are either: - // lists, or tables. - nsCOMPtr isup = nodeList->ElementAt(0); - nsCOMPtr pNode( do_QueryInterface(isup) ); - nsCOMPtr listAndTableArray; - res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); - NS_ENSURE_SUCCESS(res, res); - while (pNode) - { - if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) - { - isup = do_QueryInterface(pNode); - listAndTableArray->AppendElement(isup); - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - - // remember number of lists and tables above us - PRUint32 listAndTableParents; - PRInt32 highWaterMark = -1; - listAndTableArray->Count(&listAndTableParents); - - PRUint32 listCount, j; - if (listAndTableParents) - { - // scan insertion list for table elements (other than table). - nodeList->Count(&listCount); - for (j=0; j isupports = nodeList->ElementAt(j); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); - if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) - { - nsCOMPtr theTable = GetTableParent(curNode); - if (theTable) - { - nsCOMPtr isupTable(do_QueryInterface(theTable)); - PRInt32 indexT = listAndTableArray->IndexOf(isupTable); - if (indexT >= 0) - { - highWaterMark = indexT; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - if (nsHTMLEditUtils::IsListItem(curNode)) - { - nsCOMPtr theList = GetListParent(curNode); - if (theList) - { - nsCOMPtr isupList(do_QueryInterface(theList)); - PRInt32 indexL = listAndTableArray->IndexOf(isupList); - if (indexL >= 0) - { - highWaterMark = indexL; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - } - } - // if we have pieces of tables or lists to be inserted, let's force the paste - // to deal with table elements right away, so that it doesn't orphan some - // table or list contents outside the table or list. - if (highWaterMark >= 0) - { - nsCOMPtr isupports = listAndTableArray->ElementAt(highWaterMark); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - nsCOMPtr replaceNode, tmp; - if (nsHTMLEditUtils::IsTable(curNode)) - { - // look upward from curNode for a piece of this table - isup = nodeList->ElementAt(0); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode)) - { - nsCOMPtr tableP = GetTableParent(pNode); - if (tableP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - else // list case - { - // look upward from curNode for a piece of this list - isup = nodeList->ElementAt(0); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsListItem(pNode)) - { - nsCOMPtr listP = GetListParent(pNode); - if (listP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - - if (replaceNode) - { - isupports = do_QueryInterface(replaceNode); - nodeList->ReplaceElementAt(isupports, 0); - // postprocess list to remove any descendants of this node - // so that we dont insert them twice. - do - { - isupports = nodeList->ElementAt(1); - tmp = do_QueryInterface(isupports); - if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) - nodeList->RemoveElementAt(1); - else - break; - } while(tmp); - } - } - - // Loop over the node list and paste the nodes: - PRBool bDidInsert = PR_FALSE; - nsCOMPtr lastInsertNode, insertedContextParent; - nodeList->Count(&listCount); - for (j=0; j isupports = nodeList->ElementAt(j); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); - NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE); - NS_ENSURE_TRUE(!nsHTMLEditUtils::IsBody(curNode), NS_ERROR_FAILURE); - - if (insertedContextParent) - { - // if we had to insert something higher up in the paste heirarchy, we want to - // skip any further paste nodes that descend from that. Else we will paste twice. - if (nsHTMLEditUtils::IsDescendantOf(curNode, insertedContextParent)) - continue; - } - - // give the user a hand on table element insertion. if they have - // a table or table row on the clipboard, and are trying to insert - // into a table or table row, insert the appropriate children instead. - if ( (nsHTMLEditUtils::IsTableRow(curNode) && nsHTMLEditUtils::IsTableRow(parentNode)) - && (nsHTMLEditUtils::IsTable(curNode) || nsHTMLEditUtils::IsTable(parentNode)) ) - { - nsCOMPtr child; - curNode->GetFirstChild(getter_AddRefs(child)); - while (child) - { - InsertNodeAtPoint(child, parentNode, offsetOfNewNode, PR_TRUE); - if (NS_SUCCEEDED(res)) - { - bDidInsert = PR_TRUE; - lastInsertNode = curNode; - offsetOfNewNode++; - } - curNode->GetFirstChild(getter_AddRefs(child)); - } - } - else - { - // try to insert - res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); - if (NS_SUCCEEDED(res)) - { - bDidInsert = PR_TRUE; - lastInsertNode = curNode; - } - - // assume failure means no legal parent in the document heirarchy. - // try again with the parent of curNode in the paste heirarchy. - nsCOMPtr parent; - while (NS_FAILED(res) && curNode) - { - curNode->GetParentNode(getter_AddRefs(parent)); - if (parent && !nsHTMLEditUtils::IsBody(parent)) - { - res = InsertNodeAtPoint(parent, parentNode, offsetOfNewNode, PR_TRUE); - if (NS_SUCCEEDED(res)) - { - bDidInsert = PR_TRUE; - insertedContextParent = parent; - lastInsertNode = parent; - } - } - curNode = parent; - } - } - if (bDidInsert) - { - res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode); - NS_ENSURE_SUCCESS(res, res); - offsetOfNewNode++; - } - } - - // Now collapse the selection to the end of what we just inserted: - if (lastInsertNode) - { - res = GetNodeLocation(lastInsertNode, address_of(parentNode), &offsetOfNewNode); - NS_ENSURE_SUCCESS(res, res); - selection->Collapse(parentNode, offsetOfNewNode+1); - } - } - - res = mRules->DidDoAction(selection, &ruleInfo, res); - return res; -} - -nsresult -nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode) -{ - NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); - - nsresult res = NS_OK; - nsCOMPtr content = do_QueryInterface(aNode); - if (IsEmptyTextContent(content)) - { - nsCOMPtr parent, ignored; - aNode->GetParentNode(getter_AddRefs(parent)); - if (parent) - { - res = parent->RemoveChild(aNode, getter_AddRefs(ignored)); - return res; - } - } - - if (!nsHTMLEditUtils::IsPre(aNode)) - { - nsCOMPtr child; - aNode->GetLastChild(getter_AddRefs(child)); - - while (child) - { - nsCOMPtr tmp; - child->GetPreviousSibling(getter_AddRefs(tmp)); - res = StripFormattingNodes(child); - NS_ENSURE_SUCCESS(res, res); - child = tmp; - } - } - return res; -} // This is mostly like InsertHTMLWithCharset, // but we can't use that because it is selection-based and @@ -3177,92 +1047,6 @@ nsHTMLEditor::RebuildDocumentFromSource(const nsString& aSourceString) return CloneAttributes(bodyElement, child); } -NS_IMETHODIMP nsHTMLEditor::InsertLineBreak() -{ - nsresult res; - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsAutoEditBatch beginBatching(this); - nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext); - nsCOMPtr selection; - PRBool cancel, handled; - - // pre-process - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - if (!selection) return NS_ERROR_NULL_POINTER; - - nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertBreak); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(res)) return res; - if (!cancel && !handled) - { - // create the new BR node - nsCOMPtr newNode; - nsAutoString tag; tag.AssignWithConversion("BR"); - res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (!newNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called - if (NS_SUCCEEDED(res)) - { - // set the selection to the new node - nsCOMPtrparent; - res = newNode->GetParentNode(getter_AddRefs(parent)); - if (!parent) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called - if (NS_SUCCEEDED(res)) - { - PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not - nsCOMPtrnextNode; - newNode->GetNextSibling(getter_AddRefs(nextNode)); - if (nextNode) - { - nsCOMPtrnextTextNode; - nextTextNode = do_QueryInterface(nextNode); - if (!nextTextNode) { - nextNode = do_QueryInterface(newNode); - } - else { - offsetInParent=0; - } - } - else { - nextNode = do_QueryInterface(newNode); - } - res = GetSelection(getter_AddRefs(selection)); - if (!selection) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called - if (NS_SUCCEEDED(res)) - { - nsCOMPtr selPriv(do_QueryInterface(selection)); - if (-1==offsetInParent) - { - nextNode->GetParentNode(getter_AddRefs(parent)); - res = GetChildOffset(nextNode, parent, offsetInParent); - if (NS_SUCCEEDED(res)) { - // SetInterlinePosition(PR_TRUE) means we want the caret to stick to the content on the "right". - // We want the caret to stick to whatever is past the break. This is - // because the break is on the same line we were on, but the next content - // will be on the following line. - selPriv->SetInterlinePosition(PR_TRUE); - res = selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break - } - } - else - { - res = selection->Collapse(nextNode, offsetInParent); - } - } - } - } - } - if (!cancel) - { - // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE - res = mRules->DidDoAction(selection, &ruleInfo, res); - } - - return res; -} - - NS_IMETHODIMP nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection) { @@ -3307,7 +1091,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe // Named Anchor is a special case, // We collapse to insert element BEFORE the selection // For all other tags, we insert AFTER the selection - if (IsNamedAnchorNode(node)) + if (nsHTMLEditUtils::IsNamedAnchor(node)) { selection->CollapseToStart(); } else { @@ -3387,35 +1171,6 @@ nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, return res; } -// XXX: error handling in this routine needs to be cleaned up! -NS_IMETHODIMP -nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag, - nsIDOMNode ** aNewNode) -{ - nsCOMPtr parentSelectedNode; - PRInt32 offsetOfNewNode; - nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, - offsetOfNewNode); - if (!NS_SUCCEEDED(result)) - return result; - - nsCOMPtr newNode; - result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, - getter_AddRefs(newNode)); - // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases - *aNewNode = newNode; - NS_IF_ADDREF(*aNewNode); - - // we want the selection to be just after the new node - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1); - - return result; -} - NS_IMETHODIMP nsHTMLEditor::SelectElement(nsIDOMElement* aElement) { @@ -3626,71 +1381,6 @@ nsHTMLEditor::GetParagraphState(PRBool &aMixed, nsString &outFormat) return htmlRules->GetParagraphState(aMixed, outFormat); } -NS_IMETHODIMP -nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace) -{ - aMixed = PR_TRUE; - outFace.AssignWithConversion(""); - - nsresult res; - nsAutoString faceStr; faceStr.AssignWithConversion("face"); - PRBool first, any, all; - - res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace); - if (NS_FAILED(res)) return res; - if (any && !all) return res; // mixed - if (all) - { - aMixed = PR_FALSE; - return res; - } - - res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all); - if (NS_FAILED(res)) return res; - if (any && !all) return res; // mixed - if (all) - { - aMixed = PR_FALSE; - nsIEditProperty::tt->ToString(outFace); - } - - if (!any) - { - // there was no font face attrs of any kind. We are in normal font. - outFace.AssignWithConversion(""); - aMixed = PR_FALSE; - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditor::GetFontColorState(PRBool &aMixed, nsString &aOutColor) -{ - aMixed = PR_TRUE; - aOutColor.AssignWithConversion(""); - - nsresult res; - nsAutoString colorStr; colorStr.AssignWithConversion("color"); - PRBool first, any, all; - - res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &colorStr, nsnull, first, any, all, &aOutColor); - if (NS_FAILED(res)) return res; - if (any && !all) return res; // mixed - if (all) - { - aMixed = PR_FALSE; - return res; - } - - if (!any) - { - // there was no font color attrs of any kind.. - aOutColor.AssignWithConversion(""); - aMixed = PR_FALSE; - } - return res; -} - NS_IMETHODIMP nsHTMLEditor::GetBackgroundColorState(PRBool &aMixed, nsString &aOutColor) { @@ -4142,8 +1832,8 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * nsAutoString TagName(aTagName); TagName.ToLowerCase(); - PRBool getLink = IsLink(TagName); - PRBool getNamedAnchor = IsNamedAnchor(TagName); + PRBool getLink = IsLinkTag(TagName); + PRBool getNamedAnchor = IsNamedAnchorTag(TagName); if ( getLink || getNamedAnchor) { TagName.AssignWithConversion("a"); @@ -4161,8 +1851,8 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * { nsAutoString currentTagName; // Test if we have a link (an anchor with href set) - if ( (getLink && IsLinkNode(currentNode)) || - (getNamedAnchor && IsNamedAnchorNode(currentNode)) ) + if ( (getLink && nsHTMLEditUtils::IsLink(currentNode)) || + (getNamedAnchor && nsHTMLEditUtils::IsNamedAnchor(currentNode)) ) { bNodeFound = PR_TRUE; break; @@ -4170,14 +1860,14 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * if (findList) { // Match "ol", "ul", or "dl" for lists - if (IsListNode(currentNode)) + if (nsHTMLEditUtils::IsList(currentNode)) goto NODE_FOUND; } else if (findTableCell) { // Table cells are another special case: // Match either "td" or "th" for them - if (IsCellNode(currentNode)) + if (nsHTMLEditUtils::IsTableCell(currentNode)) goto NODE_FOUND; } else { @@ -4249,8 +1939,8 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu TagName.ToLowerCase(); // Empty string indicates we should match any element tag PRBool anyTag = (TagName.IsEmpty()); - PRBool isLinkTag = IsLink(TagName); - PRBool isNamedAnchorTag = IsNamedAnchor(TagName); + PRBool isLinkTag = IsLinkTag(TagName); + PRBool isNamedAnchorTag = IsNamedAnchorTag(TagName); nsCOMPtr selectedElement; nsCOMPtr range; @@ -4282,8 +1972,8 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu // Test for appropriate node type requested if (anyTag || (TagName == domTagName) || - (isLinkTag && IsLinkNode(selectedNode)) || - (isNamedAnchorTag && IsNamedAnchorNode(selectedNode))) + (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) || + (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode))) { bNodeFound = PR_TRUE; selectedElement = do_QueryInterface(selectedNode); @@ -4357,7 +2047,7 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu { nsCOMPtr anchorChild; anchorChild = GetChildAt(anchorNode,anchorOffset); - if (anchorChild && IsLinkNode(anchorChild) && + if (anchorChild && nsHTMLEditUtils::IsLink(anchorChild) && (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) { selectedElement = do_QueryInterface(anchorChild); @@ -4427,8 +2117,8 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu // The "A" tag is a pain, // used for both link(href is set) and "Named Anchor" nsCOMPtr selectedNode = do_QueryInterface(selectedElement); - if ( (isLinkTag && IsLinkNode(selectedNode)) || - (isNamedAnchorTag && IsNamedAnchorNode(selectedNode)) ) + if ( (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) || + (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode)) ) { bNodeFound = PR_TRUE; } else if (TagName == domTagName) { // All other tag names are handled here @@ -4482,7 +2172,7 @@ nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement* TagName.ToLowerCase(); nsAutoString realTagName; - if (IsLink(TagName) || IsNamedAnchor(TagName)) + if (IsLinkTag(TagName) || IsNamedAnchorTag(TagName)) { realTagName.AssignWithConversion("a"); } else { @@ -4657,86 +2347,6 @@ NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const n return res; } -NS_IMETHODIMP -nsHTMLEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty) -{ - if (!aDocumentIsEmpty) - return NS_ERROR_NULL_POINTER; - - if (!mRules) - return NS_ERROR_NOT_INITIALIZED; - - return mRules->DocumentIsEmpty(aDocumentIsEmpty); -} - -NS_IMETHODIMP -nsHTMLEditor::GetTextLength(PRInt32 *aCount) -{ - if (!aCount) { return NS_ERROR_NULL_POINTER; } - nsresult result; - // initialize out params - *aCount = 0; - - // special-case for empty document, to account for the bogus text node - PRBool docEmpty; - result = GetDocumentIsEmpty(&docEmpty); - if (NS_FAILED(result)) return result; - if (docEmpty) - { - *aCount = 0; - return NS_OK; - } - - // get the body node - nsCOMPtr bodyElement; - result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(result)) { return result; } - if (!bodyElement) { return NS_ERROR_NULL_POINTER; } - - // get the offsets of the first and last children of the body node - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); - if (!bodyNode) { return NS_ERROR_NULL_POINTER; } - PRInt32 numBodyChildren=0; - nsCOMPtrlastChild; - result = bodyNode->GetLastChild(getter_AddRefs(lastChild)); - if (NS_FAILED(result)) { return result; } - if (!lastChild) { return NS_ERROR_NULL_POINTER; } - result = GetChildOffset(lastChild, bodyNode, numBodyChildren); - if (NS_FAILED(result)) { return result; } - - // count - PRInt32 start, end; - result = GetAbsoluteOffsetsForPoints(bodyNode, 0, - bodyNode, numBodyChildren, - bodyNode, start, end); - if (NS_SUCCEEDED(result)) - { - NS_ASSERTION(0==start, "GetAbsoluteOffsetsForPoints failed to set start correctly."); - NS_ASSERTION(0<=end, "GetAbsoluteOffsetsForPoints failed to set end correctly."); - if (0<=end) { - *aCount = end; - } - } - return result; -} - -NS_IMETHODIMP -nsHTMLEditor::SetMaxTextLength(PRInt32 aMaxTextLength) -{ - mMaxTextLength = aMaxTextLength; - return NS_OK; -} - -NS_IMETHODIMP -nsHTMLEditor::GetMaxTextLength(PRInt32* aMaxTextLength) -{ - if (!aMaxTextLength) - return NS_ERROR_INVALID_POINTER; - *aMaxTextLength = mMaxTextLength; - return NS_OK; -} - - #ifdef XP_MAC #pragma mark - #pragma mark nsIEditorStyleSheets methods @@ -4913,167 +2523,6 @@ nsHTMLEditor::ApplyDocumentOrOverrideStyleSheet(const nsString& aURL, PRBool aOv #pragma mark - #endif -NS_IMETHODIMP -nsHTMLEditor::GetBodyStyleContext(nsIStyleContext** aStyleContext) -{ - nsCOMPtr body; - nsresult res = GetRootElement(getter_AddRefs(body)); - if (NS_FAILED(res)) return res; - nsCOMPtr content = do_QueryInterface(body); - - nsIFrame *frame; - if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; - res = ps->GetPrimaryFrameFor(content, &frame); - if (NS_FAILED(res)) return res; - - return ps->GetStyleContextFor(frame, aStyleContext); -} - -// -// Get an appropriate wrap width for saving this document. -// -NS_IMETHODIMP -nsHTMLEditor::GetWrapWidth(PRInt32 *aWrapColumn) -{ - nsresult res; - - if (! aWrapColumn) - return NS_ERROR_NULL_POINTER; - - *aWrapColumn = -1; // default: no wrap - - // Don't wrap output of text fields - if (mFlags & eEditorSingleLineMask) - return NS_OK; - - if (mFlags & eEditorPlaintextMask) - { - nsCOMPtr styleContext; - res = GetBodyStyleContext(getter_AddRefs(styleContext)); - if (NS_SUCCEEDED(res) && styleContext) - { - const nsStyleText* styleText = - (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text); - - if (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) - { - *aWrapColumn = 0; // wrap to window width - return NS_OK; - } - else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace) - { - const nsStylePosition* stylePosition = - (const nsStylePosition*)styleContext->GetStyleData(eStyleStruct_Position); - if (stylePosition->mWidth.GetUnit() == eStyleUnit_Chars) - { - *aWrapColumn = stylePosition->mWidth.GetIntValue(); - return NS_OK; - } -#ifdef DEBUG_akkana - else - printf("Can't get wrap column: style unit is %d\n", - stylePosition->mWidth.GetUnit()); -#endif - } - } - } - - // If we get here, we weren't able to get the wrap column from style, - // or we aren't plaintext so we don't expect to. - // So fall back on nsEditor (which uses a pref). - return nsEditor::GetWrapWidth(aWrapColumn); -} - -// -// See if the style value includes this attribute, and if it does, -// cut out everything from the attribute to the next semicolon. -// -static void CutStyle(const char* stylename, nsString& styleValue) -{ - // Find the current wrapping type: - PRInt32 styleStart = styleValue.Find(stylename, PR_TRUE); - if (styleStart >= 0) - { - PRInt32 styleEnd = styleValue.Find(";", PR_FALSE, styleStart); - if (styleEnd > styleStart) - styleValue.Cut(styleStart, styleEnd - styleStart + 1); - else - styleValue.Cut(styleStart, styleValue.Length() - styleStart); - } -} - -// -// Change the wrap width on the first
 tag in this document.
-// (Eventually want to search for more than one in case there are
-// interspersed quoted text blocks.)
-// Alternately: Change the wrap width on the editor style sheet.
-// 
-NS_IMETHODIMP nsHTMLEditor::SetWrapWidth(PRInt32 aWrapColumn)
-{
-  nsresult res;
-
-  // Ought to set a style sheet here ...
-  // Probably should keep around an mPlaintextStyleSheet for this purpose.
-  nsCOMPtr bodyElement;
-  res = GetRootElement(getter_AddRefs(bodyElement));
-  if (NS_FAILED(res)) return res;
-  if (!bodyElement) return NS_ERROR_NULL_POINTER;
-
-  // Get the current style for this body element:
-  nsAutoString styleName; styleName.AssignWithConversion("style");
-  nsAutoString styleValue;
-  res = bodyElement->GetAttribute(styleName, styleValue);
-  if (NS_FAILED(res)) return res;
-
-  // We'll replace styles for these values:
-  CutStyle("white-space", styleValue);
-  CutStyle("width", styleValue);
-  CutStyle("font-family", styleValue);
-
-  // If we have other style left, trim off any existing semicolons
-  // or whitespace, then add a known semicolon-space:
-  if (styleValue.Length() > 0)
-  {
-    styleValue.Trim("; \t", PR_FALSE, PR_TRUE);
-    styleValue.AppendWithConversion("; ");
-  }
-
-  // Make sure we have fixed-width font.  This should be done for us,
-  // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;".
-  // Only do this if we're wrapping.
-  PRUint32 flags = 0;
-  GetFlags(&flags);
-  if ((flags & eEditorEnableWrapHackMask) && aWrapColumn >= 0)
-    styleValue.AppendWithConversion("font-family: -moz-fixed; ");
-
-  // and now we're ready to set the new whitespace/wrapping style.
-  if (aWrapColumn > 0)        // Wrap to a fixed column
-  {
-    styleValue.AppendWithConversion("white-space: -moz-pre-wrap; width: ");
-    styleValue.AppendInt(aWrapColumn);
-    styleValue.AppendWithConversion("ch;");
-  }
-  else if (aWrapColumn == 0)
-    styleValue.AppendWithConversion("white-space: -moz-pre-wrap;");
-  else
-    styleValue.AppendWithConversion("white-space: pre;");
-
-  res = bodyElement->SetAttribute(styleName, styleValue);
-
-#ifdef DEBUG_wrapstyle
-  char* curstyle = styleValue.ToNewCString();
-  printf("Setting style: [%s]\nNow body looks like:\n", curstyle);
-  Recycle(curstyle);
-  //nsCOMPtr nodec (do_QueryInterface(bodyElement));
-  //if (nodec) nodec->List(stdout);
-  //printf("-----\n");
-#endif /* DEBUG_akkana */
-
-  return res;
-}
-
 NS_IMETHODIMP
 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
 {
@@ -5257,1075 +2706,6 @@ NS_IMETHODIMP nsHTMLEditor::CanCopy(PRBool &aCanCopy)
   return NS_OK;
 }
 
-NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable)
-{
-  // Create generic Transferable for getting the data
-  nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
-                                          NS_GET_IID(nsITransferable), 
-                                          (void**)transferable);
-  if (NS_FAILED(rv))
-    return rv;
-
-  // Get the nsITransferable interface for getting the data from the clipboard
-  if (transferable)
-  {
-    // Create the desired DataFlavor for the type of data
-    // we want to get out of the transferable
-    if ((mFlags & eEditorPlaintextMask) == 0)  // This should only happen in html editors, not plaintext
-    {
-      (*transferable)->AddDataFlavor(kJPEGImageMime);
-      (*transferable)->AddDataFlavor(kHTMLMime);
-      (*transferable)->AddDataFlavor(kFileMime);
-    }
-    (*transferable)->AddDataFlavor(kUnicodeMime);
-  }
-  
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, 
-                                                   const nsString& aContextStr,
-                                                   const nsString& aInfoStr)
-{
-  nsresult rv = NS_OK;
-  char* bestFlavor = nsnull;
-  nsCOMPtr genericDataObj;
-  PRUint32 len = 0;
-  if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
-  {
-    nsAutoTxnsConserveSelection dontSpazMySelection(this);
-    nsAutoString flavor, stuffToPaste;
-    flavor.AssignWithConversion( bestFlavor );   // just so we can use flavor.Equals()
-#ifdef DEBUG_akkana
-    printf("Got flavor [%s]\n", bestFlavor);
-#endif
-    if (flavor.EqualsWithConversion(kHTMLMime))
-    {
-      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
-      if (textDataObj && len > 0)
-      {
-        PRUnichar* text = nsnull;
-
-        textDataObj->ToString ( &text );
-        stuffToPaste.Assign ( text, len / 2 );
-        nsAutoEditBatch beginBatching(this);
-        rv = InsertHTMLWithContext(stuffToPaste, aContextStr, aInfoStr);
-        if (text)
-          nsMemory::Free(text);
-      }
-    }
-    else if (flavor.EqualsWithConversion(kUnicodeMime))
-    {
-      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
-      if (textDataObj && len > 0)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj->ToString ( &text );
-        stuffToPaste.Assign ( text, len / 2 );
-        nsAutoEditBatch beginBatching(this);
-        // pasting does not inherit local inline styles
-        RemoveAllInlineProperties();
-        rv = InsertText(stuffToPaste.GetUnicode());
-        if (text)
-          nsMemory::Free(text);
-      }
-    }
-    else if (flavor.EqualsWithConversion(kFileMime))
-    {
-      nsCOMPtr fileObj ( do_QueryInterface(genericDataObj) );
-      if (fileObj && len > 0)
-      {
-        nsCOMPtr fileURL;
-        rv = nsComponentManager::CreateInstance("@mozilla.org/network/standard-url;1", nsnull, 
-                                     NS_GET_IID(nsIURL), getter_AddRefs(fileURL));
-        if (NS_FAILED(rv))
-          return rv;
-        
-        if ( fileURL )
-        {
-          rv = fileURL->SetFile( fileObj );
-          if (NS_FAILED(rv))
-            return rv;
-          
-          PRBool insertAsImage = PR_FALSE;
-          char *fileextension = nsnull;
-          rv = fileURL->GetFileExtension( &fileextension );
-          if ( NS_SUCCEEDED(rv) && fileextension )
-          {
-            if ( (nsCRT::strcasecmp( fileextension, "jpg" ) == 0 )
-              || (nsCRT::strcasecmp( fileextension, "jpeg" ) == 0 )
-              || (nsCRT::strcasecmp( fileextension, "gif" ) == 0 )
-              || (nsCRT::strcasecmp( fileextension, "png" ) == 0 ) )
-            {
-              insertAsImage = PR_TRUE;
-            }
-          }
-          if (fileextension) nsCRT::free(fileextension);
-          
-          char *urltext = nsnull;
-          rv = fileURL->GetSpec( &urltext );
-          if ( NS_SUCCEEDED(rv) && urltext && urltext[0] != 0)
-          {
-            len = strlen(urltext);
-            if ( insertAsImage )
-            {
-              stuffToPaste.AssignWithConversion ( "" );
-            }
-            else /* insert as link */
-            {
-              stuffToPaste.AssignWithConversion ( "" );
-              stuffToPaste.AppendWithConversion ( urltext, len );
-              stuffToPaste.AppendWithConversion ( "" );
-            }
-            nsAutoEditBatch beginBatching(this);
-            rv = InsertHTML(stuffToPaste);
-          }
-          if (urltext) nsCRT::free(urltext);
-        }
-      }
-    }
-    else if (flavor.EqualsWithConversion(kJPEGImageMime))
-    {
-      // Insert Image code here
-      printf("Don't know how to insert an image yet!\n");
-      //nsIImage* image = (nsIImage *)data;
-      //NS_RELEASE(image);
-      rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code
-    }
-  }
-  nsCRT::free(bestFlavor);
-      
-  // Try to scroll the selection into view if the paste/drop succeeded
-  if (NS_SUCCEEDED(rv))
-  {
-    nsCOMPtr selCon;
-    if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
-      selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
-{
-  ForceCompositionEnd();
-  
-  nsresult rv;
-  NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  nsCOMPtr dragSession(do_QueryInterface(dragService));
-  
-  if (!dragSession) return NS_OK;
-
-  // Get the nsITransferable interface for getting the data from the drop
-  nsCOMPtr trans;
-  rv = PrepareTransferable(getter_AddRefs(trans));
-  if (NS_FAILED(rv)) return rv;
-  if (!trans) return NS_OK;  // NS_ERROR_FAILURE; SHOULD WE FAIL?
-
-  PRUint32 numItems = 0; 
-  rv = dragSession->GetNumDropItems(&numItems);
-  if (NS_FAILED(rv)) return rv;
-
-  // Combine any deletion and drop insertion into one transaction
-  nsAutoEditBatch beginBatching(this);
-
-  PRUint32 i; 
-  PRBool doPlaceCaret = PR_TRUE;
-  for (i = 0; i < numItems; ++i)
-  {
-    rv = dragSession->GetData(trans, i);
-    if (NS_FAILED(rv)) return rv;
-    if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
-
-    if ( doPlaceCaret )
-    {
-      // check if the user pressed the key to force a copy rather than a move
-      // if we run into problems here, we'll just assume the user doesn't want a copy
-      PRBool userWantsCopy = PR_FALSE;
-
-      nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent));
-      if (!nsuiEvent) return NS_ERROR_FAILURE;
-
-      nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) );
-      if (mouseEvent)
-
-#ifdef XP_MAC
-        mouseEvent->GetAltKey(&userWantsCopy);
-#else
-        mouseEvent->GetCtrlKey(&userWantsCopy);
-#endif
-      // Source doc is null if source is *not* the current editor document
-      nsCOMPtr srcdomdoc;
-      rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
-      if (NS_FAILED(rv)) return rv;
-
-      // Current doc is destination
-      nsCOMPtrdestdomdoc; 
-      rv = GetDocument(getter_AddRefs(destdomdoc)); 
-      if (NS_FAILED(rv)) return rv;
-
-      nsCOMPtr selection;
-      rv = GetSelection(getter_AddRefs(selection));
-      if (NS_FAILED(rv)) return rv;
-      if (!selection) return NS_ERROR_FAILURE;
-
-      PRBool isCollapsed;
-      rv = selection->GetIsCollapsed(&isCollapsed);
-      if (NS_FAILED(rv)) return rv;
-      
-      // Parent and offset under the mouse cursor
-      nsCOMPtr newSelectionParent;
-      PRInt32 newSelectionOffset = 0;
-      rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
-      if (NS_FAILED(rv)) return rv;
-      if (!newSelectionParent) return NS_ERROR_FAILURE;
-
-      rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
-      if (NS_FAILED(rv)) return rv;
-      /* Creating a range to store insert position because when
-         we delete the selection, range gravity will make sure the insertion
-         point is in the correct place */
-      nsCOMPtr destinationRange;
-      rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange));
-      if (NS_FAILED(rv))
-        return rv;
-      if(!destinationRange)
-        return NS_ERROR_FAILURE;
-
-      // We never have to delete if selection is already collapsed
-      PRBool deleteSelection = PR_FALSE;
-      PRBool cursorIsInSelection = PR_FALSE;
-
-      // Check if mouse is in the selection
-      if (!isCollapsed)
-      {
-        PRInt32 rangeCount;
-        rv = selection->GetRangeCount(&rangeCount);
-        if (NS_FAILED(rv)) 
-          return rv?rv:NS_ERROR_FAILURE;
-
-        for (PRInt32 j = 0; j < rangeCount; j++)
-        {
-          nsCOMPtr range;
-
-          rv = selection->GetRangeAt(j, getter_AddRefs(range));
-          if (NS_FAILED(rv) || !range) 
-            continue;//dont bail yet, iterate through them all
-
-          nsCOMPtr nsrange(do_QueryInterface(range));
-          if (NS_FAILED(rv) || !nsrange) 
-            continue;//dont bail yet, iterate through them all
-
-          rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
-          if(cursorIsInSelection)
-            break;
-        }
-        if (cursorIsInSelection)
-        {
-          // Dragging within same doc can't drop on itself -- leave!
-          // (We shouldn't get here - drag event shouldn't have started if over selection)
-          if (srcdomdoc == destdomdoc)
-            return NS_OK;
-          
-          // Dragging from another window onto a selection
-          // XXX Decision made to NOT do this,
-          //     note that 4.x does replace if dropped on
-          //deleteSelection = PR_TRUE;
-        }
-        else 
-        {
-          // We are NOT over the selection
-          if (srcdomdoc == destdomdoc)
-          {
-            // Within the same doc: delete if user doesn't want to copy
-            deleteSelection = !userWantsCopy;
-          }
-          else
-          {
-            // Different source doc: Don't delete
-            deleteSelection = PR_FALSE;
-          }
-        }
-      }
-
-      if (deleteSelection)
-      {
-        rv = DeleteSelection(eNone);
-        if (NS_FAILED(rv)) return rv;
-      }
-
-      // If we deleted the selection because we dropped from another doc,
-      //  then we don't have to relocate the caret (insert at the deletion point)
-      if (!(deleteSelection && srcdomdoc != destdomdoc))
-      {
-        // Move the selection to the point under the mouse cursor
-        rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent));
-        if (NS_FAILED(rv))
-          return rv;
-        if(!newSelectionParent)
-          return NS_ERROR_FAILURE;
-       
-        rv = destinationRange->GetStartOffset(&newSelectionOffset);
-        if (NS_FAILED(rv))
-          return rv;
-        selection->Collapse(newSelectionParent, newSelectionOffset);
-      }      
-      // We have to figure out whether to delete and relocate caret only once
-      doPlaceCaret = PR_FALSE;
-    }
-    
-    rv = InsertFromTransferable(trans, nsAutoString(), nsAutoString());
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag)
-{
-  /* we really should be checking the XY coordinates of the mouseevent and ensure that
-   * that particular point is actually within the selection (not just that there is a selection)
-   */
-  aCanDrag = PR_FALSE;
- 
-  // KLUDGE to work around bug 50703
-  // After double click and object property editing, 
-  //  we get a spurious drag event
-  if (mIgnoreSpuriousDragEvent)
-  {
-#ifdef DEBUG_cmanske
-    printf(" *** IGNORING SPURIOUS DRAG EVENT!\n");
-#endif
-    mIgnoreSpuriousDragEvent = PR_FALSE;
-    return NS_OK;
-  }
-   
-  nsCOMPtr selection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-    
-  PRBool isCollapsed;
-  res = selection->GetIsCollapsed(&isCollapsed);
-  if (NS_FAILED(res)) return res;
-  
-  // if we are collapsed, we have no selection so nothing to drag
-  if ( isCollapsed )
-    return NS_OK;
-
-  nsCOMPtr eventTarget;
-  res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget));
-  if (NS_FAILED(res)) return res;
-  if ( eventTarget )
-  {
-    nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget);
-    if ( eventTargetDomNode )
-    {
-      PRBool amTargettedCorrectly = PR_FALSE;
-      res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly);
-      if (NS_FAILED(res)) return res;
-
-    	aCanDrag = amTargettedCorrectly;
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent)
-{
-  nsresult rv;
-
-  nsCOMPtr eventTarget;
-  rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
-  if (NS_FAILED(rv)) return rv;
-  nsCOMPtr domnode = do_QueryInterface(eventTarget);
-
-  /* get the selection to be dragged */
-  nsCOMPtr selection;
-  rv = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(rv)) return rv;
-
-  /* create an array of transferables */
-  nsCOMPtr transferableArray;
-  NS_NewISupportsArray(getter_AddRefs(transferableArray));
-  if (transferableArray == nsnull)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  /* get the drag service */
-  NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  /* create html flavor transferable */
-  nsCOMPtr trans;
-  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
-                                        NS_GET_IID(nsITransferable), 
-                                        getter_AddRefs(trans));
-  if (NS_FAILED(rv)) return rv;
-  if ( !trans ) return NS_ERROR_OUT_OF_MEMORY;
-
-  nsCOMPtr domdoc;
-  rv = GetDocument(getter_AddRefs(domdoc));
-  if (NS_FAILED(rv)) return rv;
-	
-  nsCOMPtr doc = do_QueryInterface(domdoc);
-  if (doc)
-  {
-    nsCOMPtr docEncoder;
-
-    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html");
-    NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
-
-    docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0);
-    docEncoder->SetSelection(selection);
-
-    nsAutoString buffer;
-
-    rv = docEncoder->EncodeToString(buffer);
-  
-    if (NS_FAILED(rv))
-      return rv;
-
-    if ( !buffer.IsEmpty() )
-    {
-      nsCOMPtr htmlConverter;
-      rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter),
-                                              getter_AddRefs(htmlConverter));
-      if (NS_FAILED(rv)) return rv;
-      if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY;
-
-      nsCOMPtr dataWrapper;
-      rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull,
-                                              NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper));
-      if (NS_FAILED(rv)) return rv;
-      if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY;
-
-      rv = trans->AddDataFlavor(kHTMLMime);
-      if (NS_FAILED(rv)) return rv;
-      rv = trans->SetConverter(htmlConverter);
-      if (NS_FAILED(rv)) return rv;
-
-      rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) );
-      if (NS_FAILED(rv)) return rv;
-
-      // QI the data object an |nsISupports| so that when the transferable holds
-      // onto it, it will addref the correct interface.
-      nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) );
-      rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2);
-      if (NS_FAILED(rv)) return rv;
-
-      /* add the transferable to the array */
-      rv = transferableArray->AppendElement(trans);
-      if (NS_FAILED(rv)) return rv;
-
-      /* invoke drag */
-      unsigned int flags;
-      // in some cases we'll want to cut rather than copy... hmmmmm...
-      // if ( wantToCut )
-      //   flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE;
-      // else
-        flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
-      
-      rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags);
-      if (NS_FAILED(rv)) return rv;
-
-      aDragEvent->PreventBubble();
-    }
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType)
-{
-  ForceCompositionEnd();
-
-  // Get Clipboard Service
-  nsresult rv;
-  NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv );
-  if ( NS_FAILED(rv) )
-    return rv;
-    
-  // Get the nsITransferable interface for getting the data from the clipboard
-  nsCOMPtr trans;
-  rv = PrepareTransferable(getter_AddRefs(trans));
-  if (NS_SUCCEEDED(rv) && trans)
-  {
-    // Get the Data from the clipboard  
-    if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
-    {
-      // also get additional html copy hints, if present
-      nsAutoString contextStr, infoStr;
-      nsCOMPtr contextDataObj, infoDataObj;
-      PRUint32 contextLen, infoLen;
-      nsCOMPtr textDataObj;
-      
-      nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID);
-      NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER);
-      contextTrans->AddDataFlavor(kHTMLContext);
-      clipboard->GetData(contextTrans, aSelectionType);
-      contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen);
-
-      nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID);
-      NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER);
-      infoTrans->AddDataFlavor(kHTMLInfo);
-      clipboard->GetData(infoTrans, aSelectionType);
-      infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen);
-      
-      if (contextDataObj)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj = do_QueryInterface(contextDataObj);
-        textDataObj->ToString ( &text );
-        contextStr.Assign ( text, contextLen / 2 );
-        if (text)
-          nsMemory::Free(text);
-      }
-      
-      if (infoDataObj)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj = do_QueryInterface(infoDataObj);
-        textDataObj->ToString ( &text );
-        infoStr.Assign ( text, infoLen / 2 );
-        if (text)
-          nsMemory::Free(text);
-      }
-      rv = InsertFromTransferable(trans, contextStr, infoStr);
-    }
-  }
-
-  return rv;
-}
-
-
-NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste)
-{
-  aCanPaste = PR_FALSE;
-
-  // can't paste if readonly
-  if (!IsModifiable())
-    return NS_OK;
-
-  nsresult rv;
-  NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
-  if (NS_FAILED(rv)) return rv;
-  
-  // the flavors that we can deal with
-  char* textEditorFlavors[] = { kUnicodeMime, nsnull };
-  char* htmlEditorFlavors[] = { kJPEGImageMime, kHTMLMime, nsnull };
-
-  nsCOMPtr flavorsList;
-  rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, 
-         NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
-  if (NS_FAILED(rv)) return rv;
-  
-  PRUint32 editorFlags;
-  GetFlags(&editorFlags);
-  
-  // add the flavors for all editors
-  for (char** flavor = textEditorFlavors; *flavor; flavor++)
-  {
-    nsCOMPtr flavorString;            
-    nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, 
-         NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
-    if (flavorString)
-    {
-      flavorString->SetData(*flavor);
-      flavorsList->AppendElement(flavorString);
-    }
-  }
-  
-  // add the HTML-editor only flavors
-  if ((editorFlags & eEditorPlaintextMask) == 0)
-  {
-    for (char** htmlFlavor = htmlEditorFlavors; *htmlFlavor; htmlFlavor++)
-    {
-      nsCOMPtr flavorString;            
-      nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, 
-           NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
-      if (flavorString)
-      {
-        flavorString->SetData(*htmlFlavor);
-        flavorsList->AppendElement(flavorString);
-      }
-    }
-  }
-  
-  PRBool haveFlavors;
-  rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
-  if (NS_FAILED(rv)) return rv;
-  
-  aCanPaste = haveFlavors;
-  return NS_OK;
-}
-
-
-// 
-// HTML PasteAsQuotation: Paste in a blockquote type=cite
-//
-NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation(PRInt32 aSelectionType)
-{
-  if (mFlags & eEditorPlaintextMask)
-    return PasteAsPlaintextQuotation(aSelectionType);
-
-  nsAutoString citation;
-  return PasteAsCitedQuotation(citation, aSelectionType);
-}
-
-NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation,
-                                                  PRInt32 aSelectionType)
-{
-  nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
-
-  // get selection
-  nsCOMPtr selection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-
-  // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
-  PRBool cancel, handled;
-  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  if (NS_FAILED(res)) return res;
-  if (cancel) return NS_OK; // rules canceled the operation
-  if (!handled)
-  {
-    nsCOMPtr newNode;
-    nsAutoString tag; tag.AssignWithConversion("blockquote");
-    res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
-    if (NS_FAILED(res)) return res;
-    if (!newNode) return NS_ERROR_NULL_POINTER;
-
-    // Try to set type=cite.  Ignore it if this fails.
-    nsCOMPtr newElement (do_QueryInterface(newNode));
-    if (newElement)
-    {
-      nsAutoString type; type.AssignWithConversion("type");
-      nsAutoString cite; cite.AssignWithConversion("cite");
-      newElement->SetAttribute(type, cite);
-    }
-
-    // Set the selection to the underneath the node we just inserted:
-    res = selection->Collapse(newNode, 0);
-    if (NS_FAILED(res))
-    {
-#ifdef DEBUG_akkana
-      printf("Couldn't collapse");
-#endif
-      // XXX: error result:  should res be returned here?
-    }
-
-    res = Paste(aSelectionType);
-  }
-  return res;
-}
-
-//
-// Paste a plaintext quotation
-//
-NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(PRInt32 aSelectionType)
-{
-  // Get Clipboard Service
-  nsresult rv;
-  NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  // Create generic Transferable for getting the data
-  nsCOMPtr trans;
-  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
-                                          NS_GET_IID(nsITransferable), 
-                                          (void**) getter_AddRefs(trans));
-  if (NS_SUCCEEDED(rv) && trans)
-  {
-    // We only handle plaintext pastes here
-    trans->AddDataFlavor(kUnicodeMime);
-
-    // Get the Data from the clipboard
-    clipboard->GetData(trans, aSelectionType);
-
-    // Now we ask the transferable for the data
-    // it still owns the data, we just have a pointer to it.
-    // If it can't support a "text" output of the data the call will fail
-    nsCOMPtr genericDataObj;
-    PRUint32 len = 0;
-    char* flav = 0;
-    rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj),
-                                   &len);
-    if (NS_FAILED(rv))
-    {
-#ifdef DEBUG_akkana
-      printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv);
-#endif
-      return rv;
-    }
-#ifdef DEBUG_akkana
-    printf("Got flavor [%s]\n", flav);
-#endif
-    nsAutoString flavor; flavor.AssignWithConversion(flav);
-    nsAutoString stuffToPaste;
-    if (flavor.EqualsWithConversion(kUnicodeMime))
-    {
-      nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) );
-      if (textDataObj && len > 0)
-      {
-        PRUnichar* text = nsnull;
-        textDataObj->ToString ( &text );
-        stuffToPaste.Assign ( text, len / 2 );
-        nsAutoEditBatch beginBatching(this);
-        rv = InsertAsPlaintextQuotation(stuffToPaste, 0);
-        if (text)
-          nsMemory::Free(text);
-      }
-    }
-    nsCRT::free(flav);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText,
-                                              nsIDOMNode **aNodeInserted)
-{
-  if (mFlags & eEditorPlaintextMask)
-    return InsertAsPlaintextQuotation(aQuotedText, aNodeInserted);
-
-  nsAutoString citation;
-  nsAutoString charset;
-  return InsertAsCitedQuotation(aQuotedText, citation, PR_FALSE,
-                                charset, aNodeInserted);
-}
-
-// text insert.
-NS_IMETHODIMP
-nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText,
-                                         nsIDOMNode **aNodeInserted)
-{
-  // We have the text.  Cite it appropriately:
-  nsCOMPtr citer;
-  nsresult rv;
-  NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv);
-  if (NS_FAILED(rv)) return rv;
-
-  char *citationType = 0;
-  rv = prefs->CopyCharPref("mail.compose.citationType", &citationType);
-                          
-  if (NS_SUCCEEDED(rv) && citationType[0])
-  {
-    if (!strncmp(citationType, "aol", 3))
-      citer = new nsAOLCiter;
-    else
-      citer = new nsInternetCiter;
-    PL_strfree(citationType);
-  }
-  else
-    citer = new nsInternetCiter;
-  
-  // Let the citer quote it for us:
-  nsString quotedStuff;
-  rv = citer->GetCiteString(aQuotedText, quotedStuff);
-  if (!NS_SUCCEEDED(rv))
-    return rv;
-
-  // It's best to put a blank line after the quoted text so that mails
-  // written without thinking won't be so ugly.
-  quotedStuff.Append(PRUnichar('\n'));
-
-  nsCOMPtr preNode;
-  // get selection
-  nsCOMPtr selection;
-  rv = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(rv)) return rv;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-  else
-  {
-    nsAutoEditBatch beginBatching(this);
-    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
-
-    // give rules a chance to handle or cancel
-    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
-    PRBool cancel, handled;
-    rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-    if (NS_FAILED(rv)) return rv;
-    if (cancel) return NS_OK; // rules canceled the operation
-    if (!handled)
-    {
-      // Wrap the inserted quote in a 
 so it won't be wrapped:
-      nsAutoString tag; tag.AssignWithConversion("pre");
-      rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
-      
-      // If this succeeded, then set selection inside the pre
-      // so the inserted text will end up there.
-      // If it failed, we don't care what the return value was,
-      // but we'll fall through and try to insert the text anyway.
-      if (NS_SUCCEEDED(rv) && preNode)
-      {
-        // Add an attribute on the pre node so we'll know it's a quotation.
-        // Do this after the insertion, so that 
-        nsCOMPtr preElement (do_QueryInterface(preNode));
-        if (preElement)
-        {
-          preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"),
-                                   NS_LITERAL_STRING("true"));
-          // set style to not have unwanted vertical margins
-          preElement->SetAttribute(NS_LITERAL_STRING("style"),
-                                   NS_LITERAL_STRING("margin: 0 0 0 0px;"));
-        }
-
-        // and set the selection inside it:
-        selection->Collapse(preNode, 0);
-      }
-
-      rv = InsertText(quotedStuff.GetUnicode());
-
-      if (aNodeInserted && NS_SUCCEEDED(rv))
-      {
-        *aNodeInserted = preNode;
-        NS_IF_ADDREF(*aNodeInserted);
-      }
-    }
-  }
-    
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(rv) && preNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(preNode, address_of(parent), &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
-                                     const nsString& aCitation,
-                                     PRBool aInsertHTML,
-                                     const nsString& aCharset,
-                                     nsIDOMNode **aNodeInserted)
-{
-  nsCOMPtr newNode;
-  nsresult res = NS_OK;
-
-  // get selection
-  nsCOMPtr selection;
-  res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-  else
-  {
-    nsAutoEditBatch beginBatching(this);
-    nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
-
-    // give rules a chance to handle or cancel
-    nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
-    PRBool cancel, handled;
-    res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-    if (NS_FAILED(res)) return res;
-    if (cancel) return NS_OK; // rules canceled the operation
-    if (!handled)
-    {
-      nsAutoString tag; tag.AssignWithConversion("blockquote");
-      res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
-      if (NS_FAILED(res)) return res;
-      if (!newNode) return NS_ERROR_NULL_POINTER;
-
-      // Try to set type=cite.  Ignore it if this fails.
-      nsCOMPtr newElement (do_QueryInterface(newNode));
-      if (newElement)
-      {
-        nsAutoString type; type.AssignWithConversion("type");
-        nsAutoString cite; cite.AssignWithConversion("cite");
-        newElement->SetAttribute(type, cite);
-
-        if (aCitation.Length() > 0)
-          newElement->SetAttribute(cite, aCitation);
-
-        // Set the selection inside the blockquote so aQuotedText will go there:
-        selection->Collapse(newNode, 0);
-      }
-
-      if (aInsertHTML)
-        res = InsertHTMLWithCharset(aQuotedText, aCharset);
-
-      else
-        res = InsertText(aQuotedText.GetUnicode());  // XXX ignore charset
-
-      if (aNodeInserted)
-      {
-        if (NS_SUCCEEDED(res))
-        {
-          *aNodeInserted = newNode;
-          NS_IF_ADDREF(*aNodeInserted);
-        }
-      }
-    }
-  }
-
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(res) && newNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(newNode, address_of(parent), &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-  return res;
-}
-
-// Shared between OutputToString and OutputToStream
-NS_IMETHODIMP
-nsHTMLEditor::GetAndInitDocEncoder(const nsAReadableString& aFormatType,
-                                   PRUint32 aFlags,
-                                   const nsAReadableString* aCharset,
-                                   nsIDocumentEncoder** encoder)
-{
-  nsCOMPtr presShell;
-  nsresult rv = GetPresShell(getter_AddRefs(presShell));
-  if (NS_FAILED(rv)) return rv;
-  if (!presShell) return NS_ERROR_FAILURE;
-
-  nsCAutoString formatType(NS_DOC_ENCODER_CONTRACTID_BASE);
-  formatType.AppendWithConversion(aFormatType);
-  nsCOMPtr docEncoder (do_CreateInstance(formatType, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr doc;
-  rv = presShell->GetDocument(getter_AddRefs(doc));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = docEncoder->Init(doc, aFormatType, aFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (aCharset && aCharset->Length() != 0
-      && !(aCharset->Equals(NS_LITERAL_STRING("null"))))
-    docEncoder->SetCharset(*aCharset);
-
-  PRInt32 wc;
-  (void) GetWrapWidth(&wc);
-  if (wc >= 0)
-    (void) docEncoder->SetWrapColumn(wc);
-
-  // Set the selection, if appropriate.
-  // We do this either if the OutputSelectionOnly flag is set,
-  // in which case we use our existing selection ...
-  if (aFlags & nsIDocumentEncoder::OutputSelectionOnly)
-  {
-    nsCOMPtr selection;
-    rv = GetSelection(getter_AddRefs(selection));
-    if (NS_SUCCEEDED(rv) && selection)
-      rv = docEncoder->SetSelection(selection);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  // ... or if the root element is not a body,
-  // in which case we set the selection to encompass the root.
-  else
-  {
-    nsCOMPtr rootElement;
-    GetRootElement(getter_AddRefs(rootElement));
-    NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
-    if (!nsHTMLEditUtils::IsBody(rootElement))
-    {
-      // XXX Why does this use range rather than selection collapse/extend?
-      nsCOMPtr range (do_CreateInstance(kCRangeCID, &rv));
-      if (NS_FAILED(rv)) return rv;
-      if (!range) return NS_ERROR_FAILURE;
-      nsCOMPtr selection (do_CreateInstance(kCDOMSelectionCID,
-                                                          &rv));
-      if (NS_FAILED(rv)) return rv;
-      if (!selection) return NS_ERROR_FAILURE;
-
-      // get the independent selection interface
-      nsCOMPtr indSel = do_QueryInterface(selection);
-      if (indSel)
-        indSel->SetPresShell(presShell);
-
-      nsCOMPtr content(do_QueryInterface(rootElement));
-      if (content)
-      {
-        range->SetStart(rootElement,0);
-        PRInt32 children;
-        if (NS_SUCCEEDED(content->ChildCount(children)))
-          range->SetEnd(rootElement,children);
-        // XXX else, should we return the error code?
-
-        if (NS_FAILED(selection->AddRange(range)))
-          return NS_ERROR_FAILURE;
-      }
-      rv = docEncoder->SetSelection(selection);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  }
-
-  NS_ADDREF(*encoder = docEncoder);
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLEditor::OutputToString(nsAWritableString& aOutputString,
-                             const nsAReadableString& aFormatType,
-                             PRUint32 aFlags)
-{
-  // XXX Why isn't this rules stuff also in OutputToStream?
-  PRBool cancel, handled;
-  nsString resultString;
-  nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText);
-  ruleInfo.outString = &resultString;
-  // XXX Struct should store a nsAReadable*
-  nsAutoString str(aFormatType);
-  ruleInfo.outputFormat = &str;
-  nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel, &handled);
-  if (cancel || NS_FAILED(rv)) { return rv; }
-  if (handled)
-  { // this case will get triggered by password fields
-    aOutputString.Assign(*(ruleInfo.outString));
-    return rv;
-  }
-
-  nsCOMPtr encoder;
-  rv = GetAndInitDocEncoder(aFormatType, aFlags, 0, getter_AddRefs(encoder));
-  if (NS_FAILED(rv))
-    return rv;
-  rv = encoder->EncodeToString(aOutputString);
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream,
-                             const nsAReadableString& aFormatType,
-                             const nsAReadableString* aCharset,
-                             PRUint32 aFlags)
-{
-  nsresult rv;
-
-  // special-case for empty document when requesting plain text,
-  // to account for the bogus text node.
-  // XXX Should there be a similar test in OutputToString?
-  if (aFormatType == NS_LITERAL_STRING("text/plain"))
-  {
-    PRBool docEmpty;
-    rv = GetDocumentIsEmpty(&docEmpty);
-    if (NS_FAILED(rv)) return rv;
-    
-    if (docEmpty)
-       return NS_OK;    // output nothing
-  }
-
-  nsCOMPtr encoder;
-  rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset,
-                            getter_AddRefs(encoder));
-
-  if (NS_FAILED(rv))
-    return rv;
-
-  return encoder->EncodeToStream(aOutputStream);
-}
 
 static nsresult SetSelectionAroundHeadChildren(nsCOMPtr aSelection, nsWeakPtr aDocWeak)
 {
@@ -6926,96 +3306,6 @@ nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode)
   return tbl;
 }
 
-NS_IMETHODIMP
-nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode)
-{
-  nsresult result=NS_ERROR_NOT_INITIALIZED;
-  nsCOMPtr selection;
-  result = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(result)) return result;
-  if (!selection) return NS_ERROR_NULL_POINTER;
-
-  PRBool collapsed;
-  result = selection->GetIsCollapsed(&collapsed);
-  if (NS_SUCCEEDED(result) && !collapsed) 
-  {
-    result = DeleteSelection(nsIEditor::eNone);
-    if (NS_FAILED(result)) {
-      return result;
-    }
-    // get the new selection
-    result = GetSelection(getter_AddRefs(selection));
-    if (NS_FAILED(result)) {
-      return result;
-    }
-#ifdef NS_DEBUG
-    nsCOMPtrtestSelectedNode;
-    nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
-    // no selection is ok.
-    // if there is a selection, it must be collapsed
-    if (testSelectedNode)
-    {
-      PRBool testCollapsed;
-      debugResult = selection->GetIsCollapsed(&testCollapsed);
-      NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
-      NS_ASSERTION(testCollapsed, "selection not reset after deletion");
-    }
-#endif
-  }
-  // split the selected node
-  PRInt32 offsetOfSelectedNode;
-  result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
-  if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
-  {
-    nsCOMPtr selectedNode;
-    PRUint32 selectedNodeContentCount=0;
-    nsCOMPtrselectedParentNodeAsText;
-    selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
-
-    offsetOfNewNode = offsetOfSelectedNode;
-    
-    /* if the selection is a text node, split the text node if necesary
-       and compute where to put the new node
-    */
-    if (selectedParentNodeAsText) 
-    { 
-      PRInt32 indexOfTextNodeInParent;
-      selectedNode = do_QueryInterface(parentSelectedNode);
-      selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
-      selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
-      GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
-
-      if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
-      {
-        nsCOMPtr newSiblingNode;
-        result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
-        // now get the node's offset in it's parent, and insert the new tag there
-        if (NS_SUCCEEDED(result)) {
-          result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
-        }
-      }
-      else 
-      { // determine where to insert the new node
-        if (0==offsetOfSelectedNode) {
-          offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
-        }
-        else {                 // insert new node as last child
-          GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
-          offsetOfNewNode++;    // offsets are 0-based, and we need the index of the new node
-        }
-      }
-    }
-    // Here's where the new node was inserted
-  }
-#ifdef DEBUG
-  else {
-    printf("InsertLineBreak into an empty document is not yet supported\n");
-  }
-#endif
-  return result;
-}
-
-
 #ifdef XP_MAC
 #pragma mark -
 #endif
@@ -7211,272 +3501,6 @@ nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
 #pragma mark -
 #endif
 
-nsresult
-nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
-{
-  // Can only change font size by + or - 1
-  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
-    return NS_ERROR_ILLEGAL_VALUE;
-  
-  ForceCompositionEnd();
-
-  // Get the selection 
-  nsCOMPtrselection;
-  nsresult res = GetSelection(getter_AddRefs(selection));
-  if (NS_FAILED(res)) return res;
-  if (!selection) return NS_ERROR_FAILURE;
-  nsCOMPtr selPriv(do_QueryInterface(selection));  
-  // Is the selection collapsed?
-  PRBool bCollapsed;
-  res = selection->GetIsCollapsed(&bCollapsed);
-  if (NS_FAILED(res)) return res;
-  
-  // if it's collapsed set typing state
-  if (bCollapsed)
-  {
-    nsCOMPtr atom;
-    if (aSizeChange==1) atom = nsIEditProperty::big;
-    else                atom = nsIEditProperty::small;
-    // manipulating text attributes on a collapsed selection only sets state for the next text insertion
-    return mTypeInState->SetProp(atom, nsAutoString(), nsAutoString());
-  }
-  
-  // wrap with txn batching, rules sniffing, and selection preservation code
-  nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext);
-  nsAutoSelectionReset selectionResetter(selection, this);
-  nsAutoTxnsConserveSelection dontSpazMySelection(this);
-
-  // get selection range enumerator
-  nsCOMPtr enumerator;
-  res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
-  if (NS_FAILED(res)) return res;
-  if (!enumerator)    return NS_ERROR_FAILURE;
-
-  // loop thru the ranges in the selection
-  enumerator->First(); 
-  nsCOMPtr currentItem;
-  while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
-  {
-    res = enumerator->CurrentItem(getter_AddRefs(currentItem));
-    if (NS_FAILED(res)) return res;
-    if (!currentItem)   return NS_ERROR_FAILURE;
-    
-    nsCOMPtr range( do_QueryInterface(currentItem) );
-
-    // adjust range to include any ancestors who's children are entirely selected
-    res = PromoteInlineRange(range);
-    if (NS_FAILED(res)) return res;
-    
-    // check for easy case: both range endpoints in same text node
-    nsCOMPtr startNode, endNode;
-    res = range->GetStartContainer(getter_AddRefs(startNode));
-    if (NS_FAILED(res)) return res;
-    res = range->GetEndContainer(getter_AddRefs(endNode));
-    if (NS_FAILED(res)) return res;
-    if ((startNode == endNode) && IsTextNode(startNode))
-    {
-      PRInt32 startOffset, endOffset;
-      range->GetStartOffset(&startOffset);
-      range->GetEndOffset(&endOffset);
-      nsCOMPtr nodeAsText = do_QueryInterface(startNode);
-      res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
-      if (NS_FAILED(res)) return res;
-    }
-    else
-    {
-      // not the easy case.  range not contained in single text node. 
-      // there are up to three phases here.  There are all the nodes
-      // reported by the subtree iterator to be processed.  And there
-      // are potentially a starting textnode and an ending textnode
-      // which are only partially contained by the range.
-      
-      // lets handle the nodes reported by the iterator.  These nodes
-      // are entirely contained in the selection range.  We build up
-      // a list of them (since doing operations on the document during
-      // iteration would perturb the iterator).
-
-      nsCOMPtr iter;
-      res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull,
-                                                NS_GET_IID(nsIContentIterator), 
-                                                getter_AddRefs(iter));
-      if (NS_FAILED(res)) return res;
-      if (!iter)          return NS_ERROR_FAILURE;
-
-      nsCOMPtr arrayOfNodes;
-      nsCOMPtr content;
-      nsCOMPtr node;
-      nsCOMPtr isupports;
-      
-      // make a array
-      res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
-      if (NS_FAILED(res)) return res;
-      
-      // iterate range and build up array
-      res = iter->Init(range);
-      if (NS_SUCCEEDED(res))
-      {
-        while (NS_ENUMERATOR_FALSE == iter->IsDone())
-        {
-          res = iter->CurrentNode(getter_AddRefs(content));
-          if (NS_FAILED(res)) return res;
-          node = do_QueryInterface(content);
-          if (!node) return NS_ERROR_FAILURE;
-          if (IsEditable(node))
-          { 
-            isupports = do_QueryInterface(node);
-            arrayOfNodes->AppendElement(isupports);
-          }
-          iter->Next();
-        }
-        
-        // now that we have the list, do the font size change on each node
-        PRUint32 listCount;
-        PRUint32 j;
-        arrayOfNodes->Count(&listCount);
-        for (j = 0; j < listCount; j++)
-        {
-          isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
-          node = do_QueryInterface(isupports);
-          res = RelativeFontChangeOnNode(aSizeChange, node);
-          if (NS_FAILED(res)) return res;
-          arrayOfNodes->RemoveElementAt(0);
-        }
-      }
-      // now check the start and end parents of the range to see if they need to 
-      // be seperately handled (they do if they are text nodes, due to how the
-      // subtree iterator works - it will not have reported them).
-      if (IsTextNode(startNode) && IsEditable(startNode))
-      {
-        nsCOMPtr nodeAsText = do_QueryInterface(startNode);
-        PRInt32 startOffset;
-        PRUint32 textLen;
-        range->GetStartOffset(&startOffset);
-        nodeAsText->GetLength(&textLen);
-        res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
-        if (NS_FAILED(res)) return res;
-      }
-      if (IsTextNode(endNode) && IsEditable(endNode))
-      {
-        nsCOMPtr nodeAsText = do_QueryInterface(endNode);
-        PRInt32 endOffset;
-        range->GetEndOffset(&endOffset);
-        res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
-        if (NS_FAILED(res)) return res;
-      }
-    }
-    enumerator->Next();
-  }
-  
-  return res;  
-}
-
-nsresult
-nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, 
-                                            nsIDOMCharacterData *aTextNode, 
-                                            PRInt32 aStartOffset,
-                                            PRInt32 aEndOffset)
-{
-  // Can only change font size by + or - 1
-  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
-    return NS_ERROR_ILLEGAL_VALUE;
-  if (!aTextNode) return NS_ERROR_NULL_POINTER;
-  
-  // dont need to do anything if no characters actually selected
-  if (aStartOffset == aEndOffset) return NS_OK;
-  
-  nsresult res = NS_OK;
-  nsCOMPtr tmp, node = do_QueryInterface(aTextNode);
-  
-  // do we need to split the text node?
-  PRUint32 textLen;
-  aTextNode->GetLength(&textLen);
-  
-  // -1 is a magic value meaning to the end of node
-  if (aEndOffset == -1) aEndOffset = textLen;
-  
-  if ( (PRUint32)aEndOffset != textLen )
-  {
-    // we need to split off back of text node
-    res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
-    if (NS_FAILED(res)) return res;
-    node = tmp;  // remember left node
-  }
-  if ( aStartOffset )
-  {
-    // we need to split off front of text node
-    res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
-    if (NS_FAILED(res)) return res;
-  }
-  
-  // reparent the node inside font node with appropriate relative size
-  res = InsertContainerAbove(node, address_of(tmp), NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small"));
-  return res;
-}
-
-
-nsresult
-nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, 
-                                        nsIDOMNode *aNode)
-{
-  // Can only change font size by + or - 1
-  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
-    return NS_ERROR_ILLEGAL_VALUE;
-  if (!aNode) return NS_ERROR_NULL_POINTER;
-
-  nsresult res = NS_OK;
-  nsCOMPtr tmp;
-  nsAutoString tag;
-  if (aSizeChange == 1) tag.AssignWithConversion("big");
-  else tag.AssignWithConversion("small");
-  
-  // is this node a text node?
-  if (IsTextNode(aNode))
-  {
-    res = InsertContainerAbove(aNode, address_of(tmp), tag);
-    return res;
-  }
-  // is it the opposite of what we want?  
-  if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) || 
-       ((aSizeChange == -1) &&  nsHTMLEditUtils::IsBig(aNode)) )
-  {
-    // in that case, just remove this node and pull up the children
-    res = RemoveContainer(aNode);
-    return res;
-  }
-  // can it be put inside a "big" or "small"?
-  if (TagCanContain(tag, aNode))
-  {
-    // ok, chuck it in.
-    res = InsertContainerAbove(aNode, address_of(tmp), tag);
-    return res;
-  }
-  // none of the above?  then cycle through the children.
-  // MOOSE: we should group the children together if possible
-  // into a single "big" or "small".  For the moment they are
-  // each getting their own.  
-  nsCOMPtr childNodes;
-  res = aNode->GetChildNodes(getter_AddRefs(childNodes));
-  if (NS_FAILED(res)) return res;
-  if (childNodes)
-  {
-    PRInt32 j;
-    PRUint32 childCount;
-    childNodes->GetLength(&childCount);
-    for (j=0 ; j < (PRInt32)childCount; j++)
-    {
-      nsCOMPtr childNode;
-      res = childNodes->Item(j, getter_AddRefs(childNode));
-      if ((NS_SUCCEEDED(res)) && (childNode))
-      {
-        res = RelativeFontChangeOnNode(aSizeChange, childNode);
-        if (NS_FAILED(res)) return res;
-      }
-    }
-  }
-  return res;
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
 //                   one within the parent
diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h
index 30a0b1ca42b..212c3d5997f 100644
--- a/mozilla/editor/libeditor/html/nsHTMLEditor.h
+++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h
@@ -25,7 +25,8 @@
 
 #include "nsCOMPtr.h"
 
-#include "nsIPlaintextEditor.h"
+#include "nsPlaintextEditor.h"
+#include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsITableEditor.h"
 #include "nsIEditorMailSupport.h"
@@ -51,8 +52,7 @@ class nsIDocumentEncoder;
  * The HTML editor implementation.
* Use to edit HTML document represented as a DOM tree. */ -class nsHTMLEditor : public nsEditor, - public nsIPlaintextEditor, +class nsHTMLEditor : public nsPlaintextEditor, public nsIHTMLEditor, public nsIEditorMailSupport, public nsITableEditor, @@ -74,7 +74,10 @@ public: kOpRemoveList = 3006, kOpMakeDefListItem = 3007, kOpInsertElement = 3008, - kOpInsertQuotation = 3009 + kOpInsertQuotation = 3009, + kOpSetTextProperty = 3010, + kOpRemoveTextProperty = 3011, + kOpHTMLPaste = 3012 }; @@ -89,8 +92,9 @@ public: nsHTMLEditor(); virtual ~nsHTMLEditor(); - /* ------------ nsIPlaintextEditor methods -------------- */ - NS_DECL_NSIPLAINTEXTEDITOR + /* ------------ nsPlaintextEditor overrides -------------- */ + NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent); + NS_IMETHODIMP CollapseSelectionToStart(); /* ------------ nsIHTMLEditor methods -------------- */ NS_IMETHOD SetInlineProperty(nsIAtom *aProperty, @@ -118,7 +122,6 @@ public: NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); - NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); NS_IMETHOD SelectElement(nsIDOMElement* aElement); NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); @@ -240,12 +243,6 @@ public: /** prepare the editor for use */ NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags); - NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty); - - NS_IMETHOD DeleteSelection(EDirection aAction); - - NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet); - /** we override this here to install event listeners */ NS_IMETHOD PostCreate(); @@ -266,15 +263,6 @@ public: NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent); NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent); - NS_IMETHOD OutputToString(nsAWritableString& aOutputString, - const nsAReadableString& aFormatType, - PRUint32 aFlags); - - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsAReadableString& aFormatType, - const nsAReadableString* aCharsetOverride, - PRUint32 aFlags); - NS_IMETHOD GetHeadContentsAsHTML(nsString& aOutputString); NS_IMETHOD ReplaceHeadContentsWithHTML(const nsString &aSourceToInsert); @@ -307,7 +295,6 @@ public: PRInt32 aOffset, PRBool aNoEmptyNodes); - NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); /** returns the absolute position of the end points of aSelection * in the document as a text stream. @@ -356,8 +343,6 @@ protected: */ NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject); - NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode); - /* StyleSheet load callback */ static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData); @@ -376,11 +361,11 @@ protected: // key event helpers NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled); NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, - nsCOMPtr *outBRNode, EDirection aSelect = eNone); + nsCOMPtr *outBRNode, nsIEditor::EDirection aSelect = nsIEditor::eNone); NS_IMETHOD CreateBRImpl(nsCOMPtr *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr *outBRNode, - EDirection aSelect); + nsIEditor::EDirection aSelect); NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); // Table Editing (implemented in nsTableEditor.cpp) @@ -491,12 +476,6 @@ protected: PRInt32 aEndOffset, nsISelection *aSelection); - // Helpers for output routines - NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType, - PRUint32 aFlags, - const nsAReadableString* aCharset, - nsIDocumentEncoder** encoder); - // Methods for handling plaintext quotations NS_IMETHOD PasteAsPlaintextQuotation(PRInt32 aSelectionType); NS_IMETHOD InsertAsPlaintextQuotation(const nsString& aQuotedText, @@ -610,38 +589,24 @@ protected: // Data members protected: - TypeInState* mTypeInState; - nsCOMPtr mRules; - nsCOMPtr mKeyListenerP; - nsCOMPtr mMouseListenerP; - nsCOMPtr mTextListenerP; - nsCOMPtr mCompositionListenerP; - nsCOMPtr mDragListenerP; - nsCOMPtr mFocusListenerP; - PRBool mIsComposing; - - // Used by nsIPlaintextEditor but not html editors -- factor me! - PRInt32 mMaxTextLength; + TypeInState* mTypeInState; - nsCOMPtr mBoldAtom; - nsCOMPtr mItalicAtom; - nsCOMPtr mUnderlineAtom; - nsCOMPtr mFontAtom; - nsCOMPtr mLinkAtom; + nsCOMPtr mBoldAtom; + nsCOMPtr mItalicAtom; + nsCOMPtr mUnderlineAtom; + nsCOMPtr mFontAtom; + nsCOMPtr mLinkAtom; nsCOMPtr mCachedNode; - PRBool mCachedBoldStyle; - PRBool mCachedItalicStyle; - PRBool mCachedUnderlineStyle; + PRBool mCachedBoldStyle; + PRBool mCachedItalicStyle; + PRBool mCachedUnderlineStyle; nsString mCachedFontName; // Used by GetFirstSelectedCell and GetNextSelectedCell PRInt32 mSelectedCellIndex; public: - static nsIAtom *gTypingTxnName; - static nsIAtom *gIMETxnName; - static nsIAtom *gDeleteTxnName; // friends friend class nsHTMLEditRules; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp b/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp new file mode 100644 index 00000000000..0b50ebb6a1a --- /dev/null +++ b/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp @@ -0,0 +1,1486 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ +#include "nsICaret.h" + +#include "nsHTMLEditor.h" +#include "nsHTMLEditRules.h" +#include "nsHTMLEditUtils.h" + +#include "nsEditorEventListeners.h" + +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" + +// Misc +#include "nsEditorUtils.h" + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + + +NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aProperty) { return NS_ERROR_NULL_POINTER; } + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + ForceCompositionEnd(); + + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selPriv(do_QueryInterface(selection)); + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return mTypeInState->SetProp(aProperty, *aAttribute, *aValue); + } + + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + nsAutoTxnsConserveSelection dontSpazMySelection(this); + + PRBool cancel, handled; + nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (!cancel && !handled) + { + // get selection range enumerator + nsCOMPtr enumerator; + res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + + nsCOMPtr range( do_QueryInterface(currentItem) ); + + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + else + { + // not the easy case. range not contained in single text node. + // there are up to three phases here. There are all the nodes + // reported by the subtree iterator to be processed. And there + // are potentially a starting textnode and an ending textnode + // which are only partially contained by the range. + + // lets handle the nodes reported by the iterator. These nodes + // are entirely contained in the selection range. We build up + // a list of them (since doing operations on the document during + // iteration would perturb the iterator). + + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + res = iter->Init(range); + // init returns an error if no nodes in range. + // this can easily happen with the subtree + // iterator if the selection doesn't contain + // any *whole* nodes. + if (NS_SUCCEEDED(res)) + { + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; + } + } + // first check the start parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(startNode) && IsEditable(startNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + PRInt32 startOffset; + PRUint32 textLen; + range->GetStartOffset(&startOffset); + nodeAsText->GetLength(&textLen); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + + // last check the end parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(endNode) && IsEditable(endNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(endNode); + PRInt32 endOffset; + range->GetEndOffset(&endOffset); + res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + } + enumerator->Next(); + } + } + if (!cancel) + { + // post-process + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + return res; +} + + + +nsresult +nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aTextNode) return NS_ERROR_NULL_POINTER; + + // dont need to do anything if no characters actually selected + if (aStartOffset == aEndOffset) return NS_OK; + + nsresult res = NS_OK; + nsCOMPtr tmp, node = do_QueryInterface(aTextNode); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // do we need to split the text node? + PRUint32 textLen; + aTextNode->GetLength(&textLen); + + if ( (PRUint32)aEndOffset != textLen ) + { + // we need to split off back of text node + res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + node = tmp; // remember left node + } + if ( aStartOffset ) + { + // we need to split off front of text node + res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + } + + // reparent the node inside inline node with appropriate {attribute,value} + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + return res; +} + + +nsresult +nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsCOMPtr tmp; + nsAutoString tag; + aProperty->ToString(tag); + tag.ToLowerCase(); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // is it already the right kind of node, but with wrong attribute? + if (NodeIsType(aNode, aProperty)) + { + // just set the attribute on it. + // but first remove any contrary style in it's children. + res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE); + if (NS_FAILED(res)) return res; + nsCOMPtr elem = do_QueryInterface(aNode); + return SetAttribute(elem, *aAttribute, *aValue); + } + + // can it be put inside inline node? + if (TagCanContain(tag, aNode)) + { + nsCOMPtr priorNode, nextNode; + // is either of it's neighbors the right kind of node? + GetPriorHTMLSibling(aNode, address_of(priorNode)); + GetNextHTMLSibling(aNode, address_of(nextNode)); + if (priorNode && NodeIsType(priorNode, aProperty) && + HasAttrVal(priorNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, priorNode, -1); + } + else if (nextNode && NodeIsType(nextNode, aProperty) && + HasAttrVal(nextNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, nextNode, 0); + } + else + { + // ok, chuck it in it's very own container + res = InsertContainerAbove(aNode, address_of(tmp), tag, aAttribute, aValue); + } + if (NS_FAILED(res)) return res; + return RemoveStyleInside(aNode, aProperty, aAttribute); + } + // none of the above? then cycle through the children. + nsCOMPtr childNodes; + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + PRInt32 j; + PRUint32 childCount; + childNodes->GetLength(&childCount); + if (childCount) + { + nsCOMPtr arrayOfNodes; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // populate the list + for (j=0 ; j < (PRInt32)childCount; j++) + { + nsCOMPtr childNode; + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode)) + { + isupports = do_QueryInterface(childNode); + arrayOfNodes->AppendElement(isupports); + } + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + arrayOfNodes->Count(&listCount); + for (j = 0; j < (PRInt32)listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + } + return res; +} + + +nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, + nsIAtom *aProperty, + const nsString *aAttribute) +{ + if (!inRange) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, origStartNode; + PRInt32 startOffset, endOffset, origStartOffset; + + res = inRange->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + origStartNode = startNode; + origStartOffset = startOffset; + PRBool sameNode = (startNode==endNode); + + // split any matching style nodes above the start of range + res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + if (sameNode && (startNode != origStartNode)) + { + // our startNode got split. This changes the offset of the end of our range. + endOffset -= origStartOffset; + } + + // second verse, same as the first... + res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // reset the range + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr *aNode, + PRInt32 *aOffset, + nsIAtom *aProperty, // null here means we split all properties + const nsString *aAttribute, + nsCOMPtr *outLeftNode, + nsCOMPtr *outRightNode) +{ + if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER; + if (outLeftNode) *outLeftNode = nsnull; + if (outRightNode) *outRightNode = nsnull; + // split any matching style nodes above the node/offset + nsCOMPtr parent, tmp = *aNode; + PRInt32 offset; + while (tmp && !nsHTMLEditUtils::IsBody(tmp)) + { + if ( (aProperty && NodeIsType(tmp, aProperty)) || // node is the correct inline prop + (aProperty == nsIEditProperty::href && nsHTMLEditUtils::IsLink(tmp)) || // node is href - test if really GetParentNode(getter_AddRefs(*aNode)); + *aOffset = offset; + } + tmp->GetParentNode(getter_AddRefs(parent)); + tmp = parent; + } + return NS_OK; +} + +PRBool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode) +{ + if (!aNode) return PR_FALSE; + if (!IsContainer(aNode)) return PR_FALSE; + if (!IsEditable(aNode)) return PR_FALSE; + if (!IsInlineNode(aNode)) return PR_FALSE; + if (NodeIsType(aNode, nsIEditProperty::a)) return PR_FALSE; + return PR_TRUE; +} + +nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, + nsIAtom *aProperty, // null here means remove all properties + const nsString *aAttribute, + PRBool aChildrenOnly) +{ + if (!aNode) return NS_ERROR_NULL_POINTER; + if (IsTextNode(aNode)) return NS_OK; + nsresult res = NS_OK; + + // first process the children + nsCOMPtr child, tmp; + aNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + // cache next sibling since we might remove child + child->GetNextSibling(getter_AddRefs(tmp)); + res = RemoveStyleInside(child, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + child = tmp; + } + + // then process the node itself + if ( !aChildrenOnly && + ((aProperty && NodeIsType(aNode, aProperty)) || // node is prop we asked for + (aProperty == nsIEditProperty::href && nsHTMLEditUtils::IsLink(aNode))) || // but check for link (IsEmpty()) + { + res = RemoveContainer(aNode); + } + // otherwise we just want to eliminate the attribute + else + { + if (HasAttr(aNode, aAttribute)) + { + // if this matching attribute is the ONLY one on the node, + // then remove the whole node. Otherwise just nix the attribute. + if (IsOnlyAttribute(aNode, aAttribute)) + { + res = RemoveContainer(aNode); + } + else + { + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return NS_ERROR_NULL_POINTER; + res = RemoveAttribute(elem, *aAttribute); + } + } + } + } + return res; +} + +PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode || !aAttribute) return PR_FALSE; // ooops + nsCOMPtr content = do_QueryInterface(aNode); + if (!content) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID; + nsCOMPtr attrName, prefix; + content->GetAttributeCount(attrCount); + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), + *getter_AddRefs(prefix)); + nsAutoString attrString, tmp; + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's the attribute we know about, keep looking + if (attrString.EqualsIgnoreCase(*aAttribute)) continue; + // if it's a special _moz... attribute, keep looking + attrString.Left(tmp,4); + if (tmp.EqualsWithConversion("_moz")) continue; + // otherwise, it's another attribute, so return false + return PR_FALSE; + } + // if we made it through all of them without finding a real attribute + // other than aAttribute, then return PR_TRUE + return PR_TRUE; +} + +PRBool +nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1, + nsIDOMNode *aNode2) +{ + if (!aNode1 || !aNode2) return PR_FALSE; // ooops + nsCOMPtr content1 = do_QueryInterface(aNode1); + if (!content1) return PR_FALSE; // ooops + nsCOMPtr content2 = do_QueryInterface(aNode2); + if (!content2) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0; + nsCOMPtr attrName, prefix; + nsresult res, res2; + content1->GetAttributeCount(attrCount); + nsAutoString attrString, tmp, attrVal1, attrVal2; + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), + *getter_AddRefs(prefix)); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp.EqualsWithConversion("_moz")) continue; + // otherwise, it's another attribute, so count it + realCount1++; + // and compare it to element2's attributes + res = content1->GetAttribute(nameSpaceID, attrName, attrVal1); + res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2); + if (res != res2) return PR_FALSE; + if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE; + } + + content2->GetAttributeCount(attrCount); + for (i=0; iGetAttributeNameAt(i, nameSpaceID, *getter_AddRefs(attrName), + *getter_AddRefs(prefix)); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp.EqualsWithConversion("_moz")) continue; + // otherwise, it's another attribute, so count it + realCount2++; + } + + if (realCount1 != realCount2) return PR_FALSE; + // otherwise, attribute counts match, and we already compared them + // when going through the first list, so we're done. + return PR_TRUE; +} + +PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + return PR_TRUE; +} + + +PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + + // check if attribute has a value + PRBool isSet; + attNode->GetSpecified(&isSet); + // if no value, and that's what we wanted, then return true + if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; + + // get attribute value + nsAutoString attrVal; + attNode->GetValue(attrVal); + + // do values match? + if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE; + return PR_FALSE; +} + + +nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange) +{ + if (!inRange) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, parent; + PRInt32 startOffset, endOffset; + + res = inRange->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + while ( startNode && + !nsHTMLEditUtils::IsBody(startNode) && + IsAtFrontOfNode(startNode, startOffset) ) + { + res = GetNodeLocation(startNode, address_of(parent), &startOffset); + if (NS_FAILED(res)) return res; + startNode = parent; + } + if (!startNode) return NS_ERROR_NULL_POINTER; + + while ( endNode && + !nsHTMLEditUtils::IsBody(endNode) && + IsAtEndOfNode(endNode, endOffset) ) + { + res = GetNodeLocation(endNode, address_of(parent), &endOffset); + if (NS_FAILED(res)) return res; + endNode = parent; + endOffset++; // we are AFTER this node + } + if (!endNode) return NS_ERROR_NULL_POINTER; + + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + if (!aOffset) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr firstNode; + GetFirstEditableChild(aNode, address_of(firstNode)); + if (!firstNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(firstNode, aNode, offset); + if (offset < aOffset) return PR_FALSE; + return PR_TRUE; + } +} + +PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + PRUint32 len; + GetLengthOfDOMNode(aNode, len); + if (aOffset == (PRInt32)len) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr lastNode; + GetLastEditableChild(aNode, address_of(lastNode)); + if (!lastNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(lastNode, aNode, offset); + if (offset < aOffset) return PR_TRUE; + return PR_FALSE; + } +} + +NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll) +{ + return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull); +} + +NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll, + nsString *outValue) +{ + if (!aProperty) + return NS_ERROR_NULL_POINTER; +/* + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); } + nsCRT::free(propCString); + } +*/ + nsresult result; + aAny=PR_FALSE; + aAll=PR_TRUE; + aFirst=PR_FALSE; + PRBool first=PR_TRUE; + nsCOMPtrselection; + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selPriv(do_QueryInterface(selection)); + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + nsCOMPtr collapsedNode; + nsCOMPtr enumerator; + result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(result)) return result; + if (!enumerator) return NS_ERROR_NULL_POINTER; + + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + // XXX: should be a while loop, to get each separate range + // XXX: ERROR_HANDLING can currentItem be null? + if ((NS_SUCCEEDED(result)) && currentItem) + { + PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag + nsCOMPtr range(do_QueryInterface(currentItem)); + + if (isCollapsed) + { + // efficiency hack. we cache prior results for being collapsed in a given text node. + // this speeds up typing. Note that other parts of the editor code have to clear out + // this cache after certain actions. + range->GetStartContainer(getter_AddRefs(collapsedNode)); + if (!collapsedNode) return NS_ERROR_FAILURE; + // refresh the cache if we need to + if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode); + // cache now current, use it! But override it with typeInState results if any... + PRBool isSet, theSetting; + if (aAttribute) + mTypeInState->GetTypingState(isSet, theSetting, aProperty, *aAttribute, outValue); + else + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + return NS_OK; + } + /* + if (aProperty == mBoldAtom.get()) + { + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + } + else + { + aFirst = aAny = aAll = mCachedBoldStyle; + } + return NS_OK; + } + else if (aProperty == mItalicAtom.get()) + { + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + } + else + { + aFirst = aAny = aAll = mCachedItalicStyle; + } + return NS_OK; + } + else if (aProperty == mUnderlineAtom.get()) + { + mTypeInState->GetTypingState(isSet, theSetting, aProperty); + if (isSet) + { + aFirst = aAny = aAll = theSetting; + } + else + { + aFirst = aAny = aAll = mCachedUnderlineStyle; + } + return NS_OK; + } */ + } + + // either non-collapsed selection or no cached value: do it the hard way + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(result)) return result; + if (!iter) return NS_ERROR_NULL_POINTER; + + iter->Init(range); + nsCOMPtr content; + nsAutoString firstValue, theValue; + iter->CurrentNode(getter_AddRefs(content)); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + //if (gNoisy) { printf(" checking node %p\n", content.get()); } + nsCOMPtrtext; + text = do_QueryInterface(content); + PRBool skipNode = PR_FALSE; + if (text) + { + if (!isCollapsed && first && firstNodeInRange) + { + firstNodeInRange = PR_FALSE; + PRInt32 startOffset; + range->GetStartOffset(&startOffset); + PRUint32 count; + text->GetLength(&count); + if (startOffset==(PRInt32)count) + { + //if (gNoisy) { printf(" skipping node %p\n", content.get()); } + skipNode = PR_TRUE; + } + } + } + else + { // handle non-text leaf nodes here + PRBool canContainChildren; + content->CanContainChildren(canContainChildren); + if (canContainChildren) + { + //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } + skipNode = PR_TRUE; + } + else { + //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } + } + } + if (!skipNode) + { + nsCOMPtrnode; + node = do_QueryInterface(content); + if (node) + { + PRBool isSet; + nsCOMPtrresultNode; + if (first) + { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue); + aFirst = isSet; + first = PR_FALSE; + if (outValue) *outValue = firstValue; + } + else + { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue); + if (firstValue != theValue) + aAll = PR_FALSE; + } + + if (isSet) { + aAny = PR_TRUE; + } + else { + aAll = PR_FALSE; + } + } + } + result = iter->Next(); + if (NS_FAILED(result)) + break; + iter->CurrentNode(getter_AddRefs(content)); + } + } + if (!aAny) + { // make sure that if none of the selection is set, we don't report all is set + aAll = PR_FALSE; + } + //if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); } + return result; +} + + +NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties() +{ + return RemoveInlinePropertyImpl(nsnull, nsnull); +} + +NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) +{ + return RemoveInlinePropertyImpl(aProperty, aAttribute); +} + +nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsString *aAttribute) +{ + if (!mRules) return NS_ERROR_NOT_INITIALIZED; + ForceCompositionEnd(); + + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selPriv(do_QueryInterface(selection)); + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + + // For links, aProperty uses "href", use "a" instead + if (aProperty == nsIEditProperty::href) + aProperty = nsIEditProperty::a; + + if (aProperty) return mTypeInState->ClearProp(aProperty, *aAttribute); + else return mTypeInState->ClearAllProps(); + } + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + nsAutoTxnsConserveSelection dontSpazMySelection(this); + + PRBool cancel, handled; + nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (!cancel && !handled) + { + // get selection range enumerator + nsCOMPtr enumerator; + res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + + nsCOMPtr range( do_QueryInterface(currentItem) ); + + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // remove this style from ancestors of our range endpoints, + // splitting them as appropriate + res = SplitStyleAboveRange(range, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + // we're done with this range! + } + else + { + // not the easy case. range not contained in single text node. + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + iter->Init(range); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; + } + + // loop through the list, remove the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = RemoveStyleInside(node, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + enumerator->Next(); + } + } + if (!cancel) + { + // post-process + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + return res; +} + +NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize() +{ + return RelativeFontChange(1); +} + +NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize() +{ + return RelativeFontChange(-1); +} + +nsresult +nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) +{ + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + + ForceCompositionEnd(); + + // Get the selection + nsCOMPtrselection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + nsCOMPtr selPriv(do_QueryInterface(selection)); + // Is the selection collapsed? + PRBool bCollapsed; + res = selection->GetIsCollapsed(&bCollapsed); + if (NS_FAILED(res)) return res; + + // if it's collapsed set typing state + if (bCollapsed) + { + nsCOMPtr atom; + if (aSizeChange==1) atom = nsIEditProperty::big; + else atom = nsIEditProperty::small; + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return mTypeInState->SetProp(atom, nsAutoString(), nsAutoString()); + } + + // wrap with txn batching, rules sniffing, and selection preservation code + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + nsAutoTxnsConserveSelection dontSpazMySelection(this); + + // get selection range enumerator + nsCOMPtr enumerator; + res = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + + nsCOMPtr range( do_QueryInterface(currentItem) ); + + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartContainer(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndContainer(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset); + if (NS_FAILED(res)) return res; + } + else + { + // not the easy case. range not contained in single text node. + // there are up to three phases here. There are all the nodes + // reported by the subtree iterator to be processed. And there + // are potentially a starting textnode and an ending textnode + // which are only partially contained by the range. + + // lets handle the nodes reported by the iterator. These nodes + // are entirely contained in the selection range. We build up + // a list of them (since doing operations on the document during + // iteration would perturb the iterator). + + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + res = iter->Init(range); + if (NS_SUCCEEDED(res)) + { + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + iter->Next(); + } + + // now that we have the list, do the font size change on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = RelativeFontChangeOnNode(aSizeChange, node); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + // now check the start and end parents of the range to see if they need to + // be seperately handled (they do if they are text nodes, due to how the + // subtree iterator works - it will not have reported them). + if (IsTextNode(startNode) && IsEditable(startNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + PRInt32 startOffset; + PRUint32 textLen; + range->GetStartOffset(&startOffset); + nodeAsText->GetLength(&textLen); + res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen); + if (NS_FAILED(res)) return res; + } + if (IsTextNode(endNode) && IsEditable(endNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(endNode); + PRInt32 endOffset; + range->GetEndOffset(&endOffset); + res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset); + if (NS_FAILED(res)) return res; + } + } + enumerator->Next(); + } + + return res; +} + +nsresult +nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, + nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset) +{ + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + if (!aTextNode) return NS_ERROR_NULL_POINTER; + + // dont need to do anything if no characters actually selected + if (aStartOffset == aEndOffset) return NS_OK; + + nsresult res = NS_OK; + nsCOMPtr tmp, node = do_QueryInterface(aTextNode); + + // do we need to split the text node? + PRUint32 textLen; + aTextNode->GetLength(&textLen); + + // -1 is a magic value meaning to the end of node + if (aEndOffset == -1) aEndOffset = textLen; + + if ( (PRUint32)aEndOffset != textLen ) + { + // we need to split off back of text node + res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + node = tmp; // remember left node + } + if ( aStartOffset ) + { + // we need to split off front of text node + res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + } + + // reparent the node inside font node with appropriate relative size + res = InsertContainerAbove(node, address_of(tmp), NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small")); + return res; +} + + +nsresult +nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, + nsIDOMNode *aNode) +{ + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + if (!aNode) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsCOMPtr tmp; + nsAutoString tag; + if (aSizeChange == 1) tag.AssignWithConversion("big"); + else tag.AssignWithConversion("small"); + + // is this node a text node? + if (IsTextNode(aNode)) + { + res = InsertContainerAbove(aNode, address_of(tmp), tag); + return res; + } + // is it the opposite of what we want? + if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) || + ((aSizeChange == -1) && nsHTMLEditUtils::IsBig(aNode)) ) + { + // in that case, just remove this node and pull up the children + res = RemoveContainer(aNode); + return res; + } + // can it be put inside a "big" or "small"? + if (TagCanContain(tag, aNode)) + { + // ok, chuck it in. + res = InsertContainerAbove(aNode, address_of(tmp), tag); + return res; + } + // none of the above? then cycle through the children. + // MOOSE: we should group the children together if possible + // into a single "big" or "small". For the moment they are + // each getting their own. + nsCOMPtr childNodes; + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + PRInt32 j; + PRUint32 childCount; + childNodes->GetLength(&childCount); + for (j=0 ; j < (PRInt32)childCount; j++) + { + nsCOMPtr childNode; + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = RelativeFontChangeOnNode(aSizeChange, childNode); + if (NS_FAILED(res)) return res; + } + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace) +{ + aMixed = PR_TRUE; + outFace.AssignWithConversion(""); + + nsresult res; + nsAutoString faceStr; faceStr.AssignWithConversion("face"); + PRBool first, any, all; + + res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + return res; + } + + res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + nsIEditProperty::tt->ToString(outFace); + } + + if (!any) + { + // there was no font face attrs of any kind. We are in normal font. + outFace.AssignWithConversion(""); + aMixed = PR_FALSE; + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetFontColorState(PRBool &aMixed, nsString &aOutColor) +{ + aMixed = PR_TRUE; + aOutColor.AssignWithConversion(""); + + nsresult res; + nsAutoString colorStr; colorStr.AssignWithConversion("color"); + PRBool first, any, all; + + res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &colorStr, nsnull, first, any, all, &aOutColor); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + return res; + } + + if (!any) + { + // there was no font color attrs of any kind.. + aOutColor.AssignWithConversion(""); + aMixed = PR_FALSE; + } + return res; +} + diff --git a/mozilla/editor/libeditor/html/nsIHTMLEditRules.h b/mozilla/editor/libeditor/html/nsIHTMLEditRules.h index f01abd3b109..4289b52fbfe 100644 --- a/mozilla/editor/libeditor/html/nsIHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsIHTMLEditRules.h @@ -28,6 +28,8 @@ 0xa6cf9121, 0x15b3, 0x11d2, \ {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} } +#include "nsIHTMLEditor.h" + class nsIHTMLEditRules : public nsISupports { public: diff --git a/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp b/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp index 5497194948f..e48410b3fc7 100644 --- a/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp +++ b/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp @@ -21,7 +21,7 @@ * Pierre Phaneuf */ #include "nsEditorEventListeners.h" -#include "nsIPlaintextEditor.h" +#include "nsIHTMLEditor.h" #include "nsEditor.h" #include "nsVoidArray.h" #include "nsString.h" @@ -178,8 +178,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) // if we are readonly or disabled, then do nothing. if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) { - if (flags & nsIHTMLEditor::eEditorReadonlyMask || - flags & nsIHTMLEditor::eEditorDisabledMask) + if (flags & nsIPlaintextEditor::eEditorReadonlyMask || + flags & nsIPlaintextEditor::eEditorDisabledMask) return NS_OK; } else @@ -246,7 +246,7 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) break; case nsIDOMKeyEvent::DOM_VK_TAB: - if ((flags & nsIHTMLEditor::eEditorSingleLineMask)) + if ((flags & nsIPlaintextEditor::eEditorSingleLineMask)) return NS_OK; // let it be used for focus switching // else we insert the tab straight through @@ -257,9 +257,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_RETURN: case nsIDOMKeyEvent::DOM_VK_ENTER: - if (!(flags & nsIHTMLEditor::eEditorSingleLineMask)) + if (!(flags & nsIPlaintextEditor::eEditorSingleLineMask)) { - //htmlEditor->InsertBreak(); textEditor->HandleKeyPress(keyEvent); ScrollSelectionIntoView(mEditor); aKeyEvent->PreventDefault(); // consumed @@ -642,8 +641,8 @@ nsTextEditorDragListener::DragOver(nsIDOMEvent* aDragEvent) if ( dragSession ) { PRUint32 flags; if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) { - if ((flags & nsIHTMLEditor::eEditorDisabledMask) || - (flags & nsIHTMLEditor::eEditorReadonlyMask)) { + if ((flags & nsIPlaintextEditor::eEditorDisabledMask) || + (flags & nsIPlaintextEditor::eEditorReadonlyMask)) { dragSession->SetCanDrop(PR_FALSE); return NS_OK; } @@ -1041,7 +1040,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) PRUint32 flags; aEvent->PreventBubble(); mEditor->GetFlags(&flags); - if (! (flags & nsIHTMLEditor::eEditorDisabledMask)) + if (! (flags & nsIPlaintextEditor::eEditorDisabledMask)) { // only enable caret and selection if the editor is not disabled nsCOMPtreditor = do_QueryInterface(mEditor); if (editor) @@ -1050,7 +1049,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) editor->GetSelectionController(getter_AddRefs(selCon)); if (selCon) { - if (! (flags & nsIHTMLEditor::eEditorReadonlyMask)) + if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask)) { // only enable caret if the editor is not readonly PRInt32 pixelWidth; nsresult result; @@ -1059,7 +1058,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) if (NS_SUCCEEDED(result) && look) { - if(flags & nsIHTMLEditor::eEditorSingleLineMask) + if(flags & nsIPlaintextEditor::eEditorSingleLineMask) look->GetMetric(nsILookAndFeel::eMetric_SingleLineCaretWidth, pixelWidth); else look->GetMetric(nsILookAndFeel::eMetric_MultiLineCaretWidth, pixelWidth); @@ -1109,11 +1108,11 @@ nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent) if (selCon) { selCon->SetCaretEnabled(PR_FALSE); - if((flags & nsIHTMLEditor::eEditorWidgetMask) || - (flags & nsIHTMLEditor::eEditorPasswordMask) || - (flags & nsIHTMLEditor::eEditorReadonlyMask) || - (flags & nsIHTMLEditor::eEditorDisabledMask) || - (flags & nsIHTMLEditor::eEditorFilterInputMask)) + if((flags & nsIPlaintextEditor::eEditorWidgetMask) || + (flags & nsIPlaintextEditor::eEditorPasswordMask) || + (flags & nsIPlaintextEditor::eEditorReadonlyMask) || + (flags & nsIPlaintextEditor::eEditorDisabledMask) || + (flags & nsIPlaintextEditor::eEditorFilterInputMask)) { selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);//hide but do NOT turn off } diff --git a/mozilla/editor/libeditor/text/nsEditorEventListeners.h b/mozilla/editor/libeditor/text/nsEditorEventListeners.h index 39db78bdd2a..f1eb28e711b 100644 --- a/mozilla/editor/libeditor/text/nsEditorEventListeners.h +++ b/mozilla/editor/libeditor/text/nsEditorEventListeners.h @@ -35,7 +35,7 @@ #include "nsIDOMFocusListener.h" #include "nsIEditor.h" -#include "nsIHTMLEditor.h" +#include "nsIPlaintextEditor.h" /** The nsTextEditorKeyListener public nsIDOMKeyListener * This class will delegate events to its editor according to the translation diff --git a/mozilla/editor/libeditor/text/nsPlaintextDataTransfer.cpp b/mozilla/editor/libeditor/text/nsPlaintextDataTransfer.cpp new file mode 100644 index 00000000000..ae695c372ab --- /dev/null +++ b/mozilla/editor/libeditor/text/nsPlaintextDataTransfer.cpp @@ -0,0 +1,582 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ +#include "nsICaret.h" + + +#include "nsPlaintextEditor.h" +#include "nsHTMLEditUtils.h" + +#include "nsEditorEventListeners.h" + +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines +#include "nsIIndependentSelection.h" //domselections answer to frameselection + + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// netwerk +#include "nsIURI.h" +#include "nsNetUtil.h" + +// Drag & Drop, Clipboard +#include "nsWidgetsCID.h" +#include "nsIClipboard.h" +#include "nsITransferable.h" +#include "nsIDragService.h" +#include "nsIDOMNSUIEvent.h" + +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" +#include "nsIPref.h" +const PRUnichar nbsp = 160; + +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } + + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); +static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); +static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); + +// Drag & Drop, Clipboard Support +static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); +static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); +static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); +static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); +// private clipboard data flavors for html copy/paste +#define kHTMLContext "text/_moz_htmlcontext" +#define kHTMLInfo "text/_moz_htmlinfo" + + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + +NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable) +{ + // Create generic Transferable for getting the data + nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + (void**)transferable); + if (NS_FAILED(rv)) + return rv; + + // Get the nsITransferable interface for getting the data from the clipboard + if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime); + return NS_OK; +} + +NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *transferable) +{ + nsresult rv = NS_OK; + char* bestFlavor = nsnull; + nsCOMPtr genericDataObj; + PRUint32 len = 0; + if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) ) + { + nsAutoTxnsConserveSelection dontSpazMySelection(this); + nsAutoString flavor, stuffToPaste; + flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() + if (flavor.EqualsWithConversion(kUnicodeMime)) + { + nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + if (textDataObj && len > 0) + { + PRUnichar* text = nsnull; + textDataObj->ToString ( &text ); + stuffToPaste.Assign ( text, len / 2 ); + nsAutoEditBatch beginBatching(this); + rv = InsertText(stuffToPaste.GetUnicode()); + if (text) + nsMemory::Free(text); + } + } + } + nsCRT::free(bestFlavor); + + // Try to scroll the selection into view if the paste/drop succeeded + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr selCon; + if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon) + selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION); + } + + return rv; +} + +NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) +{ + ForceCompositionEnd(); + + nsresult rv; + NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr dragSession(do_QueryInterface(dragService)); + + if (!dragSession) return NS_OK; + + // Get the nsITransferable interface for getting the data from the drop + nsCOMPtr trans; + rv = PrepareTransferable(getter_AddRefs(trans)); + if (NS_FAILED(rv)) return rv; + if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL? + + PRUint32 numItems = 0; + rv = dragSession->GetNumDropItems(&numItems); + if (NS_FAILED(rv)) return rv; + + // Combine any deletion and drop insertion into one transaction + nsAutoEditBatch beginBatching(this); + + PRUint32 i; + PRBool doPlaceCaret = PR_TRUE; + for (i = 0; i < numItems; ++i) + { + rv = dragSession->GetData(trans, i); + if (NS_FAILED(rv)) return rv; + if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail? + + if ( doPlaceCaret ) + { + // check if the user pressed the key to force a copy rather than a move + // if we run into problems here, we'll just assume the user doesn't want a copy + PRBool userWantsCopy = PR_FALSE; + + nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent)); + if (!nsuiEvent) return NS_ERROR_FAILURE; + + nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) ); + if (mouseEvent) + +#ifdef XP_MAC + mouseEvent->GetAltKey(&userWantsCopy); +#else + mouseEvent->GetCtrlKey(&userWantsCopy); +#endif + // Source doc is null if source is *not* the current editor document + nsCOMPtr srcdomdoc; + rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc)); + if (NS_FAILED(rv)) return rv; + + // Current doc is destination + nsCOMPtrdestdomdoc; + rv = GetDocument(getter_AddRefs(destdomdoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + if (!selection) return NS_ERROR_FAILURE; + + PRBool isCollapsed; + rv = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(rv)) return rv; + + // Parent and offset under the mouse cursor + nsCOMPtr newSelectionParent; + PRInt32 newSelectionOffset = 0; + rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent)); + if (NS_FAILED(rv)) return rv; + if (!newSelectionParent) return NS_ERROR_FAILURE; + + rv = nsuiEvent->GetRangeOffset(&newSelectionOffset); + if (NS_FAILED(rv)) return rv; + /* Creating a range to store insert position because when + we delete the selection, range gravity will make sure the insertion + point is in the correct place */ + nsCOMPtr destinationRange; + rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange)); + if (NS_FAILED(rv)) + return rv; + if(!destinationRange) + return NS_ERROR_FAILURE; + + // We never have to delete if selection is already collapsed + PRBool deleteSelection = PR_FALSE; + PRBool cursorIsInSelection = PR_FALSE; + + // Check if mouse is in the selection + if (!isCollapsed) + { + PRInt32 rangeCount; + rv = selection->GetRangeCount(&rangeCount); + if (NS_FAILED(rv)) + return rv?rv:NS_ERROR_FAILURE; + + for (PRInt32 j = 0; j < rangeCount; j++) + { + nsCOMPtr range; + + rv = selection->GetRangeAt(j, getter_AddRefs(range)); + if (NS_FAILED(rv) || !range) + continue;//dont bail yet, iterate through them all + + nsCOMPtr nsrange(do_QueryInterface(range)); + if (NS_FAILED(rv) || !nsrange) + continue;//dont bail yet, iterate through them all + + rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection); + if(cursorIsInSelection) + break; + } + if (cursorIsInSelection) + { + // Dragging within same doc can't drop on itself -- leave! + // (We shouldn't get here - drag event shouldn't have started if over selection) + if (srcdomdoc == destdomdoc) + return NS_OK; + + // Dragging from another window onto a selection + // XXX Decision made to NOT do this, + // note that 4.x does replace if dropped on + //deleteSelection = PR_TRUE; + } + else + { + // We are NOT over the selection + if (srcdomdoc == destdomdoc) + { + // Within the same doc: delete if user doesn't want to copy + deleteSelection = !userWantsCopy; + } + else + { + // Different source doc: Don't delete + deleteSelection = PR_FALSE; + } + } + } + + if (deleteSelection) + { + rv = DeleteSelection(eNone); + if (NS_FAILED(rv)) return rv; + } + + // If we deleted the selection because we dropped from another doc, + // then we don't have to relocate the caret (insert at the deletion point) + if (!(deleteSelection && srcdomdoc != destdomdoc)) + { + // Move the selection to the point under the mouse cursor + rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent)); + if (NS_FAILED(rv)) + return rv; + if(!newSelectionParent) + return NS_ERROR_FAILURE; + + rv = destinationRange->GetStartOffset(&newSelectionOffset); + if (NS_FAILED(rv)) + return rv; + selection->Collapse(newSelectionParent, newSelectionOffset); + } + // We have to figure out whether to delete and relocate caret only once + doPlaceCaret = PR_FALSE; + } + + rv = InsertTextFromTransferable(trans); + } + + return rv; +} + +NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag) +{ + /* we really should be checking the XY coordinates of the mouseevent and ensure that + * that particular point is actually within the selection (not just that there is a selection) + */ + aCanDrag = PR_FALSE; + + // KLUDGE to work around bug 50703 + // After double click and object property editing, + // we get a spurious drag event + if (mIgnoreSpuriousDragEvent) + { + mIgnoreSpuriousDragEvent = PR_FALSE; + return NS_OK; + } + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + // if we are collapsed, we have no selection so nothing to drag + if ( isCollapsed ) + return NS_OK; + + nsCOMPtr eventTarget; + res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget)); + if (NS_FAILED(res)) return res; + if ( eventTarget ) + { + nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget); + if ( eventTargetDomNode ) + { + PRBool amTargettedCorrectly = PR_FALSE; + res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly); + if (NS_FAILED(res)) return res; + + aCanDrag = amTargettedCorrectly; + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent) +{ + nsresult rv; + + nsCOMPtr eventTarget; + rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget)); + if (NS_FAILED(rv)) return rv; + nsCOMPtr domnode = do_QueryInterface(eventTarget); + + /* get the selection to be dragged */ + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(rv)) return rv; + + /* create an array of transferables */ + nsCOMPtr transferableArray; + NS_NewISupportsArray(getter_AddRefs(transferableArray)); + if (transferableArray == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + + /* get the drag service */ + NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv); + if (NS_FAILED(rv)) return rv; + + /* create html flavor transferable */ + nsCOMPtr trans; + rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, + NS_GET_IID(nsITransferable), + getter_AddRefs(trans)); + if (NS_FAILED(rv)) return rv; + if ( !trans ) return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr domdoc; + rv = GetDocument(getter_AddRefs(domdoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr doc = do_QueryInterface(domdoc); + if (doc) + { + nsCOMPtr docEncoder; + + docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html"); + NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); + + docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0); + docEncoder->SetSelection(selection); + + nsAutoString buffer; + + rv = docEncoder->EncodeToString(buffer); + + if (NS_FAILED(rv)) + return rv; + + if ( !buffer.IsEmpty() ) + { + nsCOMPtr htmlConverter; + rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter), + getter_AddRefs(htmlConverter)); + if (NS_FAILED(rv)) return rv; + if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr dataWrapper; + rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper)); + if (NS_FAILED(rv)) return rv; + if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY; + + rv = trans->AddDataFlavor(kHTMLMime); + if (NS_FAILED(rv)) return rv; + rv = trans->SetConverter(htmlConverter); + if (NS_FAILED(rv)) return rv; + + rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) ); + if (NS_FAILED(rv)) return rv; + + // QI the data object an |nsISupports| so that when the transferable holds + // onto it, it will addref the correct interface. + nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) ); + rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2); + if (NS_FAILED(rv)) return rv; + + /* add the transferable to the array */ + rv = transferableArray->AppendElement(trans); + if (NS_FAILED(rv)) return rv; + + /* invoke drag */ + unsigned int flags; + // in some cases we'll want to cut rather than copy... hmmmmm... + // if ( wantToCut ) + // flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE; + // else + flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; + + rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags); + if (NS_FAILED(rv)) return rv; + + aDragEvent->PreventBubble(); + } + } + + return rv; +} + +NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType) +{ + ForceCompositionEnd(); + + // Get Clipboard Service + nsresult rv; + NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv ); + if ( NS_FAILED(rv) ) + return rv; + + // Get the nsITransferable interface for getting the data from the clipboard + nsCOMPtr trans; + rv = PrepareTransferable(getter_AddRefs(trans)); + if (NS_SUCCEEDED(rv) && trans) + { + // Get the Data from the clipboard + if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) + { + rv = InsertTextFromTransferable(trans); + } + } + + return rv; +} + + +NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste) +{ + aCanPaste = PR_FALSE; + + nsresult rv; + NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv); + if (NS_FAILED(rv)) return rv; + + // the flavors that we can deal with + char* textEditorFlavors[] = { kUnicodeMime, nsnull }; + + nsCOMPtr flavorsList; + rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList)); + if (NS_FAILED(rv)) return rv; + + PRUint32 editorFlags; + GetFlags(&editorFlags); + + // add the flavors for text editors + for (char** flavor = textEditorFlavors; *flavor; flavor++) + { + nsCOMPtr flavorString; + nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString)); + if (flavorString) + { + flavorString->SetData(*flavor); + flavorsList->AppendElement(flavorString); + } + } + + PRBool haveFlavors; + rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors); + if (NS_FAILED(rv)) return rv; + + aCanPaste = haveFlavors; + return NS_OK; +} diff --git a/mozilla/editor/libeditor/text/nsPlaintextEditor.cpp b/mozilla/editor/libeditor/text/nsPlaintextEditor.cpp new file mode 100644 index 00000000000..6c4d2867e85 --- /dev/null +++ b/mozilla/editor/libeditor/text/nsPlaintextEditor.cpp @@ -0,0 +1,1808 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + */ + + +#include "nsPlaintextEditor.h" +#include "nsICaret.h" +#include "nsHTMLEditUtils.h" +#include "nsTextEditRules.h" +#include "nsEditorEventListeners.h" +#include "nsIEditActionListener.h" +#include "nsIDOMText.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" +#include "nsIDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMMouseEvent.h" +#include "nsISelection.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsISelectionController.h" + +#include "nsIIndependentSelection.h" //domselections answer to frameselection + +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIEnumerator.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsEditorCID.h" +#include "nsLayoutCID.h" +#include "nsIDOMRange.h" +#include "nsIDOMNSRange.h" +#include "nsISupportsArray.h" +#include "nsVoidArray.h" +#include "nsFileSpec.h" +#include "nsIFile.h" +#include "nsIURL.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsWidgetsCID.h" +#include "nsIDocumentEncoder.h" +#include "nsIDOMDocumentFragment.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" +#include "nsIImage.h" +#include "nsISupportsPrimitives.h" +#include "InsertTextTxn.h" + +// netwerk +#include "nsIURI.h" +#include "nsNetUtil.h" + +// Transactionas +#include "PlaceholderTxn.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" +#include "nsIPref.h" +#include "nsStyleConsts.h" +#include "nsIStyleContext.h" + +const PRUnichar nbsp = 160; + +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } + + +static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); +static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); +static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); +static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); + +#if defined(NS_DEBUG) && defined(DEBUG_buster) +static PRBool gNoisy = PR_FALSE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + +nsIAtom *nsPlaintextEditor::gTypingTxnName; +nsIAtom *nsPlaintextEditor::gIMETxnName; +nsIAtom *nsPlaintextEditor::gDeleteTxnName; + +// prototype for rules creation shortcut +nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); + +nsPlaintextEditor::nsPlaintextEditor() +: nsEditor() +, mIgnoreSpuriousDragEvent(PR_FALSE) +, mRules(nsnull) +, mIsComposing(PR_FALSE) +, mMaxTextLength(-1) +, mInitTriggerCounter(0) +{ +// Done in nsEditor +// NS_INIT_REFCNT(); + if (!gTypingTxnName) + gTypingTxnName = NS_NewAtom("Typing"); + else + NS_ADDREF(gTypingTxnName); + if (!gIMETxnName) + gIMETxnName = NS_NewAtom("IME"); + else + NS_ADDREF(gIMETxnName); + if (!gDeleteTxnName) + gDeleteTxnName = NS_NewAtom("Deleting"); + else + NS_ADDREF(gDeleteTxnName); +} + +nsPlaintextEditor::~nsPlaintextEditor() +{ + /* first, delete the transaction manager if there is one. + this will release any remaining transactions. + this is important because transactions can hold onto the atoms (gTypingTxnName, ...) + and to make the optimization (holding refcounted statics) work correctly, + the editor instance needs to hold the last refcount. + If you get this wrong, expect to deref a garbage gTypingTxnName pointer if you bring up a second editor. + */ + if (mTxnMgr) { + mTxnMgr = 0; + } + nsrefcnt refCount=0; + if (gTypingTxnName) // we addref'd in the constructor + { // want to release it without nulling out the pointer. + refCount = gTypingTxnName->Release(); + if (0==refCount) { + gTypingTxnName = nsnull; + } + } + + if (gIMETxnName) // we addref'd in the constructor + { // want to release it without nulling out the pointer. + refCount = gIMETxnName->Release(); + if (0==refCount) { + gIMETxnName = nsnull; + } + } + + if (gDeleteTxnName) // we addref'd in the constructor + { // want to release it without nulling out the pointer. + refCount = gDeleteTxnName->Release(); + if (0==refCount) { + gDeleteTxnName = nsnull; + } + } + + // remove the rules as an action listener. Else we get a bad ownership loop later on. + // it's ok if the rules aren't a listener; we ignore the error. + nsCOMPtr mListener = do_QueryInterface(mRules); + RemoveEditActionListener(mListener); + + nsCOMPtr erP; + nsresult result = GetDOMEventReceiver(getter_AddRefs(erP)); + if (NS_SUCCEEDED(result) && erP) + { + if (mKeyListenerP) { + erP->RemoveEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener)); + } + if (mMouseListenerP) { + erP->RemoveEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener)); + } + if (mTextListenerP) { + erP->RemoveEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener)); + } + if (mCompositionListenerP) { + erP->RemoveEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener)); + } + if (mFocusListenerP) { + erP->RemoveEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener)); + } + if (mDragListenerP) { + erP->RemoveEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener)); + } + } + else + NS_NOTREACHED("~nsTextEditor"); + +} + +NS_IMPL_ADDREF_INHERITED(nsPlaintextEditor, nsEditor) +NS_IMPL_RELEASE_INHERITED(nsPlaintextEditor, nsEditor) + + +NS_IMETHODIMP nsPlaintextEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + + *aInstancePtr = nsnull; + + if (aIID.Equals(NS_GET_IID(nsIPlaintextEditor))) { + *aInstancePtr = NS_STATIC_CAST(nsIPlaintextEditor*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + + return nsEditor::QueryInterface(aIID, aInstancePtr); +} + + +NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc, + nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags) +{ + NS_PRECONDITION(aDoc && aPresShell, "bad arg"); + if (!aDoc || !aPresShell) + return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK, rulesRes = NS_OK; + + if (1) + { + // block to scope nsAutoEditInitRulesTrigger + nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes); + + // Init the base editor + res = nsEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags); + } + + if (NS_FAILED(rulesRes)) return rulesRes; + return res; +} + +void +nsPlaintextEditor::BeginEditorInit() +{ + mInitTriggerCounter++; +} + +nsresult +nsPlaintextEditor::EndEditorInit() +{ + nsresult res = NS_OK; + NS_PRECONDITION(mInitTriggerCounter > 0, "ended editor init before we began?"); + mInitTriggerCounter--; + if (mInitTriggerCounter == 0) + { + res = InitRules(); + if (NS_SUCCEEDED(res)) + EnableUndo(PR_TRUE); + } + return res; +} + +NS_IMETHODIMP +nsPlaintextEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) +{ + nsresult result; + + result = nsEditor::SetDocumentCharacterSet(characterSet); + + // update META charset tag + if (NS_SUCCEEDED(result)) { + nsCOMPtrdomdoc; + result = GetDocument(getter_AddRefs(domdoc)); + if (NS_SUCCEEDED(result) && domdoc) { + nsAutoString newMetaString; + nsCOMPtrmetaList; + nsCOMPtrmetaNode; + nsCOMPtrmetaElement; + PRBool newMetaCharset = PR_TRUE; + + // get a list of META tags + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("meta"), getter_AddRefs(metaList)); + if (NS_SUCCEEDED(result) && metaList) { + PRUint32 listLength = 0; + (void) metaList->GetLength(&listLength); + + for (PRUint32 i = 0; i < listLength; i++) { + metaList->Item(i, getter_AddRefs(metaNode)); + if (!metaNode) continue; + metaElement = do_QueryInterface(metaNode); + if (!metaElement) continue; + + const NS_ConvertASCIItoUCS2 content("charset="); + nsString currentValue; + + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("http-equiv"), currentValue))) continue; + + if (kNotFound != currentValue.Find("content-type", PR_TRUE)) { + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("content"), currentValue))) continue; + + PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE); + if (kNotFound != offset) { + currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html) + newMetaString.Append(content); + newMetaString.Append(characterSet); + result = nsEditor::SetAttribute(metaElement, NS_ConvertASCIItoUCS2("content"), newMetaString); + if (NS_SUCCEEDED(result)) + newMetaCharset = PR_FALSE; + break; + } + } + } + } + + if (newMetaCharset) { + nsCOMPtrheadList; + nsCOMPtrheadNode; + nsCOMPtrresultNode; + + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"),getter_AddRefs(headList)); + if (NS_SUCCEEDED(result) && headList) { + headList->Item(0, getter_AddRefs(headNode)); + if (headNode) { + // Create a new meta charset tag + // can't use |NS_LITERAL_STRING| here until |CreateNode| is fixed to accept readables + result = CreateNode(NS_ConvertASCIItoUCS2("meta"), headNode, 0, getter_AddRefs(resultNode)); + if (NS_FAILED(result)) + return NS_ERROR_FAILURE; + + // Set attributes to the created element + if (resultNode && nsCRT::strlen(characterSet) > 0) { + metaElement = do_QueryInterface(resultNode); + if (metaElement) { + // not undoable, undo should undo CreateNode + result = metaElement->SetAttribute(NS_LITERAL_STRING("http-equiv"), NS_LITERAL_STRING("Content-Type")); + if (NS_SUCCEEDED(result)) { + newMetaString.AssignWithConversion("text/html;charset="); + newMetaString.Append(characterSet); + // not undoable, undo should undo CreateNode + result = metaElement->SetAttribute(NS_LITERAL_STRING("content"), newMetaString); + } + } + } + } + } + } + } + } + + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::PostCreate() +{ + nsresult result = InstallEventListeners(); + if (NS_FAILED(result)) return result; + + result = nsEditor::PostCreate(); + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::InstallEventListeners() +{ + NS_ASSERTION(mDocWeak, "no document set on this editor"); + if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED; + + nsresult result; + // get a key listener + result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // get a mouse listener + result = NS_NewEditorMouseListener(getter_AddRefs(mMouseListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // get a text listener + result = NS_NewEditorTextListener(getter_AddRefs(mTextListenerP),this); + if (NS_FAILED(result)) { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a composition listener + result = NS_NewEditorCompositionListener(getter_AddRefs(mCompositionListenerP),this); + if (NS_FAILED(result)) { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a drag listener + result = NS_NewEditorDragListener(getter_AddRefs(mDragListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // get a focus listener + result = NS_NewEditorFocusListener(getter_AddRefs(mFocusListenerP), this); + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + nsCOMPtr erP; + result = GetDOMEventReceiver(getter_AddRefs(erP)); + + //end hack + if (NS_FAILED(result)) { + HandleEventListenerError(); + return result; + } + + // register the event listeners with the DOM event reveiver + result = erP->AddEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register key listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register mouse listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register focus listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register text listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register composition listener"); + if (NS_SUCCEEDED(result)) + { + result = erP->AddEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener)); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register drag listener"); + } + } + } + } + } + if (NS_FAILED(result)) { + HandleEventListenerError(); + } + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetFlags(PRUint32 *aFlags) +{ + if (!mRules || !aFlags) { return NS_ERROR_NULL_POINTER; } + return mRules->GetFlags(aFlags); +} + + +NS_IMETHODIMP +nsPlaintextEditor::SetFlags(PRUint32 aFlags) +{ + if (!mRules) { return NS_ERROR_NULL_POINTER; } + return mRules->SetFlags(aFlags); +} + + +NS_IMETHODIMP nsPlaintextEditor::InitRules() +{ + // instantiate the rules for this text editor + nsresult res = NS_ERROR_FAILURE; + res = NS_NewTextEditRules(getter_AddRefs(mRules)); + if (NS_FAILED(res)) return res; + if (!mRules) return NS_ERROR_UNEXPECTED; + res = mRules->Init(this, mFlags); + + return res; +} + + +PRBool nsPlaintextEditor::IsModifiable() +{ + PRUint32 flags; + if (NS_SUCCEEDED(GetFlags(&flags))) + return ((flags & eEditorReadonlyMask) == 0); + else + return PR_FALSE; +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIHTMLEditor methods +#pragma mark - +#endif + +NS_IMETHODIMP nsPlaintextEditor::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent) +{ + PRUint32 keyCode, character; + PRBool isShift, ctrlKey, altKey, metaKey; + + if (!aKeyEvent) return NS_ERROR_NULL_POINTER; + + if (NS_SUCCEEDED(aKeyEvent->GetKeyCode(&keyCode)) && + NS_SUCCEEDED(aKeyEvent->GetShiftKey(&isShift)) && + NS_SUCCEEDED(aKeyEvent->GetCtrlKey(&ctrlKey)) && + NS_SUCCEEDED(aKeyEvent->GetAltKey(&altKey)) && + NS_SUCCEEDED(aKeyEvent->GetMetaKey(&metaKey))) + { + aKeyEvent->GetCharCode(&character); + if (keyCode == nsIDOMKeyEvent::DOM_VK_RETURN + || keyCode == nsIDOMKeyEvent::DOM_VK_ENTER) + { + nsString empty; + return TypedText(empty.GetUnicode(), eTypedBreak); + } + else if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE) + { + // pass escape keypresses through as empty strings: needed for ime support + nsString empty; + return TypedText(empty.GetUnicode(), eTypedText); + } + + if (character && !altKey && !ctrlKey && !isShift && !metaKey) + { + nsAutoString key(character); + return TypedText(key.GetUnicode(), eTypedText); + } + } + return NS_ERROR_FAILURE; +} + +/* This routine is needed to provide a bottleneck for typing for logging + purposes. Can't use EditorKeyPress() (above) for that since it takes + a nsIDOMUIEvent* parameter. So instead we pass enough info through + to TypedText() to determine what action to take, but without passing + an event. + */ +NS_IMETHODIMP nsPlaintextEditor::TypedText(const PRUnichar* aString, + PRInt32 aAction) +{ + nsAutoPlaceHolderBatch batch(this, gTypingTxnName); + + switch (aAction) + { + case eTypedText: + { + return InsertText(aString); + } + case eTypedBreak: + { + return InsertLineBreak(); + } + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsPlaintextEditor::CreateBRImpl(nsCOMPtr *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr *outBRNode, EDirection aSelect) +{ + if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER; + *outBRNode = nsnull; + nsresult res; + + // we need to insert a br. unfortunately, we may have to split a text node to do it. + nsCOMPtr node = *aInOutParent; + PRInt32 theOffset = *aInOutOffset; + nsCOMPtr nodeAsText = do_QueryInterface(node); + nsAutoString brType; brType.AssignWithConversion("br"); + nsCOMPtr brNode; + if (nodeAsText) + { + nsCOMPtr tmp; + PRInt32 offset; + PRUint32 len; + nodeAsText->GetLength(&len); + GetNodeLocation(node, &tmp, &offset); + if (!tmp) return NS_ERROR_FAILURE; + if (!theOffset) + { + // we are already set to go + } + else if (theOffset == (PRInt32)len) + { + // update offset to point AFTER the text node + offset++; + } + else + { + // split the text node + res = SplitNode(node, theOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + res = GetNodeLocation(node, &tmp, &offset); + if (NS_FAILED(res)) return res; + } + // create br + res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode)); + if (NS_FAILED(res)) return res; + *aInOutParent = tmp; + *aInOutOffset = offset+1; + } + else + { + res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode)); + if (NS_FAILED(res)) return res; + (*aInOutOffset)++; + } + + *outBRNode = brNode; + if (*outBRNode && (aSelect != eNone)) + { + nsCOMPtr selection; + nsCOMPtr parent; + PRInt32 offset; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsCOMPtr selPriv(do_QueryInterface(selection)); + res = GetNodeLocation(*outBRNode, &parent, &offset); + if (NS_FAILED(res)) return res; + if (aSelect == eNext) + { + // position selection after br + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(parent, offset+1); + } + else if (aSelect == ePrevious) + { + // position selection before br + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(parent, offset); + } + } + return NS_OK; +} + + +NS_IMETHODIMP nsPlaintextEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect) +{ + nsCOMPtr parent = aNode; + PRInt32 offset = aOffset; + return CreateBRImpl(&parent, &offset, outBRNode, aSelect); +} + +NS_IMETHODIMP nsPlaintextEditor::InsertBR(nsCOMPtr *outBRNode) +{ + PRBool bCollapsed; + nsCOMPtr selection; + + if (!outBRNode) return NS_ERROR_NULL_POINTER; + *outBRNode = nsnull; + + // calling it text insertion to trigger moz br treatment by rules + nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext); + + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsCOMPtr selPriv(do_QueryInterface(selection)); + res = selection->GetIsCollapsed(&bCollapsed); + if (NS_FAILED(res)) return res; + if (!bCollapsed) + { + res = DeleteSelection(nsIEditor::eNone); + if (NS_FAILED(res)) return res; + } + nsCOMPtr selNode; + PRInt32 selOffset; + res = GetStartNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + + res = CreateBR(selNode, selOffset, outBRNode); + if (NS_FAILED(res)) return res; + + // position selection after br + res = GetNodeLocation(*outBRNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(selNode, selOffset+1); + + return res; +} +nsresult nsPlaintextEditor::GetTextSelectionOffsets(nsISelection *aSelection, + PRInt32 &aOutStartOffset, + PRInt32 &aOutEndOffset) +{ + if(!aSelection) { return NS_ERROR_NULL_POINTER; } + nsresult result; + // initialize out params + aOutStartOffset = 0; // default to first char in selection + aOutEndOffset = -1; // default to total length of text in selection + + nsCOMPtr startNode, endNode, parentNode; + PRInt32 startOffset, endOffset; + aSelection->GetAnchorNode(getter_AddRefs(startNode)); + aSelection->GetAnchorOffset(&startOffset); + aSelection->GetFocusNode(getter_AddRefs(endNode)); + aSelection->GetFocusOffset(&endOffset); + + nsCOMPtr enumerator; + nsCOMPtr selection(aSelection); + nsCOMPtr selPriv(do_QueryInterface(selection)); + result = selPriv->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(result)) return result; + if (!enumerator) return NS_ERROR_NULL_POINTER; + + // don't use "result" in this block + enumerator->First(); + nsCOMPtr currentItem; + nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(findParentResult)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + range->GetCommonAncestorContainer(getter_AddRefs(parentNode)); + } + else + { + parentNode = do_QueryInterface(startNode); + } + + + return GetAbsoluteOffsetsForPoints(startNode, startOffset, + endNode, endOffset, + parentNode, + aOutStartOffset, aOutEndOffset); +} + +nsresult +nsPlaintextEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, + PRInt32 aInStartOffset, + nsIDOMNode *aInEndNode, + PRInt32 aInEndOffset, + nsIDOMNode *aInCommonParentNode, + PRInt32 &aOutStartOffset, + PRInt32 &aOutEndOffset) +{ + if(!aInStartNode || !aInEndNode || !aInCommonParentNode) + return NS_ERROR_NULL_POINTER; + + nsresult result; + // initialize out params + aOutStartOffset = 0; // default to first char in selection + aOutEndOffset = -1; // default to total length of text in selection + + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(result)) return result; + if (!iter) return NS_ERROR_NULL_POINTER; + + PRUint32 totalLength=0; + nsCOMPtrtextNode; + nsCOMPtrblockParentContent = do_QueryInterface(aInCommonParentNode); + iter->Init(blockParentContent); + // loop through the content iterator for each content node + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + textNode = do_QueryInterface(content); + if (textNode) + { + nsCOMPtrcurrentNode = do_QueryInterface(textNode); + if (!currentNode) {return NS_ERROR_NO_INTERFACE;} + if (IsEditable(currentNode)) + { + if (currentNode.get() == aInStartNode) + { + aOutStartOffset = totalLength + aInStartOffset; + } + if (currentNode.get() == aInEndNode) + { + aOutEndOffset = totalLength + aInEndOffset; + break; + } + PRUint32 length; + textNode->GetLength(&length); + totalLength += length; + } + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + if (-1==aOutEndOffset) { + aOutEndOffset = totalLength; + } + + // guarantee that aOutStartOffset <= aOutEndOffset + if (aOutEndOffset end"); + return result; +} + +nsresult +nsPlaintextEditor::GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver) +{ + if (!aEventReceiver) + return NS_ERROR_NULL_POINTER; + + *aEventReceiver = 0; + + nsCOMPtr rootElement; + + nsresult result = GetRootElement(getter_AddRefs(rootElement)); + + if (NS_FAILED(result)) + return result; + + if (!rootElement) + return NS_ERROR_FAILURE; + + // Now hack to make sure we are not anonymous content. + // If we are grab the parent of root element for our observer. + + nsCOMPtr content = do_QueryInterface(rootElement); + + if (content) + { + nsCOMPtr parent; + if (NS_SUCCEEDED(content->GetParent(*getter_AddRefs(parent))) && parent) + { + PRInt32 index; + if (NS_FAILED(parent->IndexOf(content, index)) || index < 0 ) + { + rootElement = do_QueryInterface(parent); //this will put listener on the form element basically + result = rootElement->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), (void **)aEventReceiver); + } + else + rootElement = 0; // Let the event receiver work on the document instead of the root element + } + } + else + rootElement = 0; + + if (!rootElement && mDocWeak) + { + // Don't use getDocument here, because we have no way of knowing if + // Init() was ever called. So we need to get the document ourselves, + // if it exists. + + nsCOMPtr domdoc = do_QueryReferent(mDocWeak); + + if (!domdoc) + return NS_ERROR_FAILURE; + + result = domdoc->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), (void **)aEventReceiver); + } + + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::CollapseSelectionToStart() +{ + nsCOMPtr bodyElement; + nsresult res = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtr bodyNode = do_QueryInterface(bodyElement); + nsCOMPtr selection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + return selection->Collapse(bodyNode,0); +} + +NS_IMETHODIMP nsPlaintextEditor::DeleteSelection(nsIEditor::EDirection aAction) +{ + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel, handled; + nsresult result; + + // delete placeholder txns merge. + nsAutoPlaceHolderBatch batch(this, gDeleteTxnName); + nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); + + // If it's one of these modes, + // we have to extend the selection first. + // This needs to happen inside selection batching, + // otherwise the deleted text is autocopied to the clipboard. + if (aAction == eNextWord || aAction == ePreviousWord + || aAction == eToBeginningOfLine || aAction == eToEndOfLine) + { + if (!mSelConWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr selCont (do_QueryReferent(mSelConWeak)); + if (!selCont) + return NS_ERROR_NO_INTERFACE; + + switch (aAction) + { + case eNextWord: + result = selCont->WordMove(PR_TRUE, PR_TRUE); + // DeleteSelectionImpl doesn't handle these actions + // because it's inside batching, so don't confuse it: + aAction = eNone; + break; + case ePreviousWord: + result = selCont->WordMove(PR_FALSE, PR_TRUE); + aAction = eNone; + break; + case eToBeginningOfLine: + selCont->IntraLineMove(PR_TRUE, PR_FALSE); // try to move to end + result = selCont->IntraLineMove(PR_FALSE, PR_TRUE); // select to beginning + aAction = eNone; + break; + case eToEndOfLine: + result = selCont->IntraLineMove(PR_TRUE, PR_TRUE); + // Bugs 54449/54452: the selection jumps to the wrong place + // when deleting past a
and action is eNext or ePrev, + // so setting action to eNone makes delete-to-end marginally usable. + // aAction should really be set to eNext + aAction = eNone; + break; + default: // avoid several compiler warnings + result = NS_OK; + break; + } + if (NS_FAILED(result)) + { +#ifdef DEBUG + printf("Selection controller interface didn't work!\n"); +#endif + return result; + } + } + + // pre-process + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection); + ruleInfo.collapsedAction = aAction; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(result)) return result; + if (!cancel && !handled) + { + result = DeleteSelectionImpl(aAction); + } + if (!cancel) + { + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + + return result; +} + +NS_IMETHODIMP nsPlaintextEditor::InsertText(const PRUnichar* aStringToInsert) +{ + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel, handled; + PRInt32 theAction = nsTextEditRules::kInsertText; + PRInt32 opID = kOpInsertText; + if (mInIMEMode) + { + theAction = nsTextEditRules::kInsertTextIME; + opID = kOpInsertIMEText; + } + nsAutoPlaceHolderBatch batch(this, nsnull); + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); + + // pre-process + nsresult result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + nsAutoString resultString; + // XXX can we trust instring to outlive ruleInfo, + // XXX and ruleInfo not to refer to instring in its dtor? + nsAutoString instring(aStringToInsert); + nsTextRulesInfo ruleInfo(theAction); + ruleInfo.inString = &instring; + ruleInfo.outString = &resultString; + ruleInfo.maxLength = mMaxTextLength; + + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(result)) return result; + if (!cancel && !handled) + { + // we rely on rules code for now - no default implementation + } + if (!cancel) + { + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + return result; +} + + +NS_IMETHODIMP nsPlaintextEditor::InsertLineBreak() +{ + nsresult res; + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext); + nsCOMPtr selection; + PRBool cancel, handled; + + // pre-process + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertBreak); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (!cancel && !handled) + { + // create the new BR node + nsCOMPtr newNode; + nsAutoString tag; tag.AssignWithConversion("BR"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (!newNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called + if (NS_SUCCEEDED(res)) + { + // set the selection to the new node + nsCOMPtrparent; + res = newNode->GetParentNode(getter_AddRefs(parent)); + if (!parent) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called + if (NS_SUCCEEDED(res)) + { + PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not + nsCOMPtrnextNode; + newNode->GetNextSibling(getter_AddRefs(nextNode)); + if (nextNode) + { + nsCOMPtrnextTextNode; + nextTextNode = do_QueryInterface(nextNode); + if (!nextTextNode) { + nextNode = do_QueryInterface(newNode); + } + else { + offsetInParent=0; + } + } + else { + nextNode = do_QueryInterface(newNode); + } + res = GetSelection(getter_AddRefs(selection)); + if (!selection) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called + if (NS_SUCCEEDED(res)) + { + nsCOMPtr selPriv(do_QueryInterface(selection)); + if (-1==offsetInParent) + { + nextNode->GetParentNode(getter_AddRefs(parent)); + res = GetChildOffset(nextNode, parent, offsetInParent); + if (NS_SUCCEEDED(res)) { + // SetInterlinePosition(PR_TRUE) means we want the caret to stick to the content on the "right". + // We want the caret to stick to whatever is past the break. This is + // because the break is on the same line we were on, but the next content + // will be on the following line. + selPriv->SetInterlinePosition(PR_TRUE); + res = selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break + } + } + else + { + res = selection->Collapse(nextNode, offsetInParent); + } + } + } + } + } + if (!cancel) + { + // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + + return res; +} + + +NS_IMETHODIMP +nsPlaintextEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty) +{ + if (!aDocumentIsEmpty) + return NS_ERROR_NULL_POINTER; + + if (!mRules) + return NS_ERROR_NOT_INITIALIZED; + + return mRules->DocumentIsEmpty(aDocumentIsEmpty); +} + +NS_IMETHODIMP +nsPlaintextEditor::GetTextLength(PRInt32 *aCount) +{ + if (!aCount) { return NS_ERROR_NULL_POINTER; } + nsresult result; + // initialize out params + *aCount = 0; + + // special-case for empty document, to account for the bogus text node + PRBool docEmpty; + result = GetDocumentIsEmpty(&docEmpty); + if (NS_FAILED(result)) return result; + if (docEmpty) + { + *aCount = 0; + return NS_OK; + } + + // get the body node + nsCOMPtr bodyElement; + result = nsEditor::GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(result)) { return result; } + if (!bodyElement) { return NS_ERROR_NULL_POINTER; } + + // get the offsets of the first and last children of the body node + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + if (!bodyNode) { return NS_ERROR_NULL_POINTER; } + PRInt32 numBodyChildren=0; + nsCOMPtrlastChild; + result = bodyNode->GetLastChild(getter_AddRefs(lastChild)); + if (NS_FAILED(result)) { return result; } + if (!lastChild) { return NS_ERROR_NULL_POINTER; } + result = GetChildOffset(lastChild, bodyNode, numBodyChildren); + if (NS_FAILED(result)) { return result; } + + // count + PRInt32 start, end; + result = GetAbsoluteOffsetsForPoints(bodyNode, 0, + bodyNode, numBodyChildren, + bodyNode, start, end); + if (NS_SUCCEEDED(result)) + { + NS_ASSERTION(0==start, "GetAbsoluteOffsetsForPoints failed to set start correctly."); + NS_ASSERTION(0<=end, "GetAbsoluteOffsetsForPoints failed to set end correctly."); + if (0<=end) { + *aCount = end; + } + } + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::SetMaxTextLength(PRInt32 aMaxTextLength) +{ + mMaxTextLength = aMaxTextLength; + return NS_OK; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetMaxTextLength(PRInt32* aMaxTextLength) +{ + if (!aMaxTextLength) + return NS_ERROR_INVALID_POINTER; + *aMaxTextLength = mMaxTextLength; + return NS_OK; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetBodyStyleContext(nsIStyleContext** aStyleContext) +{ + nsCOMPtr body; + nsresult res = GetRootElement(getter_AddRefs(body)); + if (NS_FAILED(res)) return res; + nsCOMPtr content = do_QueryInterface(body); + + nsIFrame *frame; + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + res = ps->GetPrimaryFrameFor(content, &frame); + if (NS_FAILED(res)) return res; + + return ps->GetStyleContextFor(frame, aStyleContext); +} + +// +// Get the wrap width for the root of the document. +// +NS_IMETHODIMP +nsPlaintextEditor::GetWrapWidth(PRInt32 *aWrapColumn) +{ + nsresult res; + + if (! aWrapColumn) + return NS_ERROR_NULL_POINTER; + + *aWrapColumn = -1; // default: no wrap + + nsCOMPtr styleContext; + res = GetBodyStyleContext(getter_AddRefs(styleContext)); + if (NS_FAILED(res)) return res; + + const nsStyleText* styleText = + (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text); + + if (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) + { + *aWrapColumn = 0; // wrap to window width + } + else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace) + { + const nsStylePosition* stylePosition = + (const nsStylePosition*)styleContext->GetStyleData(eStyleStruct_Position); + if (stylePosition->mWidth.GetUnit() == eStyleUnit_Chars) + *aWrapColumn = stylePosition->mWidth.GetIntValue(); + else + { + *aWrapColumn = -1; + return NS_ERROR_UNEXPECTED; + } + } + else + { + *aWrapColumn = -1; + } + return NS_OK; +} + +// +// See if the style value includes this attribute, and if it does, +// cut out everything from the attribute to the next semicolon. +// +static void CutStyle(const char* stylename, nsString& styleValue) +{ + // Find the current wrapping type: + PRInt32 styleStart = styleValue.Find(stylename, PR_TRUE); + if (styleStart >= 0) + { + PRInt32 styleEnd = styleValue.Find(";", PR_FALSE, styleStart); + if (styleEnd > styleStart) + styleValue.Cut(styleStart, styleEnd - styleStart + 1); + else + styleValue.Cut(styleStart, styleValue.Length() - styleStart); + } +} + +// +// Change the wrap width on the root of this document. +// +NS_IMETHODIMP +nsPlaintextEditor::SetWrapWidth(PRInt32 aWrapColumn) +{ + nsresult res; + + // Ought to set a style sheet here ... + // Probably should keep around an mPlaintextStyleSheet for this purpose. + nsCOMPtr bodyElement; + res = GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + + // Get the current style for this body element: + nsAutoString styleName; styleName.AssignWithConversion("style"); + nsAutoString styleValue; + res = bodyElement->GetAttribute(styleName, styleValue); + if (NS_FAILED(res)) return res; + + // We'll replace styles for these values: + CutStyle("white-space", styleValue); + CutStyle("width", styleValue); + CutStyle("font-family", styleValue); + + // If we have other style left, trim off any existing semicolons + // or whitespace, then add a known semicolon-space: + if (styleValue.Length() > 0) + { + styleValue.Trim("; \t", PR_FALSE, PR_TRUE); + styleValue.AppendWithConversion("; "); + } + + // Make sure we have fixed-width font. This should be done for us, + // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;". + // Only do this if we're wrapping. + PRUint32 flags = 0; + GetFlags(&flags); + if ((flags & eEditorEnableWrapHackMask) && aWrapColumn >= 0) + styleValue.AppendWithConversion("font-family: -moz-fixed; "); + + // and now we're ready to set the new whitespace/wrapping style. + if (aWrapColumn > 0) // Wrap to a fixed column + { + styleValue.AppendWithConversion("white-space: -moz-pre-wrap; width: "); + styleValue.AppendInt(aWrapColumn); + styleValue.AppendWithConversion("ch;"); + } + else if (aWrapColumn == 0) + styleValue.AppendWithConversion("white-space: -moz-pre-wrap;"); + else + styleValue.AppendWithConversion("white-space: pre;"); + + res = bodyElement->SetAttribute(styleName, styleValue); + return res; +} + + + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIEditor overrides +#pragma mark - +#endif + +NS_IMETHODIMP +nsPlaintextEditor::Undo(PRUint32 aCount) +{ + ForceCompositionEnd(); + nsresult result = NS_OK; + + nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone); + + nsTextRulesInfo ruleInfo(nsTextEditRules::kUndo); + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + PRBool cancel, handled; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + + if (!cancel && NS_SUCCEEDED(result)) + { + result = nsEditor::Undo(aCount); + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + + return result; +} + + +NS_IMETHODIMP +nsPlaintextEditor::Redo(PRUint32 aCount) +{ + nsresult result = NS_OK; + + nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone); + + nsTextRulesInfo ruleInfo(nsTextEditRules::kRedo); + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + PRBool cancel, handled; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + + if (!cancel && NS_SUCCEEDED(result)) + { + result = nsEditor::Redo(aCount); + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + + return result; +} + +NS_IMETHODIMP nsPlaintextEditor::Cut() +{ + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (!NS_SUCCEEDED(res)) + return res; + + PRBool isCollapsed; + if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed) + return NS_ERROR_NOT_AVAILABLE; + + res = Copy(); + if (NS_SUCCEEDED(res)) + res = DeleteSelection(eNone); + return res; +} + +NS_IMETHODIMP nsPlaintextEditor::CanCut(PRBool &aCanCut) +{ + aCanCut = PR_FALSE; + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + aCanCut = !isCollapsed && IsModifiable(); + return NS_OK; +} + +NS_IMETHODIMP nsPlaintextEditor::Copy() +{ + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + return ps->DoCopy(); +} + +NS_IMETHODIMP nsPlaintextEditor::CanCopy(PRBool &aCanCopy) +{ + aCanCopy = PR_FALSE; + + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + aCanCopy = !isCollapsed; + return NS_OK; +} + + +// Shared between OutputToString and OutputToStream +NS_IMETHODIMP +nsPlaintextEditor::GetAndInitDocEncoder(const nsAReadableString& aFormatType, + PRUint32 aFlags, + const nsAReadableString* aCharset, + nsIDocumentEncoder** encoder) +{ + nsCOMPtr presShell; + nsresult rv = GetPresShell(getter_AddRefs(presShell)); + if (NS_FAILED(rv)) return rv; + if (!presShell) return NS_ERROR_FAILURE; + + nsCAutoString formatType(NS_DOC_ENCODER_CONTRACTID_BASE); + formatType.AppendWithConversion(aFormatType); + nsCOMPtr docEncoder (do_CreateInstance(formatType, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr doc; + rv = presShell->GetDocument(getter_AddRefs(doc)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = docEncoder->Init(doc, aFormatType, aFlags); + NS_ENSURE_SUCCESS(rv, rv); + + if (aCharset && aCharset->Length() != 0 + && !(aCharset->Equals(NS_LITERAL_STRING("null")))) + docEncoder->SetCharset(*aCharset); + + PRInt32 wc; + (void) GetWrapWidth(&wc); + if (wc >= 0) + (void) docEncoder->SetWrapColumn(wc); + + // Set the selection, if appropriate. + // We do this either if the OutputSelectionOnly flag is set, + // in which case we use our existing selection ... + if (aFlags & nsIDocumentEncoder::OutputSelectionOnly) + { + nsCOMPtr selection; + rv = GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(rv) && selection) + rv = docEncoder->SetSelection(selection); + NS_ENSURE_SUCCESS(rv, rv); + } + // ... or if the root element is not a body, + // in which case we set the selection to encompass the root. + else + { + nsCOMPtr rootElement; + GetRootElement(getter_AddRefs(rootElement)); + NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE); + if (!nsHTMLEditUtils::IsBody(rootElement)) + { + // XXX Why does this use range rather than selection collapse/extend? + nsCOMPtr range (do_CreateInstance(kCRangeCID, &rv)); + if (NS_FAILED(rv)) return rv; + if (!range) return NS_ERROR_FAILURE; + nsCOMPtr selection (do_CreateInstance(kCDOMSelectionCID, + &rv)); + if (NS_FAILED(rv)) return rv; + if (!selection) return NS_ERROR_FAILURE; + + // get the independent selection interface + nsCOMPtr indSel = do_QueryInterface(selection); + if (indSel) + indSel->SetPresShell(presShell); + + nsCOMPtr content(do_QueryInterface(rootElement)); + if (content) + { + range->SetStart(rootElement,0); + PRInt32 children; + if (NS_SUCCEEDED(content->ChildCount(children))) + range->SetEnd(rootElement,children); + // XXX else, should we return the error code? + + if (NS_FAILED(selection->AddRange(range))) + return NS_ERROR_FAILURE; + } + rv = docEncoder->SetSelection(selection); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + NS_ADDREF(*encoder = docEncoder); + return rv; +} + + +NS_IMETHODIMP +nsPlaintextEditor::OutputToString(nsAWritableString& aOutputString, + const nsAReadableString& aFormatType, + PRUint32 aFlags) +{ + PRBool cancel, handled; + nsString resultString; + nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText); + ruleInfo.outString = &resultString; + // XXX Struct should store a nsAReadable* + nsAutoString str(aFormatType); + ruleInfo.outputFormat = &str; + nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel, &handled); + if (cancel || NS_FAILED(rv)) { return rv; } + if (handled) + { // this case will get triggered by password fields + aOutputString.Assign(*(ruleInfo.outString)); + return rv; + } + + nsCOMPtr encoder; + rv = GetAndInitDocEncoder(aFormatType, aFlags, 0, getter_AddRefs(encoder)); + if (NS_FAILED(rv)) + return rv; + rv = encoder->EncodeToString(aOutputString); + return rv; +} + +NS_IMETHODIMP +nsPlaintextEditor::OutputToStream(nsIOutputStream* aOutputStream, + const nsAReadableString& aFormatType, + const nsAReadableString* aCharset, + PRUint32 aFlags) +{ + nsresult rv; + + // special-case for empty document when requesting plain text, + // to account for the bogus text node. + // XXX Should there be a similar test in OutputToString? + if (aFormatType == NS_LITERAL_STRING("text/plain")) + { + PRBool docEmpty; + rv = GetDocumentIsEmpty(&docEmpty); + if (NS_FAILED(rv)) return rv; + + if (docEmpty) + return NS_OK; // output nothing + } + + nsCOMPtr encoder; + rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset, + getter_AddRefs(encoder)); + + if (NS_FAILED(rv)) + return rv; + + return encoder->EncodeToStream(aOutputStream); +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIEditorIMESupport overrides +#pragma mark - +#endif + +NS_IMETHODIMP +nsPlaintextEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply) +{ + NS_ASSERTION(aTextRangeList, "null ptr"); + if(nsnull == aTextRangeList) + return NS_ERROR_NULL_POINTER; + nsCOMPtr caretP; + + // workaround for windows ime bug 23558: we get every ime event twice. + // for escape keypress, this causes an empty string to be passed + // twice, which freaks out the editor. This is to detect and aviod that + // situation: + if (aCompositionString.IsEmpty() && !mIMETextNode) + { + return NS_OK; + } + + nsCOMPtr selection; + nsresult result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + + mIMETextRangeList = aTextRangeList; + nsAutoPlaceHolderBatch batch(this, gIMETxnName); + + result = InsertText(aCompositionString.GetUnicode()); + + mIMEBufferLength = aCompositionString.Length(); + + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + ps->GetCaret(getter_AddRefs(caretP)); + caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed,selection); + + // second part of 23558 fix: + if (aCompositionString.IsEmpty()) + { + mIMETextNode = nsnull; + } + + return result; +} + +NS_IMETHODIMP +nsPlaintextEditor::GetReconversionString(nsReconversionEventReply* aReply) +{ + nsresult res; + + nsCOMPtr selection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res) || !selection) + return (res == NS_OK) ? NS_ERROR_FAILURE : res; + + // get the first range in the selection. Since it is + // unclear what to do if reconversion happens with a + // multirange selection, we will ignore any additional ranges. + + nsCOMPtr range; + res = selection->GetRangeAt(0, getter_AddRefs(range)); + if (NS_FAILED(res) || !range) + return (res == NS_OK) ? NS_ERROR_FAILURE : res; + + nsAutoString textValue; + res = range->ToString(textValue); + if (NS_FAILED(res)) + return res; + + aReply->mReconversionString = (PRUnichar*) nsMemory::Clone(textValue.GetUnicode(), + (textValue.Length() + 1) * sizeof(PRUnichar)); + if (!aReply->mReconversionString) + return NS_ERROR_OUT_OF_MEMORY; + + // delete the selection + res = DeleteSelection(eNone); + + return res; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark nsEditor overrides +#pragma mark - +#endif + + +/** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ +NS_IMETHODIMP +nsPlaintextEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) +{ + nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection + if (mRules) return mRules->BeforeEdit(mAction, mDirection); + return NS_OK; +} + + +/** All editor operations which alter the doc should be followed + * with a call to EndOperation */ +NS_IMETHODIMP +nsPlaintextEditor::EndOperation() +{ + // post processing + nsresult res = NS_OK; + if (mRules) res = mRules->AfterEdit(mAction, mDirection); + nsEditor::EndOperation(); // will clear mAction, mDirection + return res; +} + + +NS_IMETHODIMP +nsPlaintextEditor::SelectEntireDocument(nsISelection *aSelection) +{ + nsresult res; + if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; } + + // get body node + nsCOMPtrbodyElement; + res = GetRootElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + if (!bodyNode) return NS_ERROR_FAILURE; + + // is doc empty? + PRBool bDocIsEmpty; + res = mRules->DocumentIsEmpty(&bDocIsEmpty); + if (NS_FAILED(res)) return res; + + if (bDocIsEmpty) + { + // if its empty dont select entire doc - that would select the bogus node + return aSelection->Collapse(bodyNode, 0); + } + else + { + return nsEditor::SelectEntireDocument(aSelection); + } + return res; +} + + + +#ifdef XP_MAC +#pragma mark - +#pragma mark Random methods +#pragma mark - +#endif + + +NS_IMETHODIMP nsPlaintextEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject) +{ + nsresult result = NS_ERROR_FAILURE; // we return an error unless we get the index + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + + if ((nsnull!=aNode)) + { // get the content interface + nsCOMPtr nodeAsContent( do_QueryInterface(aNode) ); + if (nodeAsContent) + { // get the frame from the content interface + //Note: frames are not ref counted, so don't use an nsCOMPtr + *aLayoutObject = nsnull; + result = ps->GetLayoutObjectFor(nodeAsContent, aLayoutObject); + } + } + else { + result = NS_ERROR_NULL_POINTER; + } + + return result; +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +NS_IMETHODIMP +nsPlaintextEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) +{ + static char bodyTag[] = "body"; + static char tdTag[] = "td"; + static char thTag[] = "th"; + static char captionTag[] = "caption"; + if (aTag.EqualsIgnoreCase(bodyTag) || + aTag.EqualsIgnoreCase(tdTag) || + aTag.EqualsIgnoreCase(thTag) || + aTag.EqualsIgnoreCase(captionTag) ) + { + aIsTag = PR_TRUE; + } + else { + aIsTag = PR_FALSE; + } + return NS_OK; +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +void nsPlaintextEditor::HandleEventListenerError() +{ + if (gNoisy) { printf("failed to add event listener\n"); } + // null out the nsCOMPtrs + mKeyListenerP = nsnull; + mMouseListenerP = nsnull; + mTextListenerP = nsnull; + mDragListenerP = nsnull; + mCompositionListenerP = nsnull; + mFocusListenerP = nsnull; +} + +#ifdef XP_MAC +#pragma mark - +#endif + diff --git a/mozilla/editor/libeditor/text/nsPlaintextEditor.h b/mozilla/editor/libeditor/text/nsPlaintextEditor.h new file mode 100644 index 00000000000..c2bc4435a22 --- /dev/null +++ b/mozilla/editor/libeditor/text/nsPlaintextEditor.h @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsPlaintextEditor_h__ +#define nsPlaintextEditor_h__ + +#include "nsCOMPtr.h" + +#include "nsIPlaintextEditor.h" + +#include "nsEditor.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventListener.h" + +#include "TypeInState.h" +#include "nsEditRules.h" + +class nsIDOMKeyEvent; +class nsITransferable; +class nsIDOMEventReceiver; +class nsIDocumentEncoder; + +/** + * The text editor implementation. + * Use to edit text document represented as a DOM tree. + */ +class nsPlaintextEditor : public nsEditor, + public nsIPlaintextEditor +{ + +public: + +// Interfaces for addref and release and queryinterface +// NOTE macro used is for classes that inherit from +// another class. Only the base class should use NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED + + /* below used by TypedText() */ + enum { + eTypedText, /* user typed text */ + eTypedBR, /* user typed shift-enter to get a br */ + eTypedBreak /* user typed enter */ + }; + + nsPlaintextEditor(); + virtual ~nsPlaintextEditor(); + + /* ------------ nsIPlaintextEditor methods -------------- */ + NS_DECL_NSIPLAINTEXTEDITOR + + /* ------------ nsIEditorIMESupport overrides -------------- */ + + NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply); + NS_IMETHOD GetReconversionString(nsReconversionEventReply* aReply); + + /* ------------ Overrides of nsEditor interface methods -------------- */ + + /** prepare the editor for use */ + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags); + + NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty); + + NS_IMETHOD DeleteSelection(EDirection aAction); + + NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet); + + /** we override this here to install event listeners */ + NS_IMETHOD PostCreate(); + + NS_IMETHOD GetFlags(PRUint32 *aFlags); + NS_IMETHOD SetFlags(PRUint32 aFlags); + + NS_IMETHOD Undo(PRUint32 aCount); + NS_IMETHOD Redo(PRUint32 aCount); + + NS_IMETHOD Cut(); + NS_IMETHOD CanCut(PRBool &aCanCut); + NS_IMETHOD Copy(); + NS_IMETHOD CanCopy(PRBool &aCanCopy); + NS_IMETHOD Paste(PRInt32 aSelectionType); + NS_IMETHOD CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste); + + NS_IMETHOD CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag); + NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent); + NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent); + + NS_IMETHOD OutputToString(nsAWritableString& aOutputString, + const nsAReadableString& aFormatType, + PRUint32 aFlags); + + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsAReadableString& aFormatType, + const nsAReadableString* aCharsetOverride, + PRUint32 aFlags); + + + /** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ + NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection); + + /** All editor operations which alter the doc should be followed + * with a call to EndOperation */ + NS_IMETHOD EndOperation(); + + /** make the given selection span the entire document */ + NS_IMETHOD SelectEntireDocument(nsISelection *aSelection); + + /* ------------ Utility Routines, not part of public API -------------- */ + NS_IMETHOD TypedText(const PRUnichar* aString, PRInt32 aAction); + + /** returns the absolute position of the end points of aSelection + * in the document as a text stream. + */ + nsresult GetTextSelectionOffsets(nsISelection *aSelection, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset); + + nsresult GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, + PRInt32 aInStartOffset, + nsIDOMNode *aInEndNode, + PRInt32 aInEndOffset, + nsIDOMNode *aInCommonParentNode, + PRInt32 &aOutStartOffset, + PRInt32 &aEndOffset); + +protected: + + NS_IMETHOD InitRules(); + void BeginEditorInit(); + nsresult EndEditorInit(); + + /** install the event listeners for the editor + * used to be part of Init, but now broken out into a separate method + * called by PostCreate, giving the caller the chance to interpose + * their own listeners before we install our own backstops. + */ + NS_IMETHOD InstallEventListeners(); + + /** returns the layout object (nsIFrame in the real world) for aNode + * @param aNode the content to get a frame for + * @param aLayoutObject the "primary frame" for aNode, if one exists. May be null + * @return NS_OK whether a frame is found or not + * an error if some serious error occurs + */ + NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject); + NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); + + // Helpers for output routines + NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType, + PRUint32 aFlags, + const nsAReadableString* aCharset, + nsIDocumentEncoder** encoder); + + // key event helpers + NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, + nsCOMPtr *outBRNode, EDirection aSelect = eNone); + NS_IMETHOD CreateBRImpl(nsCOMPtr *aInOutParent, + PRInt32 *aInOutOffset, + nsCOMPtr *outBRNode, + EDirection aSelect); + NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); + + NS_IMETHOD IsRootTag(nsString &aTag, PRBool &aIsTag); + + + // factored methods for handling insertion of data from transferables (drag&drop or clipboard) + NS_IMETHOD PrepareTransferable(nsITransferable **transferable); + NS_IMETHOD InsertTextFromTransferable(nsITransferable *transferable); + + /** simple utility to handle any error with event listener allocation or registration */ + void HandleEventListenerError(); + + /* small utility routine to test the eEditorReadonly bit */ + PRBool IsModifiable(); + + nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver); + + //XXX Kludge: Used to suppress spurious drag/drop events (bug 50703) + PRBool mIgnoreSpuriousDragEvent; + NS_IMETHOD IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;} + +// Data members +protected: + + nsCOMPtr mRules; + nsCOMPtr mKeyListenerP; + nsCOMPtr mMouseListenerP; + nsCOMPtr mTextListenerP; + nsCOMPtr mCompositionListenerP; + nsCOMPtr mDragListenerP; + nsCOMPtr mFocusListenerP; + PRBool mIsComposing; + PRInt32 mMaxTextLength; + PRInt32 mInitTriggerCounter; + +public: + static nsIAtom *gTypingTxnName; + static nsIAtom *gIMETxnName; + static nsIAtom *gDeleteTxnName; + +// friends +friend class nsHTMLEditRules; +friend class nsTextEditRules; +friend class nsAutoEditInitRulesTrigger; + +}; + +#endif //nsPlaintextEditor_h__ + diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index ab8f8df4e4c..844031ae288 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -48,7 +48,7 @@ static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ - if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \ + if ((mFlags & nsIPlaintextEditor::eEditorReadonlyMask) || (mFlags & nsIPlaintextEditor::eEditorDisabledMask)) \ { \ *aCancel = PR_TRUE; \ return NS_OK; \ @@ -101,7 +101,7 @@ NS_IMPL_QUERY_INTERFACE1(nsTextEditRules, nsIEditRules) ********************************************************/ NS_IMETHODIMP -nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) +nsTextEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags) { if (!aEditor) { return NS_ERROR_NULL_POINTER; } @@ -125,10 +125,8 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) if (NS_FAILED(res)) return res; // create a range that is the entire body contents - nsCOMPtr wholeDoc; - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(wholeDoc)); - if (NS_FAILED(res)) return res; + nsCOMPtr wholeDoc = do_CreateInstance(kRangeCID); + if (!wholeDoc) return NS_ERROR_NULL_POINTER; wholeDoc->SetStart(mBody,0); nsCOMPtr list; res = mBody->GetChildNodes(getter_AddRefs(list)); @@ -163,11 +161,9 @@ nsTextEditRules::SetFlags(PRUint32 aFlags) // SetFlags() is really meant to only be called once // and at editor init time. - PRBool willBePlaintext = (aFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0; - PRBool alreadyPlaintext = (mFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0; - + PRBool willBePlaintext = (aFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; + PRBool alreadyPlaintext = (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; mFlags = aFlags; - return NS_OK; } @@ -207,8 +203,6 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) res = CreateBogusNodeIfNeeded(selection); if (NS_FAILED(res)) return res; - // create moz-br and adjust selection if needed - res = AdjustSelection(selection, aDirection); } return res; } @@ -340,42 +334,7 @@ nsTextEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel) mBogusNode = do_QueryInterface(nsnull); } - // this next only works for collapsed selections right now, - // because selection is a pain to work with when not collapsed. - // (no good way to extend start or end of selection) - PRBool bCollapsed; - nsresult res = aSelection->GetIsCollapsed(&bCollapsed); - if (NS_FAILED(res)) return res; - if (!bCollapsed) return NS_OK; - - // if we are after a mozBR in the same block, then move selection - // to be before it - nsCOMPtr selNode, priorNode; - PRInt32 selOffset; - // get the (collapsed) selection location - res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset); - if (NS_FAILED(res)) return res; - // get prior node - res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(priorNode)); - if (NS_SUCCEEDED(res) && priorNode && nsHTMLEditUtils::IsMozBR(priorNode)) - { - nsCOMPtr block1, block2; - if (mEditor->IsBlockNode(selNode)) block1 = selNode; - else block1 = mEditor->GetBlockNodeParent(selNode); - block2 = mEditor->GetBlockNodeParent(priorNode); - - if (block1 != block2) return NS_OK; - - // if we are here then the selection is right after a mozBR - // that is in the same block as the selection. We need to move - // the selection start to be before the mozBR. - res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode,selOffset); - if (NS_FAILED(res)) return res; - } - - return res; + return NS_OK; } nsresult @@ -384,44 +343,13 @@ nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult) return NS_OK; } -nsresult -nsTextEditRules::GetTopEnclosingPre(nsIDOMNode *aNode, - nsIDOMNode** aOutPreNode) -{ - // check parms - if (!aNode || !aOutPreNode) - return NS_ERROR_NULL_POINTER; - *aOutPreNode = 0; - - nsresult res = NS_OK; - nsCOMPtr node, parentNode; - node = do_QueryInterface(aNode); - - while (node) - { - nsAutoString tag; - nsEditor::GetTagString(node, tag); - if (tag.EqualsWithConversion("pre", PR_TRUE)) - *aOutPreNode = node; - else if (tag.EqualsWithConversion("body", PR_TRUE)) - break; - - res = node->GetParentNode(getter_AddRefs(parentNode)); - if (NS_FAILED(res)) return res; - node = parentNode; - } - - NS_IF_ADDREF(*aOutPreNode); - return res; -} - nsresult nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled) { if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } CANCEL_OPERATION_IF_READONLY_OR_DISABLED *aHandled = PR_FALSE; - if (mFlags & nsIHTMLEditor::eEditorSingleLineMask) { + if (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) { *aCancel = PR_TRUE; } else @@ -444,72 +372,6 @@ nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo // we want to ignore result of WillInsert() *aCancel = PR_FALSE; - // Mail rule: split any
 tags in the way,
-    // since they're probably quoted text.
-    // For now, do this for all plaintext since mail is our main customer
-    // and we don't currently set eEditorMailMask for plaintext mail.
-    if (mTheAction != nsHTMLEditor::kOpInsertQuotation) // && mFlags & nsIHTMLEditor::eEditorMailMask)
-    {
-      nsCOMPtr preNode, selNode;
-      PRInt32 selOffset, newOffset;
-      res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
-      if (NS_FAILED(res)) return res;
-
-      // If any of the following fail, then just proceed with the
-      // normal break insertion without worrying about the error
-      res = GetTopEnclosingPre(selNode, getter_AddRefs(preNode));
-      if (NS_SUCCEEDED(res) && preNode)
-      {
-        // Only split quote nodes: see if it has the attribute _moz_quote
-        nsCOMPtr preElement (do_QueryInterface(preNode));
-        if (preElement)
-        {
-          nsString mozQuote; mozQuote.AssignWithConversion("_moz_quote");
-          nsString mozQuoteVal;
-          PRBool isMozQuote = PR_FALSE;
-          if (NS_SUCCEEDED(mEditor->GetAttributeValue(preElement, mozQuote,
-                                                      mozQuoteVal, isMozQuote))
-              && isMozQuote)
-          {
-            printf("It's a moz quote -- splitting\n");
-            nsCOMPtr outLeftNode;
-            nsCOMPtr outRightNode;
-            res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, PR_TRUE, address_of(outLeftNode), address_of(outRightNode));
-            if (NS_FAILED(res)) return res;
-            PRBool bIsEmptyNode;
-            
-            // rememeber parent of selNode now, since we might delete selNode below
-            res = preNode->GetParentNode(getter_AddRefs(selNode));
-            if (NS_FAILED(res)) return res;
-            
-            if (outLeftNode)
-            {
-              res = mEditor->IsEmptyNode(outLeftNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
-              if (NS_FAILED(res)) return res;
-              if (bIsEmptyNode) mEditor->DeleteNode(outLeftNode);
-            }
-            if (outRightNode)
-            {
-              // HACK alert: consume a br if there is one at front of node
-              nsCOMPtr firstNode;
-              res =  mEditor->GetFirstEditableNode(outRightNode, address_of(firstNode));
-              if (firstNode &&  nsHTMLEditUtils::IsBreak(firstNode))
-              {
-                mEditor->DeleteNode(firstNode);
-              }
-                
-              res = mEditor->IsEmptyNode(outRightNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
-              if (NS_FAILED(res)) return res;
-              if (bIsEmptyNode) mEditor->DeleteNode(outRightNode);
-            }
-            nsCOMPtr brNode;
-            // last ePrevious param causes selection to be set before the break
-            res = mEditor->CreateBR(selNode, newOffset, address_of(brNode), nsIEditor::ePrevious);
-            *aHandled = PR_TRUE;
-          }
-        }
-      }
-    }  
   }
   return NS_OK;
 }
@@ -519,42 +381,44 @@ nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
 {
   // we only need to execute the stuff below if we are a plaintext editor.
   // html editors have a different mechanism for putting in mozBR's
-  // (because there are a bunch more placesyou have to worry about it in html) 
-  if (!nsIHTMLEditor::eEditorPlaintextMask & mFlags) return NS_OK;
+  // (because there are a bunch more places you have to worry about it in html) 
+  if (!nsIPlaintextEditor::eEditorPlaintextMask & mFlags) return NS_OK;
 
   // if we are at the end of the document, we need to insert 
   // a special mozBR following the normal br, and then set the
   // selection to stick to the mozBR.
   PRInt32 selOffset;
-  nsCOMPtr nearNode, selNode;
+  nsCOMPtr nearNode, selNode, root, temp;
+  nsCOMPtr rootElem;
   nsresult res;
   res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
   if (NS_FAILED(res)) return res;
-  res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
+  // confirm we are at end of document
+  if (selOffset == 0) return NS_OK;  // cant be after a br if we are at offset 0
+  res = mEditor->GetRootElement(getter_AddRefs(rootElem));
   if (NS_FAILED(res)) return res;
+  root = do_QueryInterface(rootElem);
+  if (!root) return NS_ERROR_NULL_POINTER;
+  if (selNode != root) return NS_OK; // must be inside text node or somewhere other than end of root
+  temp = mEditor->GetChildAt(selNode, selOffset);
+  if (temp) return NS_OK; // cant be at end of there is a node after us.
+  nearNode = mEditor->GetChildAt(selNode, selOffset-1);
   if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) && !nsHTMLEditUtils::IsMozBR(nearNode))
   {
-    PRBool bIsLast;
-    res = mEditor->IsLastEditableChild(nearNode, &bIsLast);
+    nsCOMPtr sel(aSelection);
+    nsCOMPtrselPrivate(do_QueryInterface(sel));
+    // need to insert special moz BR. Why?  Because if we don't
+    // the user will see no new line for the break.  Also, things
+    // like table cells won't grow in height.
+    nsCOMPtr brNode;
+    res = CreateMozBR(selNode, selOffset, address_of(brNode));
+    if (NS_FAILED(res)) return res;
+    res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
+    if (NS_FAILED(res)) return res;
+    selPrivate->SetInterlinePosition(PR_TRUE);
+    res = aSelection->Collapse(selNode,selOffset);
     if (NS_FAILED(res)) return res;
-    if (bIsLast)
-    {
-      nsCOMPtr sel(aSelection);
-      nsCOMPtrselPrivate(do_QueryInterface(sel));
-      // need to insert special moz BR. Why?  Because if we don't
-      // the user will see no new line for the break.  Also, things
-      // like table cells won't grow in height.
-      nsCOMPtr brNode;
-      res = CreateMozBR(selNode, selOffset, address_of(brNode));
-      if (NS_FAILED(res)) return res;
-      res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
-      if (NS_FAILED(res)) return res;
-      selPrivate->SetInterlinePosition(PR_TRUE);
-      res = aSelection->Collapse(selNode,selOffset);
-      if (NS_FAILED(res)) return res;
-    }
   }
-//  mEditor->DumpContentTree();
   return res;
 }
 
@@ -595,7 +459,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
   if (NS_FAILED(res)) return res;
   
   // handle password field docs
-  if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
     NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
@@ -621,7 +485,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
   // handle password field data
   // this has the side effect of changing all the characters in aOutString
   // to the replacement character
-  if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     res = EchoInsertionToPWBuff(start, end, outString);
     if (NS_FAILED(res)) return res;
@@ -646,7 +510,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
     rv = prefs->GetIntPref("editor.singleLine.pasteNewlines",
                            &singleLineNewlineBehavior);
 
-  if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
+  if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
   {
     if (singleLineNewlineBehavior == eReplaceWithSpaces)
       outString->ReplaceChar(CRLF, ' ');
@@ -731,7 +595,7 @@ nsTextEditRules::WillInsertText(PRInt32          aAction,
         // is it a return?
         if (subStr.EqualsWithConversion("\n"))
         {
-          if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
+          if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
           {
             NS_ASSERTION((singleLineNewlineBehavior == ePasteIntact),
                   "Newline improperly getting into single-line edit field!");
@@ -814,7 +678,7 @@ nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, PRBool *aCancel,
   nsresult res = NS_OK;
 
   // XXX: should probably return a success value other than NS_OK that means "not allowed"
-  if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
+  if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
     *aCancel = PR_TRUE;
   }
   return res;
@@ -834,7 +698,7 @@ nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, PRBool *aCance
   nsresult res = NS_OK;
 
   // XXX: should probably return a success value other than NS_OK that means "not allowed"
-  if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
+  if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
     *aCancel = PR_TRUE;
   }
   return res;
@@ -864,7 +728,7 @@ nsTextEditRules::WillDeleteSelection(nsISelection *aSelection,
     *aCancel = PR_TRUE;
     return NS_OK;
   }
-  if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+  if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     // manage the password buffer
     PRInt32 start, end;
@@ -1098,7 +962,7 @@ nsTextEditRules::WillOutputText(nsISelection *aSelection,
 
   if (PR_TRUE == aOutputFormat->EqualsWithConversion("text/plain"))
   { // only use these rules for plain text output
-    if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
+    if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
     {
       *aOutString = mPasswordText;
       *aHandled = PR_TRUE;
@@ -1288,7 +1152,7 @@ nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection,
   nsresult res = NS_OK;
   *aOutString = *aInString;
   
-  if ((-1 != aMaxLength) && (mFlags & nsIHTMLEditor::eEditorPlaintextMask))
+  if ((-1 != aMaxLength) && (mFlags & nsIPlaintextEditor::eEditorPlaintextMask))
   {
     // Get the current text length.
     // Get the length of inString.
@@ -1388,69 +1252,3 @@ nsTextEditRules::DeleteEmptyTextNode(nsIDOMNode *aNode)
   return PR_FALSE;
 }
 
-
-nsresult 
-nsTextEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection)
-{
-  if (!aSelection) return NS_ERROR_NULL_POINTER;
-  
-  // if the selection isn't collapsed, do nothing.
-  PRBool bCollapsed;
-  nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
-  if (NS_FAILED(res)) return res;
-  if (!bCollapsed) return res;
-
-  // get the (collapsed) selection location
-  nsCOMPtr selNode, temp;
-  PRInt32 selOffset;
-  res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
-  if (NS_FAILED(res)) return res;
-  temp = selNode;
-  
-  // are we in an editable node?
-  while (!mEditor->IsEditable(selNode))
-  {
-    // scan up the tree until we find an editable place to be
-    res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset);
-    if (NS_FAILED(res)) return res;
-    if (!selNode) return NS_ERROR_FAILURE;
-    temp = selNode;
-  }
-  
-  // are we in a text node? 
-  nsCOMPtr textNode = do_QueryInterface(selNode);
-  if (textNode) 
-    return NS_OK; // we LIKE it when we are in a text node.  that RULZ
-  
-  // do we need to insert a special mozBR?  We do if we are:
-  // 1) after a block element AND
-  // 2) at the end of the body OR before another block
-
-  nsCOMPtr priorNode, nextNode;
-  res = mEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(priorNode));
-  if (NS_FAILED(res)) return res;
-  res = mEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nextNode));
-  if (NS_FAILED(res)) return res;
-  
-  // is priorNode a block?
-  if (priorNode && mEditor->IsBlockNode(priorNode)) 
-  {
-    if (!nextNode || mEditor->IsBlockNode(nextNode))
-    {
-      nsCOMPtr sel(aSelection);
-      nsCOMPtrselPrivate(do_QueryInterface(sel));
-
-      nsCOMPtr brNode;
-      res = CreateMozBR(selNode, selOffset, address_of(brNode));
-      if (NS_FAILED(res)) return res;
-      res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
-      if (NS_FAILED(res)) return res;
-      // selection stays *before* moz-br, sticking to it
-      selPrivate->SetInterlinePosition(PR_TRUE);
-      res = aSelection->Collapse(selNode,selOffset);
-      if (NS_FAILED(res)) return res;
-    }
-  }
-  return res;
-}
-
diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.h b/mozilla/editor/libeditor/text/nsTextEditRules.h
index def80ad9dad..8e295d7d14f 100644
--- a/mozilla/editor/libeditor/text/nsTextEditRules.h
+++ b/mozilla/editor/libeditor/text/nsTextEditRules.h
@@ -25,7 +25,7 @@
 
 #include "nsCOMPtr.h"
 
-#include "nsHTMLEditor.h"
+#include "nsPlaintextEditor.h"
 #include "nsIDOMNode.h"
 
 #include "nsEditRules.h"
@@ -50,7 +50,7 @@ public:
   virtual     ~nsTextEditRules();
 
   // nsIEditRules methods
-  NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags);
+  NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
@@ -163,17 +163,15 @@ protected:
 
   PRBool DeleteEmptyTextNode(nsIDOMNode *aNode);
 
-  nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection);
-  
   // data members
-  nsHTMLEditor *mEditor;  // note that we do not refcount the editor
-  nsString      mPasswordText;  // a buffer we use to store the real value of password editors
-  nsCOMPtr mBogusNode;  // magic node acts as placeholder in empty doc
-  nsCOMPtr mBody;    // cached root node
-  PRUint32 mFlags;
-  PRUint32 mActionNesting;
-  PRBool   mLockRulesSniffing;
-  PRInt32  mTheAction;    // the top level editor action
+  nsPlaintextEditor   *mEditor;        // note that we do not refcount the editor
+  nsString             mPasswordText;  // a buffer we use to store the real value of password editors
+  nsCOMPtr mBogusNode;     // magic node acts as placeholder in empty doc
+  nsCOMPtr mBody;          // cached root node
+  PRUint32             mFlags;
+  PRUint32             mActionNesting;
+  PRBool               mLockRulesSniffing;
+  PRInt32              mTheAction;     // the top level editor action
   // friends
   friend class nsAutoLockRulesSniffing;
 
diff --git a/mozilla/editor/macbuild/editor.mcp b/mozilla/editor/macbuild/editor.mcp
index 044be4f3514..cd43f550479 100644
Binary files a/mozilla/editor/macbuild/editor.mcp and b/mozilla/editor/macbuild/editor.mcp differ
diff --git a/mozilla/editor/public/nsEditorCID.h b/mozilla/editor/public/nsEditorCID.h
index abfede691c7..f3cffbe386c 100644
--- a/mozilla/editor/public/nsEditorCID.h
+++ b/mozilla/editor/public/nsEditorCID.h
@@ -5,6 +5,11 @@
 0xd3de3431, 0x8a75, 0x11d2, \
 { 0x91, 0x8c, 0x0, 0x80, 0xc8, 0xe4, 0x4d, 0xb5 } }
 
+#define NS_TEXTEDITOR_CID \
+{/* {e197cc01-cfe1-11d4-8eb0-87ae406dfd3f}*/ \
+0xe197cc01, 0xcfe1, 0x11d4, \
+{ 0x8e, 0xb0, 0x87, 0xae, 0x40, 0x6d, 0xfd, 0x3f } }
+
 #define NS_HTMLEDITOR_CID \
 {/* {ed0244e0-c144-11d2-8f4c-006008159b0c}*/ \
 0xed0244e0, 0xc144, 0x11d2, \
diff --git a/mozilla/editor/public/nsIEditor.h b/mozilla/editor/public/nsIEditor.h
index be7379b6d1c..74e1e6e36c7 100644
--- a/mozilla/editor/public/nsIEditor.h
+++ b/mozilla/editor/public/nsIEditor.h
@@ -140,6 +140,14 @@ public:
    */
   NS_IMETHOD DeleteSelection(EDirection aAction)=0;
 
+  /** 
+   * DeleteSelectionAndCreateNode combines DeleteSelection and CreateNode
+   * It deletes only if there is something selected (doesn't do DEL, BACKSPACE action)   
+   * @param aTag      The type of object to create
+   * @param aNewNode  [OUT] The node created.  Caller must release aNewNode.
+   */
+  NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0;
+
 
   /* ------------ Document info and file methods -------------- */
   
diff --git a/mozilla/editor/public/nsIHTMLEditor.h b/mozilla/editor/public/nsIHTMLEditor.h
index 88f27b57bec..e57a778b636 100644
--- a/mozilla/editor/public/nsIHTMLEditor.h
+++ b/mozilla/editor/public/nsIHTMLEditor.h
@@ -45,45 +45,6 @@ class nsIHTMLEditor : public nsISupports
 public:
   static const nsIID& GetIID() { static nsIID iid = NS_IHTMLEDITOR_IID; return iid; }
 
-
-  // the bits in an editor behavior mask.
-  enum {
-    eEditorPlaintextBit       = 0,        // only plain text entry is allowed via events
-    eEditorSingleLineBit,                 // enter key and CR-LF handled specially
-    eEditorPasswordBit,                   // text is not entered into content, only a representative character
-    eEditorReadonlyBit,                   // editing events are disabled.  Editor may still accept focus.
-    eEditorDisabledBit,                   // all events are disabled (like scrolling).  Editor will not accept focus.
-    eEditorFilterInputBit,                // text input is limited to certain character types, use mFilter
-    eEditorMailBit,                       // use mail-compose editting rules
-    eEditorDisableForcedUpdatesBit,       // prevent immediate view refreshes
-    eEditorDisableForcedReflowsBit,       // prevent immediate reflows
-    eEditorEnableWrapHackBit,             // allow the editor to set font: monospace on the root node
-    eEditorWidgetBit                      // bit for widgets
-    
-    // max 32 bits
-  };
-  
-  enum {
-    eEditorPlaintextMask            = (1 << eEditorPlaintextBit),
-    eEditorSingleLineMask           = (1 << eEditorSingleLineBit),
-    eEditorPasswordMask             = (1 << eEditorPasswordBit),
-    eEditorReadonlyMask             = (1 << eEditorReadonlyBit),
-    eEditorDisabledMask             = (1 << eEditorDisabledBit),
-    eEditorFilterInputMask          = (1 << eEditorFilterInputBit),
-    eEditorMailMask                 = (1 << eEditorMailBit),
-    eEditorDisableForcedUpdatesMask = (1 << eEditorDisableForcedUpdatesBit),
-    eEditorDisableForcedReflowsMask = (1 << eEditorDisableForcedReflowsBit),
-    eEditorEnableWrapHackMask       = (1 << eEditorEnableWrapHackBit),
-    eEditorWidgetMask               = (1 << eEditorWidgetBit)
-  };
-  
-  // below used by TypedText()
-  enum {
-    eTypedText,  // user typed text
-    eTypedBR,    // user typed shift-enter to get a br
-    eTypedBreak  // user typed enter
-  };
-  
   // used by GetAlignment()
   typedef enum {
     eLeft,
@@ -232,14 +193,6 @@ public:
     */  
   NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection)=0;
 
-  /** 
-   * DeleteSelectionAndCreateNode combines DeleteSelection and CreateNode
-   * It deletes only if there is something selected (doesn't do DEL, BACKSPACE action)   
-   * @param aTag      The type of object to create
-   * @param aNewNode  [OUT] The node created.  Caller must release aNewNode.
-   */
-  NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0;
-
   /* ------------ Selection manipulation -------------- */
   /* Should these be moved to nsISelection? */
   
diff --git a/mozilla/layout/html/forms/src/nsGfxTextControlFrame2.cpp b/mozilla/layout/html/forms/src/nsGfxTextControlFrame2.cpp
index 2e3e2f36d94..71c17201596 100644
--- a/mozilla/layout/html/forms/src/nsGfxTextControlFrame2.cpp
+++ b/mozilla/layout/html/forms/src/nsGfxTextControlFrame2.cpp
@@ -31,7 +31,6 @@
 #include "nsIFormControl.h"
 #include "nsIServiceManager.h"
 #include "nsIFrameSelection.h"
-#include "nsIHTMLEditor.h"
 #include "nsIPlaintextEditor.h"
 #include "nsEditorCID.h"
 #include "nsLayoutCID.h"
@@ -101,7 +100,7 @@
 #define GUESS_INPUT_SIZE 150  // 10 pixels wide
 
 
-static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID);
+static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
 static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
 
 
@@ -1612,8 +1611,8 @@ nsGfxTextControlFrame2::SetInitialValue()
     // immediate reflows during any editor calls.
 
     rv = mEditor->SetFlags(editorFlags |
-                           nsIHTMLEditor::eEditorDisableForcedUpdatesMask |
-                           nsIHTMLEditor::eEditorDisableForcedReflowsMask);
+                           nsIPlaintextEditor::eEditorDisableForcedUpdatesMask |
+                           nsIPlaintextEditor::eEditorDisableForcedReflowsMask);
 
     if (NS_FAILED(rv))
       return rv;
@@ -1751,9 +1750,10 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
 
   // Create an editor
 
-  rv = nsComponentManager::CreateInstance(kHTMLEditorCID,
-                                              nsnull,
-                                              NS_GET_IID(nsIEditor), getter_AddRefs(mEditor));
+  rv = nsComponentManager::CreateInstance(kTextEditorCID,
+                                          nsnull,
+                                          NS_GET_IID(nsIEditor), 
+                                          getter_AddRefs(mEditor));
   if (NS_FAILED(rv))
     return rv;
   if (!mEditor) 
@@ -1784,14 +1784,14 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
 
   PRUint32 editorFlags = 0;
   if (IsPlainTextControl())
-    editorFlags |= nsIHTMLEditor::eEditorPlaintextMask;
+    editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
   if (IsSingleLineTextControl())
-    editorFlags |= nsIHTMLEditor::eEditorSingleLineMask;
+    editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
   if (IsPasswordTextControl())
-    editorFlags |= nsIHTMLEditor::eEditorPasswordMask;
+    editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
 
   //all gfxtextcontrolframe2's are widgets
-  editorFlags |= nsIHTMLEditor::eEditorWidgetMask;
+  editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
 
   // Now initialize the editor.
   //
@@ -1949,7 +1949,7 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
       return rv;
 
     if (NS_CONTENT_ATTR_NOT_THERE != rv)
-      editorFlags |= nsIHTMLEditor::eEditorReadonlyMask;
+      editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
 
     // Check if the disabled attribute is set.
 
@@ -1959,18 +1959,18 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
       return rv;
 
     if (NS_CONTENT_ATTR_NOT_THERE != rv) 
-      editorFlags |= nsIHTMLEditor::eEditorDisabledMask;
+      editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
 
     // Disable the caret and selection if neccessary.
 
-    if (editorFlags & nsIHTMLEditor::eEditorReadonlyMask ||
-        editorFlags & nsIHTMLEditor::eEditorDisabledMask)
+    if (editorFlags & nsIPlaintextEditor::eEditorReadonlyMask ||
+        editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
     {
       if (mSelCon)
       {
         mSelCon->SetCaretEnabled(PR_FALSE);
 
-        if (editorFlags & nsIHTMLEditor::eEditorDisabledMask)
+        if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
           mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
       }
 
@@ -2689,14 +2689,14 @@ nsGfxTextControlFrame2::AttributeChanged(nsIPresContext* aPresContext,
     mEditor->GetFlags(&flags);
     if (NS_CONTENT_ATTR_NOT_THERE != rv) 
     { // set readonly
-      flags |= nsIHTMLEditor::eEditorReadonlyMask;
+      flags |= nsIPlaintextEditor::eEditorReadonlyMask;
       if (mSelCon)
         mSelCon->SetCaretEnabled(PR_FALSE);
     }
     else 
     { // unset readonly
-      flags &= ~(nsIHTMLEditor::eEditorReadonlyMask);
-      if (mSelCon && !(flags & nsIHTMLEditor::eEditorDisabledMask))
+      flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
+      if (mSelCon && !(flags & nsIPlaintextEditor::eEditorDisabledMask))
         mSelCon->SetCaretEnabled(PR_TRUE);
     }    
     mEditor->SetFlags(flags);
@@ -2713,7 +2713,7 @@ nsGfxTextControlFrame2::AttributeChanged(nsIPresContext* aPresContext,
     mEditor->GetFlags(&flags);
     if (NS_CONTENT_ATTR_NOT_THERE != rv) 
     { // set disabled
-      flags |= nsIHTMLEditor::eEditorDisabledMask;
+      flags |= nsIPlaintextEditor::eEditorDisabledMask;
       if (mSelCon)
       {
         mSelCon->SetCaretEnabled(PR_FALSE);
@@ -2722,10 +2722,10 @@ nsGfxTextControlFrame2::AttributeChanged(nsIPresContext* aPresContext,
     }
     else 
     { // unset disabled
-      flags &= ~(nsIHTMLEditor::eEditorDisabledMask);
+      flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
       if (mSelCon)
       {
-        if (! (flags & nsIHTMLEditor::eEditorReadonlyMask))
+        if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask))
           mSelCon->SetCaretEnabled(PR_TRUE);
         mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
       }
@@ -3019,15 +3019,15 @@ nsGfxTextControlFrame2::SetTextControlFrameState(const nsAReadableString& aValue
 			if (NS_FAILED(rv)) return;
 			if (!domDoc) return;
       mSelCon->SelectAll();
-      nsCOMPtr htmlEditor = do_QueryInterface(mEditor);
+      nsCOMPtr htmlEditor = do_QueryInterface(mEditor);
 			if (!htmlEditor) return;
 
 			// get the flags, remove readonly and disabled, set the value, restore flags
 			PRUint32 flags, savedFlags;
 			mEditor->GetFlags(&savedFlags);
 			flags = savedFlags;
-			flags &= ~(nsIHTMLEditor::eEditorDisabledMask);
-			flags &= ~(nsIHTMLEditor::eEditorReadonlyMask);
+			flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
+			flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
 			mEditor->SetFlags(flags);
       if (currentValue.Length() < 1)
         mEditor->DeleteSelection(nsIEditor::eNone);