Back to index

lightning-sunbird  0.9+nobinonly
nsEditor.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Daniel Glazman <glazman@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "pratom.h"
00041 #include "nsIDOMDocument.h"
00042 #include "nsIDOMHTMLDocument.h"
00043 #include "nsIDOMHTMLElement.h"
00044 #include "nsIDOMNSHTMLElement.h"
00045 #include "nsIDOMEventReceiver.h"
00046 #include "nsIEventListenerManager.h"
00047 #include "nsIPrefBranch.h"
00048 #include "nsIPrefService.h"
00049 #include "nsUnicharUtils.h"
00050 #include "nsReadableUtils.h"
00051 
00052 #include "nsIDOMText.h"
00053 #include "nsIDOMElement.h"
00054 #include "nsIDOMAttr.h"
00055 #include "nsIDOMNode.h"
00056 #include "nsIDOMDocumentFragment.h"
00057 #include "nsIDOMNamedNodeMap.h"
00058 #include "nsIDOMNodeList.h"
00059 #include "nsIDOMRange.h"
00060 #include "nsIDOMEventListener.h"
00061 #include "nsIDOMEventGroup.h"
00062 #include "nsIDOMMouseListener.h"
00063 #include "nsIDOMFocusListener.h"
00064 #include "nsIDOMTextListener.h"
00065 #include "nsIDOMCompositionListener.h"
00066 #include "nsIDOMDragListener.h"
00067 #include "nsIDocument.h"
00068 #include "nsITransactionManager.h"
00069 #include "nsIAbsorbingTransaction.h"
00070 #include "nsIPresShell.h"
00071 #include "nsPresContext.h"
00072 #include "nsIViewManager.h"
00073 #include "nsISelection.h"
00074 #include "nsISelectionPrivate.h"
00075 #include "nsISelectionController.h"
00076 #include "nsIEnumerator.h"
00077 #include "nsIAtom.h"
00078 #include "nsICaret.h"
00079 #include "nsIEditActionListener.h"
00080 #include "nsIEditorObserver.h"
00081 #include "nsIKBStateControl.h"
00082 #include "nsIWidget.h"
00083 #include "nsIPlaintextEditor.h"
00084 #include "nsGUIEvent.h"  // nsTextEventReply
00085 
00086 #include "nsIFrame.h"  // Needed by IME code
00087 
00088 #include "nsICSSStyleSheet.h"
00089 #include "nsIDocumentStateListener.h"
00090 #include "nsITextContent.h"
00091 
00092 #include "nsIContent.h"
00093 #include "nsServiceManagerUtils.h"
00094 
00095 // transactions the editor knows how to build
00096 #include "TransactionFactory.h"
00097 #include "EditAggregateTxn.h"
00098 #include "PlaceholderTxn.h"
00099 #include "ChangeAttributeTxn.h"
00100 #include "CreateElementTxn.h"
00101 #include "InsertElementTxn.h"
00102 #include "DeleteElementTxn.h"
00103 #include "InsertTextTxn.h"
00104 #include "DeleteTextTxn.h"
00105 #include "DeleteRangeTxn.h"
00106 #include "SplitElementTxn.h"
00107 #include "JoinElementTxn.h"
00108 #include "nsStyleSheetTxns.h"
00109 #include "IMETextTxn.h"
00110 
00111 #include "nsEditor.h"
00112 #include "nsEditorUtils.h"
00113 #include "nsISelectionDisplay.h"
00114 #include "nsIInlineSpellChecker.h"
00115 #include "nsINameSpaceManager.h"
00116 #include "nsIHTMLDocument.h"
00117 #include "nsIParserService.h"
00118 
00119 #define NS_ERROR_EDITOR_NO_SELECTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,1)
00120 #define NS_ERROR_EDITOR_NO_TEXTNODE  NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,2)
00121 
00122 #ifdef NS_DEBUG_EDITOR
00123 static PRBool gNoisy = PR_FALSE;
00124 #endif
00125 
00126 
00127 // Value of "ime.password.onFocus.dontCare"
00128 static PRBool gDontCareForIMEOnFocusPassword = PR_FALSE;
00129 // Value of "ime.password.onBlur.dontCare"
00130 static PRBool gDontCareForIMEOnBlurPassword  = PR_FALSE;
00131 
00132 // Defined in nsEditorRegistration.cpp
00133 extern nsIParserService *sParserService;
00134 
00135 //---------------------------------------------------------------------------
00136 //
00137 // nsEditor: base editor class implementation
00138 //
00139 //---------------------------------------------------------------------------
00140 
00141 nsIAtom *nsEditor::gTypingTxnName;
00142 nsIAtom *nsEditor::gIMETxnName;
00143 nsIAtom *nsEditor::gDeleteTxnName;
00144 
00145 nsEditor::nsEditor()
00146 :  mModCount(0)
00147 ,  mPresShellWeak(nsnull)
00148 ,  mViewManager(nsnull)
00149 ,  mUpdateCount(0)
00150 ,  mSpellcheckCheckboxState(eTriUnset)
00151 ,  mPlaceHolderTxn(nsnull)
00152 ,  mPlaceHolderName(nsnull)
00153 ,  mPlaceHolderBatch(0)
00154 ,  mSelState(nsnull)
00155 ,  mSavedSel()
00156 ,  mRangeUpdater()
00157 ,  mAction(nsnull)
00158 ,  mDirection(eNone)
00159 ,  mIMETextRangeList(nsnull)
00160 ,  mIMETextNode(nsnull)
00161 ,  mIMETextOffset(0)
00162 ,  mIMEBufferLength(0)
00163 ,  mInIMEMode(PR_FALSE)
00164 ,  mIsIMEComposing(PR_FALSE)
00165 ,  mNeedRecoverIMEOpenState(PR_FALSE)
00166 ,  mShouldTxnSetSelection(PR_TRUE)
00167 ,  mDidPreDestroy(PR_FALSE)
00168 ,  mActionListeners(nsnull)
00169 ,  mEditorObservers(nsnull)
00170 ,  mDocDirtyState(-1)
00171 ,  mDocWeak(nsnull)
00172 ,  mPhonetic(nsnull)
00173 {
00174   //initialize member variables here
00175   if (!gTypingTxnName)
00176     gTypingTxnName = NS_NewAtom("Typing");
00177   else
00178     NS_ADDREF(gTypingTxnName);
00179   if (!gIMETxnName)
00180     gIMETxnName = NS_NewAtom("IME");
00181   else
00182     NS_ADDREF(gIMETxnName);
00183   if (!gDeleteTxnName)
00184     gDeleteTxnName = NS_NewAtom("Deleting");
00185   else
00186     NS_ADDREF(gDeleteTxnName);
00187 }
00188 
00189 nsEditor::~nsEditor()
00190 {
00191   /* first, delete the transaction manager if there is one.
00192      this will release any remaining transactions.
00193      this is important because transactions can hold onto the atoms (gTypingTxnName, ...)
00194      and to make the optimization (holding refcounted statics) work correctly, 
00195      the editor instance needs to hold the last refcount.
00196      If you get this wrong, expect to deref a garbage gTypingTxnName pointer if you bring up a second editor.
00197   */
00198   if (mTxnMgr) { 
00199     mTxnMgr = 0;
00200   }
00201   nsrefcnt refCount=0;
00202   if (gTypingTxnName)  // we addref'd in the constructor
00203   { // want to release it without nulling out the pointer.
00204     refCount = gTypingTxnName->Release();
00205     if (0==refCount) {
00206       gTypingTxnName = nsnull; 
00207     }
00208   }
00209 
00210   if (gIMETxnName)  // we addref'd in the constructor
00211   { // want to release it without nulling out the pointer.
00212     refCount = gIMETxnName->Release();
00213     if (0==refCount) {
00214       gIMETxnName = nsnull;
00215     }
00216   }
00217 
00218   if (gDeleteTxnName)  // we addref'd in the constructor
00219   { // want to release it without nulling out the pointer.
00220     refCount = gDeleteTxnName->Release();
00221     if (0==refCount) {
00222       gDeleteTxnName = nsnull;
00223     }
00224   }
00225 
00226   delete mEditorObservers;   // no need to release observers; we didn't addref them
00227   mEditorObservers = 0;
00228   
00229   if (mActionListeners)
00230   {
00231     PRInt32 i;
00232     nsIEditActionListener *listener;
00233 
00234     for (i = 0; i < mActionListeners->Count(); i++)
00235     {
00236       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
00237       NS_IF_RELEASE(listener);
00238     }
00239 
00240     delete mActionListeners;
00241     mActionListeners = 0;
00242   }
00243 
00244   /* shut down all classes that needed initialization */
00245   InsertTextTxn::ClassShutdown();
00246   IMETextTxn::ClassShutdown();
00247 
00248   delete mPhonetic;
00249  
00250   NS_IF_RELEASE(mViewManager);
00251 }
00252 
00253 NS_IMPL_ISUPPORTS5(nsEditor, nsIEditor, nsIEditor_MOZILLA_1_8_BRANCH,
00254                    nsIEditorIMESupport, nsISupportsWeakReference, nsIPhonetic)
00255 
00256 #ifdef XP_MAC
00257 #pragma mark -
00258 #pragma mark  nsIEditorMethods 
00259 #pragma mark -
00260 #endif
00261 
00262 
00263 NS_IMETHODIMP
00264 nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
00265 {
00266   NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg");
00267   if ((nsnull==aDoc) || (nsnull==aPresShell))
00268     return NS_ERROR_NULL_POINTER;
00269 
00270   mFlags = aFlags;
00271   mDocWeak = do_GetWeakReference(aDoc);  // weak reference to doc
00272   mPresShellWeak = do_GetWeakReference(aPresShell);   // weak reference to pres shell
00273   mSelConWeak = do_GetWeakReference(aSelCon);   // weak reference to selectioncontroller
00274   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
00275   if (!ps) return NS_ERROR_NOT_INITIALIZED;
00276   
00277   //set up root element if we are passed one.  
00278   if (aRoot)
00279     mRootElement = do_QueryInterface(aRoot);
00280 
00281   // Set up the DTD
00282   // XXX - in the long run we want to get this from the document, but there
00283   // is no way to do that right now.  So we leave it null here and set
00284   // up a nav html dtd in nsHTMLEditor::Init
00285 
00286   mViewManager = ps->GetViewManager();
00287   if (!mViewManager) {return NS_ERROR_NULL_POINTER;}
00288   NS_ADDREF(mViewManager);
00289 
00290   mUpdateCount=0;
00291   InsertTextTxn::ClassInit();
00292 
00293   /* initalize IME stuff */
00294   IMETextTxn::ClassInit();
00295   mIMETextNode = do_QueryInterface(nsnull);
00296   mIMETextOffset = 0;
00297   mIMEBufferLength = 0;
00298   
00299   /* Show the caret */
00300   aSelCon->SetCaretReadOnly(PR_FALSE);
00301   aSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
00302   
00303   aSelCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
00304 
00305 #if 1
00306   // THIS BLOCK CAUSES ASSERTIONS because sometimes we don't yet have
00307   // a moz-br but we do have a presshell.
00308 
00309   // Set the selection to the beginning:
00310 
00311 //hack to get around this for now.
00312   nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mSelConWeak);
00313   if (shell)
00314     BeginningOfDocument();
00315 #endif
00316 
00317   NS_POSTCONDITION(mDocWeak && mPresShellWeak, "bad state");
00318 
00319   nsresult result;
00320   nsCOMPtr<nsIPrefBranch> prefBranch = 
00321       do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
00322   if (NS_SUCCEEDED(result) && prefBranch) {
00323     PRBool val;
00324     if (NS_SUCCEEDED(prefBranch->GetBoolPref("ime.password.onFocus.dontCare", &val)))
00325       gDontCareForIMEOnFocusPassword = val;
00326     if (NS_SUCCEEDED(prefBranch->GetBoolPref("ime.password.onBlur.dontCare", &val)))
00327       gDontCareForIMEOnBlurPassword = val;
00328   }
00329 
00330   return NS_OK;
00331 }
00332 
00333 
00334 NS_IMETHODIMP
00335 nsEditor::PostCreate()
00336 {
00337   // Set up spellchecking
00338   nsresult rv = SyncRealTimeSpell();
00339   NS_ENSURE_SUCCESS(rv, rv);
00340 
00341   // Set up listeners
00342   rv = CreateEventListeners();
00343   if (NS_FAILED(rv))
00344   {
00345     RemoveEventListeners();
00346 
00347     return rv;
00348   }
00349 
00350   rv = InstallEventListeners();
00351   NS_ENSURE_SUCCESS(rv, rv);
00352 
00353   // nuke the modification count, so the doc appears unmodified
00354   // do this before we notify listeners
00355   ResetModificationCount();
00356   
00357   // update the UI with our state
00358   NotifyDocumentListeners(eDocumentCreated);
00359   NotifyDocumentListeners(eDocumentStateChanged);
00360   
00361   // Call ResetInputState() for initialization
00362   ForceCompositionEnd();
00363 
00364   return NS_OK;
00365 }
00366 
00367 nsresult
00368 nsEditor::InstallEventListeners()
00369 {
00370   NS_ENSURE_TRUE(mDocWeak && mPresShellWeak && mKeyListenerP &&
00371                  mMouseListenerP && mFocusListenerP && mTextListenerP &&
00372                  mCompositionListenerP && mDragListenerP,
00373                  NS_ERROR_NOT_INITIALIZED);
00374 
00375   nsCOMPtr<nsIDOMEventReceiver> erP = GetDOMEventReceiver();
00376 
00377   if (!erP) {
00378     RemoveEventListeners();
00379     return NS_ERROR_FAILURE;
00380   }
00381 
00382   nsresult rv = NS_OK;
00383 
00384   // register the event listeners with the listener manager
00385   nsCOMPtr<nsIDOMEventGroup> sysGroup;
00386   erP->GetSystemEventGroup(getter_AddRefs(sysGroup));
00387   nsCOMPtr<nsIEventListenerManager> elmP;
00388   erP->GetListenerManager(getter_AddRefs(elmP));
00389 
00390   if (sysGroup && elmP)
00391   {
00392     rv = elmP->AddEventListenerByType(mKeyListenerP,
00393                                       NS_LITERAL_STRING("keypress"),
00394                                       NS_EVENT_FLAG_BUBBLE |
00395                                       NS_PRIV_EVENT_UNTRUSTED_PERMITTED,
00396                                       sysGroup);
00397     NS_ASSERTION(NS_SUCCEEDED(rv),
00398                  "failed to register key listener in system group");
00399   }
00400 
00401   rv |= erP->AddEventListenerByIID(mMouseListenerP,
00402                                    NS_GET_IID(nsIDOMMouseListener));
00403 
00404   rv |= erP->AddEventListenerByIID(mFocusListenerP,
00405                                    NS_GET_IID(nsIDOMFocusListener));
00406 
00407   rv |= erP->AddEventListenerByIID(mTextListenerP,
00408                                    NS_GET_IID(nsIDOMTextListener));
00409 
00410   rv |= erP->AddEventListenerByIID(mCompositionListenerP,
00411                                    NS_GET_IID(nsIDOMCompositionListener));
00412 
00413   rv |= erP->AddEventListenerByIID(mDragListenerP,
00414                                    NS_GET_IID(nsIDOMDragListener));
00415 
00416   if (NS_FAILED(rv))
00417   {
00418     NS_ERROR("failed to register some event listeners");
00419 
00420     RemoveEventListeners();
00421   }
00422 
00423   return rv;
00424 }
00425 
00426 void
00427 nsEditor::RemoveEventListeners()
00428 {
00429   if (!mDocWeak)
00430   {
00431     return;
00432   }
00433 
00434   nsCOMPtr<nsIDOMEventReceiver> erP = GetDOMEventReceiver();
00435 
00436   if (erP)
00437   {
00438     // unregister the event listeners with the DOM event reveiver
00439 
00440     if (mKeyListenerP)
00441     {
00442       nsCOMPtr<nsIDOMEventGroup> sysGroup;
00443       erP->GetSystemEventGroup(getter_AddRefs(sysGroup));
00444       nsCOMPtr<nsIEventListenerManager> elmP;
00445       erP->GetListenerManager(getter_AddRefs(elmP));
00446       if (sysGroup && elmP)
00447       {
00448         elmP->RemoveEventListenerByType(mKeyListenerP,
00449                                         NS_LITERAL_STRING("keypress"),
00450                                         NS_EVENT_FLAG_BUBBLE |
00451                                         NS_PRIV_EVENT_UNTRUSTED_PERMITTED,
00452                                         sysGroup);
00453       }
00454     }
00455 
00456     if (mMouseListenerP)
00457     {
00458       erP->RemoveEventListenerByIID(mMouseListenerP,
00459                                     NS_GET_IID(nsIDOMMouseListener));
00460     }
00461 
00462     if (mFocusListenerP)
00463     {
00464       erP->RemoveEventListenerByIID(mFocusListenerP,
00465                                     NS_GET_IID(nsIDOMFocusListener));
00466     }
00467 
00468     if (mTextListenerP)
00469     {
00470       erP->RemoveEventListenerByIID(mTextListenerP,
00471                                     NS_GET_IID(nsIDOMTextListener));
00472     }
00473 
00474     if (mCompositionListenerP)
00475     {
00476       erP->RemoveEventListenerByIID(mCompositionListenerP,
00477                                     NS_GET_IID(nsIDOMCompositionListener));
00478     }
00479 
00480     if (mDragListenerP)
00481     {
00482       erP->RemoveEventListenerByIID(mDragListenerP,
00483                                     NS_GET_IID(nsIDOMDragListener));
00484     }
00485   }
00486 }
00487 
00488 PRBool
00489 nsEditor::GetDesiredSpellCheckState()
00490 {
00491   // Check user override on this element
00492   if (mSpellcheckCheckboxState != eTriUnset) {
00493     return (mSpellcheckCheckboxState == eTriTrue);
00494   }
00495 
00496   // Check user preferences
00497   nsresult rv;
00498   nsCOMPtr<nsIPrefBranch> prefBranch =
00499     do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00500   PRInt32 spellcheckLevel = 1;
00501   if (NS_SUCCEEDED(rv) && prefBranch) {
00502     prefBranch->GetIntPref("layout.spellcheckDefault", &spellcheckLevel);
00503   }
00504 
00505   if (spellcheckLevel == 0) {
00506     return PR_FALSE;                    // Spellchecking forced off globally
00507   }
00508 
00509   // Check for password/readonly/disabled, which are not spellchecked
00510   // regardless of DOM
00511   PRUint32 flags;
00512   if (NS_SUCCEEDED(GetFlags(&flags)) &&
00513       flags & (nsIPlaintextEditor::eEditorPasswordMask |
00514                nsIPlaintextEditor::eEditorReadonlyMask |
00515                nsIPlaintextEditor::eEditorDisabledMask)) {
00516     return PR_FALSE;
00517   }
00518 
00519   nsCOMPtr<nsIPresShell> presShell;
00520   rv = GetPresShell(getter_AddRefs(presShell));
00521   if (NS_SUCCEEDED(rv)) {
00522     nsPresContext* context = presShell->GetPresContext();
00523     if (context->Type() == nsPresContext::eContext_PrintPreview ||
00524         context->Type() == nsPresContext::eContext_Print) {
00525       return PR_FALSE;
00526     }
00527   }
00528 
00529   // Check DOM state
00530   nsCOMPtr<nsIContent> content = do_QueryInterface(GetRoot());
00531   if (!content) {
00532     return PR_FALSE;
00533   }
00534 
00535   if (content->IsNativeAnonymous()) {
00536     content = content->GetParent();
00537   }
00538 
00539   nsCOMPtr<nsIDOMNSHTMLElement_MOZILLA_1_8_BRANCH> element =
00540     do_QueryInterface(content);
00541   if (!element) {
00542     return PR_FALSE;
00543   }
00544 
00545   PRBool enable;
00546   element->GetSpellcheck(&enable);
00547 
00548   return enable;
00549 }
00550 
00551 NS_IMETHODIMP
00552 nsEditor::PreDestroy()
00553 {
00554   if (mDidPreDestroy)
00555     return NS_OK;
00556 
00557   // Let spellchecker clean up its observers etc. It is important not to
00558   // actually free the spellchecker here, since the spellchecker could have
00559   // caused flush notifications, which could have gotten here if a textbox
00560   // is being removed. Setting the spellchecker to NULL could free the
00561   // object that is still in use! It will be freed when the editor is
00562   // destroyed.
00563   if (mInlineSpellChecker)
00564     mInlineSpellChecker->Cleanup();
00565 
00566   // tell our listeners that the doc is going away
00567   NotifyDocumentListeners(eDocumentToBeDestroyed);
00568 
00569   // Unregister event listeners
00570   RemoveEventListeners();
00571 
00572   mDidPreDestroy = PR_TRUE;
00573   return NS_OK;
00574 }
00575 
00576 NS_IMETHODIMP
00577 nsEditor::GetFlags(PRUint32 *aFlags)
00578 {
00579   *aFlags = mFlags;
00580   return NS_OK;
00581 }
00582 
00583 NS_IMETHODIMP
00584 nsEditor::SetFlags(PRUint32 aFlags)
00585 {
00586   mFlags = aFlags;
00587 
00588   // Changing the flags can change whether spellchecking is on, so re-sync it
00589   SyncRealTimeSpell();
00590   return NS_OK;
00591 }
00592 
00593 NS_IMETHODIMP
00594 nsEditor::GetIsDocumentEditable(PRBool *aIsDocumentEditable)
00595 {
00596   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
00597   nsCOMPtr<nsIDOMDocument> doc;
00598   GetDocument(getter_AddRefs(doc));
00599   *aIsDocumentEditable = doc ? PR_TRUE : PR_FALSE;
00600 
00601   return NS_OK;
00602 }
00603 
00604 NS_IMETHODIMP 
00605 nsEditor::GetDocument(nsIDOMDocument **aDoc)
00606 {
00607   if (!aDoc)
00608     return NS_ERROR_NULL_POINTER;
00609   *aDoc = nsnull; // init out param
00610   NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
00611   nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
00612   if (!doc) return NS_ERROR_NOT_INITIALIZED;
00613   NS_ADDREF(*aDoc = doc);
00614   return NS_OK;
00615 }
00616 
00617 
00618 nsresult 
00619 nsEditor::GetPresShell(nsIPresShell **aPS)
00620 {
00621   if (!aPS)
00622     return NS_ERROR_NULL_POINTER;
00623   *aPS = nsnull; // init out param
00624   NS_PRECONDITION(mPresShellWeak, "bad state, null mPresShellWeak");
00625   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
00626   if (!ps) return NS_ERROR_NOT_INITIALIZED;
00627   NS_ADDREF(*aPS = ps);
00628   return NS_OK;
00629 }
00630 
00631 
00632 /* attribute string contentsMIMEType; */
00633 NS_IMETHODIMP
00634 nsEditor::GetContentsMIMEType(char * *aContentsMIMEType)
00635 {
00636   NS_ENSURE_ARG_POINTER(aContentsMIMEType);
00637   *aContentsMIMEType = ToNewCString(mContentMIMEType);
00638   return NS_OK;
00639 }
00640 
00641 NS_IMETHODIMP
00642 nsEditor::SetContentsMIMEType(const char * aContentsMIMEType)
00643 {
00644   mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : "");
00645   return NS_OK;
00646 }
00647 
00648 NS_IMETHODIMP
00649 nsEditor::GetSelectionController(nsISelectionController **aSel)
00650 {
00651   if (!aSel)
00652     return NS_ERROR_NULL_POINTER;
00653   *aSel = nsnull; // init out param
00654   NS_PRECONDITION(mSelConWeak, "bad state, null mSelConWeak");
00655   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
00656   if (!selCon) return NS_ERROR_NOT_INITIALIZED;
00657   NS_ADDREF(*aSel = selCon);
00658   return NS_OK;
00659 }
00660 
00661 
00662 NS_IMETHODIMP
00663 nsEditor::DeleteSelection(EDirection aAction)
00664 {
00665   return DeleteSelectionImpl(aAction);
00666 }
00667 
00668 
00669 
00670 NS_IMETHODIMP
00671 nsEditor::GetSelection(nsISelection **aSelection)
00672 {
00673   if (!aSelection)
00674     return NS_ERROR_NULL_POINTER;
00675   *aSelection = nsnull;
00676   nsCOMPtr<nsISelectionController> selcon = do_QueryReferent(mSelConWeak);
00677   if (!selcon) return NS_ERROR_NOT_INITIALIZED;
00678   return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection);  // does an addref
00679 }
00680 
00681 NS_IMETHODIMP 
00682 nsEditor::DoTransaction(nsITransaction *aTxn)
00683 {
00684 #ifdef NS_DEBUG_EDITOR
00685   if (gNoisy) { printf("Editor::DoTransaction ----------\n"); }
00686 #endif
00687 
00688   nsresult result = NS_OK;
00689   
00690   if (mPlaceHolderBatch && !mPlaceHolderTxn)
00691   {
00692     // it's pretty darn amazing how many different types of pointers
00693     // this transaction goes through here.  I bet this is a record.
00694     
00695     // We start off with an EditTxn since that's what the factory returns.
00696     nsRefPtr<EditTxn> editTxn;
00697     result = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(),
00698                                                    getter_AddRefs(editTxn));
00699     if (NS_FAILED(result)) { return result; }
00700     if (!editTxn) { return NS_ERROR_NULL_POINTER; }
00701 
00702     // Then we QI to an nsIAbsorbingTransaction to get at placeholder functionality
00703     nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
00704     editTxn->QueryInterface(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
00705     // have to use line above instead of "plcTxn = do_QueryInterface(editTxn);"
00706     // due to our broken interface model for transactions.
00707 
00708     // save off weak reference to placeholder txn
00709     mPlaceHolderTxn = do_GetWeakReference(plcTxn);
00710     plcTxn->Init(mPlaceHolderName, mSelState, this);
00711     mSelState = nsnull;  // placeholder txn took ownership of this pointer
00712 
00713     // finally we QI to an nsITransaction since that's what DoTransaction() expects
00714     nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
00715     DoTransaction(theTxn);  // we will recurse, but will not hit this case in the nested call
00716 
00717     if (mTxnMgr)
00718     {
00719       nsCOMPtr<nsITransaction> topTxn;
00720       result = mTxnMgr->PeekUndoStack(getter_AddRefs(topTxn));
00721       if (NS_FAILED(result)) return result;
00722       if (topTxn)
00723       {
00724         plcTxn = do_QueryInterface(topTxn);
00725         if (plcTxn)
00726         {
00727           // there is a palceholder transaction on top of the undo stack.  It is 
00728           // either the one we just created, or an earlier one that we are now merging
00729           // into.  From here on out remember this placeholder instead of the one
00730           // we just created.
00731           mPlaceHolderTxn = do_GetWeakReference(plcTxn);
00732         }
00733       }
00734     }
00735   }
00736 
00737   if (aTxn)
00738   {  
00739     // XXX: Why are we doing selection specific batching stuff here?
00740     // XXX: Most entry points into the editor have auto variables that
00741     // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
00742     // XXX: these selection batch calls no-ops.
00743     // XXX:
00744     // XXX: I suspect that this was placed here to avoid multiple
00745     // XXX: selection changed notifications from happening until after
00746     // XXX: the transaction was done. I suppose that can still happen
00747     // XXX: if an embedding application called DoTransaction() directly
00748     // XXX: to pump its own transactions through the system, but in that
00749     // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
00750     // XXX: its auto equivalent nsAutoUpdateViewBatch to ensure that
00751     // XXX: selection listeners have access to accurate frame data?
00752     // XXX:
00753     // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
00754     // XXX: we will need to make sure that they are disabled during
00755     // XXX: the init of the editor for text widgets to avoid layout
00756     // XXX: re-entry during initial reflow. - kin
00757 
00758     // get the selection and start a batch change
00759     nsCOMPtr<nsISelection>selection;
00760     result = GetSelection(getter_AddRefs(selection));
00761     if (NS_FAILED(result)) { return result; }
00762     if (!selection) { return NS_ERROR_NULL_POINTER; }
00763     nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
00764 
00765     selPrivate->StartBatchChanges();
00766 
00767     if (mTxnMgr) {
00768       result = mTxnMgr->DoTransaction(aTxn);
00769     }
00770     else {
00771       result = aTxn->DoTransaction();
00772     }
00773     if (NS_SUCCEEDED(result)) {
00774       result = DoAfterDoTransaction(aTxn);
00775     }
00776 
00777     selPrivate->EndBatchChanges(); // no need to check result here, don't lose result of operation
00778   }
00779  
00780   NS_POSTCONDITION((NS_SUCCEEDED(result)), "transaction did not execute properly\n");
00781 
00782   return result;
00783 }
00784 
00785 
00786 NS_IMETHODIMP
00787 nsEditor::EnableUndo(PRBool aEnable)
00788 {
00789   nsresult result=NS_OK;
00790 
00791   if (PR_TRUE==aEnable)
00792   {
00793     if (!mTxnMgr)
00794     {
00795       mTxnMgr = do_CreateInstance(NS_TRANSACTIONMANAGER_CONTRACTID, &result);
00796       if (NS_FAILED(result) || !mTxnMgr) {
00797         return NS_ERROR_NOT_AVAILABLE;
00798       }
00799     }
00800     mTxnMgr->SetMaxTransactionCount(-1);
00801   }
00802   else
00803   { // disable the transaction manager if it is enabled
00804     if (mTxnMgr)
00805     {
00806       mTxnMgr->Clear();
00807       mTxnMgr->SetMaxTransactionCount(0);
00808     }
00809   }
00810 
00811   return NS_OK;
00812 }
00813 
00814 
00815 NS_IMETHODIMP
00816 nsEditor::GetTransactionManager(nsITransactionManager* *aTxnManager)
00817 {
00818   NS_ENSURE_ARG_POINTER(aTxnManager);
00819   
00820   *aTxnManager = NULL;
00821   if (!mTxnMgr)
00822     return NS_ERROR_FAILURE;
00823 
00824   NS_ADDREF(*aTxnManager = mTxnMgr);
00825   return NS_OK;
00826 }
00827 
00828 
00829 NS_IMETHODIMP 
00830 nsEditor::Undo(PRUint32 aCount)
00831 {
00832 #ifdef NS_DEBUG_EDITOR
00833   if (gNoisy) { printf("Editor::Undo ----------\n"); }
00834 #endif
00835 
00836   nsresult result = NS_OK;
00837   ForceCompositionEnd();
00838 
00839   PRBool hasTxnMgr, hasTransaction = PR_FALSE;
00840   CanUndo(&hasTxnMgr, &hasTransaction);
00841   if (!hasTransaction)
00842     return result;
00843 
00844   nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone);
00845 
00846   if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
00847   {
00848     PRUint32 i=0;
00849     for ( ; i<aCount; i++)
00850     {
00851       result = mTxnMgr->UndoTransaction();
00852 
00853       if (NS_SUCCEEDED(result))
00854         result = DoAfterUndoTransaction();
00855           
00856       if (NS_FAILED(result))
00857         break;
00858     }
00859   }
00860 
00861   NotifyEditorObservers();  
00862   return result;
00863 }
00864 
00865 
00866 NS_IMETHODIMP nsEditor::CanUndo(PRBool *aIsEnabled, PRBool *aCanUndo)
00867 {
00868   if (!aIsEnabled || !aCanUndo)
00869      return NS_ERROR_NULL_POINTER;
00870   *aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get()));
00871   if (*aIsEnabled)
00872   {
00873     PRInt32 numTxns=0;
00874     mTxnMgr->GetNumberOfUndoItems(&numTxns);
00875     *aCanUndo = ((PRBool)(0!=numTxns));
00876   }
00877   else {
00878     *aCanUndo = PR_FALSE;
00879   }
00880   return NS_OK;
00881 }
00882 
00883 
00884 NS_IMETHODIMP 
00885 nsEditor::Redo(PRUint32 aCount)
00886 {
00887 #ifdef NS_DEBUG_EDITOR
00888   if (gNoisy) { printf("Editor::Redo ----------\n"); }
00889 #endif
00890 
00891   nsresult result = NS_OK;
00892 
00893   PRBool hasTxnMgr, hasTransaction = PR_FALSE;
00894   CanRedo(&hasTxnMgr, &hasTransaction);
00895   if (!hasTransaction)
00896     return result;
00897 
00898   nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone);
00899 
00900   if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
00901   {
00902     PRUint32 i=0;
00903     for ( ; i<aCount; i++)
00904     {
00905       result = mTxnMgr->RedoTransaction();
00906 
00907       if (NS_SUCCEEDED(result))
00908         result = DoAfterRedoTransaction();
00909 
00910       if (NS_FAILED(result))
00911         break;
00912     }
00913   }
00914 
00915   NotifyEditorObservers();  
00916   return result;
00917 }
00918 
00919 
00920 NS_IMETHODIMP nsEditor::CanRedo(PRBool *aIsEnabled, PRBool *aCanRedo)
00921 {
00922   if (!aIsEnabled || !aCanRedo)
00923      return NS_ERROR_NULL_POINTER;
00924 
00925   *aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get()));
00926   if (*aIsEnabled)
00927   {
00928     PRInt32 numTxns=0;
00929     mTxnMgr->GetNumberOfRedoItems(&numTxns);
00930     *aCanRedo = ((PRBool)(0!=numTxns));
00931   }
00932   else {
00933     *aCanRedo = PR_FALSE;
00934   }
00935   return NS_OK;
00936 }
00937 
00938 
00939 NS_IMETHODIMP 
00940 nsEditor::BeginTransaction()
00941 {
00942   BeginUpdateViewBatch();
00943 
00944   if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
00945   {
00946     mTxnMgr->BeginBatch();
00947   }
00948 
00949   return NS_OK;
00950 }
00951 
00952 NS_IMETHODIMP 
00953 nsEditor::EndTransaction()
00954 {
00955   if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
00956   {
00957     mTxnMgr->EndBatch();
00958   }
00959 
00960   EndUpdateViewBatch();
00961 
00962   return NS_OK;
00963 }
00964 
00965 
00966 // These two routines are similar to the above, but do not use
00967 // the transaction managers batching feature.  Instead we use
00968 // a placeholder transaction to wrap up any further transaction
00969 // while the batch is open.  The advantage of this is that
00970 // placeholder transactions can later merge, if needed.  Merging
00971 // is unavailable between transaction manager batches.
00972 
00973 NS_IMETHODIMP 
00974 nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
00975 {
00976   NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
00977   if (!mPlaceHolderBatch)
00978   {
00979     // time to turn on the batch
00980     BeginUpdateViewBatch();
00981     mPlaceHolderTxn = nsnull;
00982     mPlaceHolderName = aName;
00983     nsCOMPtr<nsISelection> selection;
00984     nsresult res = GetSelection(getter_AddRefs(selection));
00985     if (NS_FAILED(res)) return res;
00986     mSelState = new nsSelectionState();
00987     if (!mSelState)
00988       return NS_ERROR_OUT_OF_MEMORY;
00989 
00990     mSelState->SaveSelection(selection);
00991   }
00992   mPlaceHolderBatch++;
00993 
00994   return NS_OK;
00995 }
00996 
00997 NS_IMETHODIMP 
00998 nsEditor::EndPlaceHolderTransaction()
00999 {
01000   NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
01001   if (mPlaceHolderBatch == 1)
01002   {
01003     nsCOMPtr<nsISelection>selection;
01004     nsresult rv = GetSelection(getter_AddRefs(selection));
01005 
01006     nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
01007 
01008    // By making the assumption that no reflow happens during the calls
01009    // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
01010    // allow the selection to cache a frame offset which is used by the
01011    // caret drawing code. We only enable this cache here; at other times,
01012    // we have no way to know whether reflow invalidates it
01013    // See bugs 35296 and 199412.
01014     if (selPrivate) {
01015       selPrivate->SetCanCacheFrameOffset(PR_TRUE);
01016     }
01017     
01018     // time to turn off the batch
01019     EndUpdateViewBatch();
01020     // make sure selection is in view
01021     ScrollSelectionIntoView(PR_FALSE);
01022 
01023     // cached for frame offset are Not available now
01024     if (selPrivate) {
01025       selPrivate->SetCanCacheFrameOffset(PR_FALSE);
01026     }
01027 
01028     if (mSelState)
01029     {
01030       // we saved the selection state, but never got to hand it to placeholder 
01031       // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
01032       delete mSelState;
01033       mSelState = nsnull;
01034     }
01035     if (mPlaceHolderTxn)  // we might have never made a placeholder if no action took place
01036     {
01037       nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
01038       if (plcTxn) 
01039       {
01040         plcTxn->EndPlaceHolderBatch();
01041       }
01042       else  
01043       {
01044         // in the future we will check to make sure undo is off here,
01045         // since that is the only known case where the placeholdertxn would disappear on us.
01046         // For now just removing the assert.
01047       }
01048       // notify editor observers of action unless it is uncommitted IME
01049       if (!mInIMEMode) NotifyEditorObservers();
01050     }
01051   }
01052   mPlaceHolderBatch--;
01053   
01054   return NS_OK;
01055 }
01056 
01057 NS_IMETHODIMP
01058 nsEditor::ShouldTxnSetSelection(PRBool *aResult)
01059 {
01060   if (!aResult) return NS_ERROR_NULL_POINTER;
01061   *aResult = mShouldTxnSetSelection;
01062   return NS_OK;
01063 }
01064 
01065 NS_IMETHODIMP  
01066 nsEditor::SetShouldTxnSetSelection(PRBool aShould)
01067 {
01068   mShouldTxnSetSelection = aShould;
01069   return NS_OK;
01070 }
01071 
01072 NS_IMETHODIMP
01073 nsEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)
01074 {
01075   *aDocumentIsEmpty = PR_TRUE;
01076 
01077   nsIDOMElement *rootElement = GetRoot(); 
01078   if (!rootElement)
01079     return NS_ERROR_NULL_POINTER; 
01080 
01081   PRBool hasChildNodes;
01082   nsresult res = rootElement->HasChildNodes(&hasChildNodes);
01083 
01084   *aDocumentIsEmpty = !hasChildNodes;
01085 
01086   return res;
01087 }
01088 
01089 
01090 // XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
01091 NS_IMETHODIMP nsEditor::SelectAll()
01092 {
01093   if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
01094   ForceCompositionEnd();
01095 
01096   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
01097   if (!selCon) return NS_ERROR_NOT_INITIALIZED;
01098   nsCOMPtr<nsISelection> selection;
01099   nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
01100   if (NS_SUCCEEDED(result) && selection)
01101   {
01102     result = SelectEntireDocument(selection);
01103   }
01104   return result;
01105 }
01106 
01107 NS_IMETHODIMP nsEditor::BeginningOfDocument()
01108 {
01109   if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
01110 
01111   // get the selection
01112   nsCOMPtr<nsISelection> selection;
01113   nsresult result = GetSelection(getter_AddRefs(selection));
01114   if (NS_FAILED(result))
01115     return result;
01116   if (!selection)
01117     return NS_ERROR_NOT_INITIALIZED;
01118     
01119   // get the root element 
01120   nsIDOMElement *rootElement = GetRoot(); 
01121   if (!rootElement)
01122     return NS_ERROR_NULL_POINTER; 
01123   
01124   // find first editable thingy
01125   nsCOMPtr<nsIDOMNode> firstNode;
01126   result = GetFirstEditableNode(rootElement, address_of(firstNode));
01127   if (firstNode)
01128   {
01129     // if firstNode is text, set selection to beginning of the text node
01130     if (IsTextNode(firstNode)) 
01131     {
01132       result = selection->Collapse(firstNode, 0);
01133     }
01134     else
01135     { // otherwise, it's a leaf node and we set the selection just in front of it
01136       nsCOMPtr<nsIDOMNode> parentNode;
01137       result = firstNode->GetParentNode(getter_AddRefs(parentNode));
01138       if (NS_FAILED(result)) { return result; }
01139       if (!parentNode) { return NS_ERROR_NULL_POINTER; }
01140       PRInt32 offsetInParent;
01141       result = nsEditor::GetChildOffset(firstNode, parentNode, offsetInParent);
01142       if (NS_FAILED(result)) return result;
01143       result = selection->Collapse(parentNode, offsetInParent);
01144     }
01145   }
01146   else
01147   {
01148     // just the root node, set selection to inside the root
01149     result = selection->Collapse(rootElement, 0);
01150   }
01151   return result;
01152 }
01153 
01154 NS_IMETHODIMP
01155 nsEditor::EndOfDocument() 
01156 { 
01157   if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; } 
01158   nsresult res; 
01159 
01160   // get selection 
01161   nsCOMPtr<nsISelection> selection; 
01162   res = GetSelection(getter_AddRefs(selection)); 
01163   if (NS_FAILED(res)) return res; 
01164   if (!selection)   return NS_ERROR_NULL_POINTER; 
01165   
01166   // get the root element 
01167   nsIDOMElement *rootElement = GetRoot(); 
01168   if (!rootElement)
01169     return NS_ERROR_NULL_POINTER; 
01170 
01171   // get the length of the rot element 
01172   PRUint32 len; 
01173   res = GetLengthOfDOMNode(rootElement, len); 
01174   if (NS_FAILED(res)) return res;
01175 
01176   // set the selection to after the last child of the root element 
01177   return selection->Collapse(rootElement, (PRInt32)len); 
01178 } 
01179   
01180 NS_IMETHODIMP
01181 nsEditor::GetDocumentModified(PRBool *outDocModified)
01182 {
01183   if (!outDocModified)
01184     return NS_ERROR_NULL_POINTER;
01185 
01186   PRInt32  modCount = 0;
01187   GetModificationCount(&modCount);
01188 
01189   *outDocModified = (modCount != 0);
01190   return NS_OK;
01191 }
01192 
01193 NS_IMETHODIMP
01194 nsEditor::GetDocumentCharacterSet(nsACString &characterSet)
01195 {
01196   nsCOMPtr<nsIPresShell> presShell;
01197 
01198   nsresult rv = GetPresShell(getter_AddRefs(presShell));
01199   if (NS_SUCCEEDED(rv))
01200   {
01201     nsIDocument *doc = presShell->GetDocument();
01202     if (doc) {
01203       characterSet = doc->GetDocumentCharacterSet();
01204       return NS_OK;
01205     }
01206     rv = NS_ERROR_NULL_POINTER;
01207   }
01208 
01209   return rv;
01210 
01211 }
01212 
01213 NS_IMETHODIMP
01214 nsEditor::SetDocumentCharacterSet(const nsACString& characterSet)
01215 {
01216   nsCOMPtr<nsIPresShell> presShell;
01217   nsresult rv = GetPresShell(getter_AddRefs(presShell));
01218   if (NS_SUCCEEDED(rv))
01219   {
01220     nsIDocument *doc = presShell->GetDocument();
01221     if (doc) {
01222       doc->SetDocumentCharacterSet(characterSet);
01223       return NS_OK;
01224     }
01225     rv = NS_ERROR_NULL_POINTER;
01226   }
01227 
01228   return rv;
01229 }
01230 
01231 //
01232 // Get an appropriate wrap width for saving this document.
01233 // This class just uses a pref; subclasses are expected to
01234 // override if they know more about the document.
01235 //
01236 NS_IMETHODIMP
01237 nsEditor::GetWrapWidth(PRInt32 *aWrapColumn)
01238 {
01239   NS_ENSURE_ARG_POINTER(aWrapColumn);
01240   *aWrapColumn = 72;
01241 
01242   nsresult rv;
01243   nsCOMPtr<nsIPrefBranch> prefBranch =
01244     do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01245   if (NS_SUCCEEDED(rv) && prefBranch)
01246     (void) prefBranch->GetIntPref("editor.htmlWrapColumn", aWrapColumn);
01247   return NS_OK;
01248 }
01249 
01250 NS_IMETHODIMP
01251 nsEditor::Cut()
01252 {
01253   return NS_ERROR_NOT_IMPLEMENTED; 
01254 }
01255 
01256 NS_IMETHODIMP
01257 nsEditor::CanCut(PRBool *aCanCut)
01258 {
01259   return NS_ERROR_NOT_IMPLEMENTED; 
01260 }
01261 
01262 NS_IMETHODIMP
01263 nsEditor::Copy()
01264 {
01265   return NS_ERROR_NOT_IMPLEMENTED; 
01266 }
01267 
01268 NS_IMETHODIMP
01269 nsEditor::CanCopy(PRBool *aCanCut)
01270 {
01271   return NS_ERROR_NOT_IMPLEMENTED; 
01272 }
01273 
01274 NS_IMETHODIMP
01275 nsEditor::Paste(PRInt32 aSelectionType)
01276 {
01277   return NS_ERROR_NOT_IMPLEMENTED; 
01278 }
01279 
01280 NS_IMETHODIMP
01281 nsEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
01282 {
01283   return NS_ERROR_NOT_IMPLEMENTED; 
01284 }
01285 
01286 NS_IMETHODIMP
01287 nsEditor::CanDrag(nsIDOMEvent *aEvent, PRBool *aCanDrag)
01288 {
01289   return NS_ERROR_NOT_IMPLEMENTED; 
01290 }
01291 
01292 NS_IMETHODIMP
01293 nsEditor::DoDrag(nsIDOMEvent *aEvent)
01294 {
01295   return NS_ERROR_NOT_IMPLEMENTED; 
01296 }
01297 
01298 NS_IMETHODIMP
01299 nsEditor::InsertFromDrop(nsIDOMEvent *aEvent)
01300 {
01301   return NS_ERROR_NOT_IMPLEMENTED; 
01302 }
01303 
01304 
01305 NS_IMETHODIMP 
01306 nsEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & aAttribute, const nsAString & aValue)
01307 {
01308   nsRefPtr<ChangeAttributeTxn> txn;
01309   nsresult result = CreateTxnForSetAttribute(aElement, aAttribute, aValue,
01310                                              getter_AddRefs(txn));
01311   if (NS_SUCCEEDED(result))  {
01312     result = DoTransaction(txn);  
01313   }
01314   return result;
01315 }
01316 
01317 NS_IMETHODIMP 
01318 nsEditor::GetAttributeValue(nsIDOMElement *aElement, 
01319                             const nsAString & aAttribute, 
01320                             nsAString & aResultValue, 
01321                             PRBool *aResultIsSet)
01322 {
01323   if (!aResultIsSet)
01324     return NS_ERROR_NULL_POINTER;
01325   *aResultIsSet=PR_FALSE;
01326   nsresult result=NS_OK;
01327   if (aElement)
01328   {
01329     nsCOMPtr<nsIDOMAttr> attNode;
01330     result = aElement->GetAttributeNode(aAttribute, getter_AddRefs(attNode));
01331     if ((NS_SUCCEEDED(result)) && attNode)
01332     {
01333       attNode->GetSpecified(aResultIsSet);
01334       attNode->GetValue(aResultValue);
01335     }
01336   }
01337   return result;
01338 }
01339 
01340 NS_IMETHODIMP 
01341 nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString& aAttribute)
01342 {
01343   nsRefPtr<ChangeAttributeTxn> txn;
01344   nsresult result = CreateTxnForRemoveAttribute(aElement, aAttribute,
01345                                                 getter_AddRefs(txn));
01346   if (NS_SUCCEEDED(result))  {
01347     result = DoTransaction(txn);  
01348   }
01349   return result;
01350 }
01351 
01352 
01353 NS_IMETHODIMP
01354 nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
01355 {  
01356   //  mark the node dirty.
01357   nsCOMPtr<nsIDOMElement> element (do_QueryInterface(aNode));
01358   if (element)
01359     element->SetAttribute(NS_LITERAL_STRING("_moz_dirty"), EmptyString());
01360   return NS_OK;
01361 }
01362 
01363 NS_IMETHODIMP nsEditor::GetInlineSpellChecker(nsIInlineSpellChecker ** aInlineSpellChecker)
01364 {
01365   NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
01366 
01367   if (mDidPreDestroy) {
01368     // Don't allow people to get or create the spell checker once the editor
01369     // is going away.
01370     *aInlineSpellChecker = nsnull;
01371     return NS_ERROR_NOT_AVAILABLE;
01372   }
01373 
01374   if (!mInlineSpellChecker) {
01375     nsresult rv;
01376     mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
01377     NS_ENSURE_SUCCESS(rv, rv);
01378 
01379     rv = mInlineSpellChecker->Init(this);
01380     if (NS_FAILED(rv))
01381       mInlineSpellChecker = nsnull;
01382     NS_ENSURE_SUCCESS(rv, rv);
01383   }
01384 
01385   NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
01386 
01387   return NS_OK;
01388 }
01389 
01390 NS_IMETHODIMP nsEditor::GetInlineSpellCheckerOptionally(PRBool autoCreate,
01391                                   nsIInlineSpellChecker ** aInlineSpellChecker)
01392 {
01393   NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
01394 
01395   if (mDidPreDestroy) {
01396     // Don't allow people to get or create the spell checker once the editor
01397     // is going away.
01398     *aInlineSpellChecker = nsnull;
01399     return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
01400   }
01401 
01402   if (!mInlineSpellChecker) {
01403     nsresult rv;
01404     mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
01405     NS_ENSURE_SUCCESS(rv, rv);
01406 
01407     rv = mInlineSpellChecker->Init(this);
01408     if (NS_FAILED(rv))
01409       mInlineSpellChecker = nsnull;
01410     NS_ENSURE_SUCCESS(rv, rv);
01411   }
01412 
01413   NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
01414 
01415   return NS_OK;
01416 }
01417 
01418 NS_IMETHODIMP nsEditor::SyncRealTimeSpell()
01419 {
01420   PRBool enable = GetDesiredSpellCheckState();
01421 
01422   nsCOMPtr<nsIInlineSpellChecker> spellChecker;
01423   GetInlineSpellCheckerOptionally(enable, getter_AddRefs(spellChecker));
01424 
01425   if (spellChecker) {
01426     spellChecker->SetEnableRealTimeSpell(enable);
01427   }
01428 
01429   return NS_OK;
01430 }
01431 
01432 NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(PRBool enable)
01433 {
01434   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
01435 
01436   return SyncRealTimeSpell();
01437 }
01438 
01439 #ifdef XP_MAC
01440 #pragma mark -
01441 #pragma mark  main node manipulation routines 
01442 #pragma mark -
01443 #endif
01444 
01445 NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag,
01446                                    nsIDOMNode *    aParent,
01447                                    PRInt32         aPosition,
01448                                    nsIDOMNode **   aNewNode)
01449 {
01450   PRInt32 i;
01451   nsIEditActionListener *listener;
01452 
01453   nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::eNext);
01454   
01455   if (mActionListeners)
01456   {
01457     for (i = 0; i < mActionListeners->Count(); i++)
01458     {
01459       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01460       if (listener)
01461         listener->WillCreateNode(aTag, aParent, aPosition);
01462     }
01463   }
01464 
01465   nsRefPtr<CreateElementTxn> txn;
01466   nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition,
01467                                               getter_AddRefs(txn));
01468   if (NS_SUCCEEDED(result)) 
01469   {
01470     result = DoTransaction(txn);  
01471     if (NS_SUCCEEDED(result)) 
01472     {
01473       result = txn->GetNewNode(aNewNode);
01474       NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::DoTransaction succeeded.");
01475     }
01476   }
01477   
01478   mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
01479   
01480   if (mActionListeners)
01481   {
01482     for (i = 0; i < mActionListeners->Count(); i++)
01483     {
01484       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01485       if (listener)
01486         listener->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result);
01487     }
01488   }
01489 
01490   return result;
01491 }
01492 
01493 
01494 NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
01495                                    nsIDOMNode * aParent,
01496                                    PRInt32      aPosition)
01497 {
01498   PRInt32 i;
01499   nsIEditActionListener *listener;
01500   nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
01501 
01502   if (mActionListeners)
01503   {
01504     for (i = 0; i < mActionListeners->Count(); i++)
01505     {
01506       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01507       if (listener)
01508         listener->WillInsertNode(aNode, aParent, aPosition);
01509     }
01510   }
01511 
01512   nsRefPtr<InsertElementTxn> txn;
01513   nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition,
01514                                               getter_AddRefs(txn));
01515   if (NS_SUCCEEDED(result))  {
01516     result = DoTransaction(txn);  
01517   }
01518 
01519   mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
01520 
01521   if (mActionListeners)
01522   {
01523     for (i = 0; i < mActionListeners->Count(); i++)
01524     {
01525       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01526       if (listener)
01527         listener->DidInsertNode(aNode, aParent, aPosition, result);
01528     }
01529   }
01530 
01531   return result;
01532 }
01533 
01534 
01535 NS_IMETHODIMP 
01536 nsEditor::SplitNode(nsIDOMNode * aNode,
01537                     PRInt32      aOffset,
01538                     nsIDOMNode **aNewLeftNode)
01539 {
01540   PRInt32 i;
01541   nsIEditActionListener *listener;
01542   nsAutoRules beginRulesSniffing(this, kOpSplitNode, nsIEditor::eNext);
01543 
01544   if (mActionListeners)
01545   {
01546     for (i = 0; i < mActionListeners->Count(); i++)
01547     {
01548       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01549       if (listener)
01550         listener->WillSplitNode(aNode, aOffset);
01551     }
01552   }
01553 
01554   nsRefPtr<SplitElementTxn> txn;
01555   nsresult result = CreateTxnForSplitNode(aNode, aOffset, getter_AddRefs(txn));
01556   if (NS_SUCCEEDED(result))  
01557   {
01558     result = DoTransaction(txn);
01559     if (NS_SUCCEEDED(result))
01560     {
01561       result = txn->GetNewNode(aNewLeftNode);
01562       NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
01563     }
01564   }
01565 
01566   mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
01567 
01568   if (mActionListeners)
01569   {
01570     for (i = 0; i < mActionListeners->Count(); i++)
01571     {
01572       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01573       if (listener)
01574       {
01575         nsIDOMNode *ptr = (aNewLeftNode) ? *aNewLeftNode : 0;
01576         listener->DidSplitNode(aNode, aOffset, ptr, result);
01577       }
01578     }
01579   }
01580 
01581   return result;
01582 }
01583 
01584 
01585 
01586 NS_IMETHODIMP
01587 nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
01588                     nsIDOMNode * aRightNode,
01589                     nsIDOMNode * aParent)
01590 {
01591   PRInt32 i, offset;
01592   nsIEditActionListener *listener;
01593   nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious);
01594 
01595   // remember some values; later used for saved selection updating.
01596   // find the offset between the nodes to be joined.
01597   nsresult result = GetChildOffset(aRightNode, aParent, offset);
01598   if (NS_FAILED(result)) return result;
01599   // find the number of children of the lefthand node
01600   PRUint32 oldLeftNodeLen;
01601   result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen);
01602   if (NS_FAILED(result)) return result;
01603 
01604   if (mActionListeners)
01605   {
01606     for (i = 0; i < mActionListeners->Count(); i++)
01607     {
01608       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01609       if (listener)
01610         listener->WillJoinNodes(aLeftNode, aRightNode, aParent);
01611     }
01612   }
01613 
01614   nsRefPtr<JoinElementTxn> txn;
01615   result = CreateTxnForJoinNode(aLeftNode, aRightNode, getter_AddRefs(txn));
01616   if (NS_SUCCEEDED(result))  {
01617     result = DoTransaction(txn);  
01618   }
01619 
01620   mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
01621   
01622   if (mActionListeners)
01623   {
01624     for (i = 0; i < mActionListeners->Count(); i++)
01625     {
01626       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01627       if (listener)
01628         listener->DidJoinNodes(aLeftNode, aRightNode, aParent, result);
01629     }
01630   }
01631 
01632   return result;
01633 }
01634 
01635 
01636 NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
01637 {
01638   PRInt32 i, offset;
01639   nsCOMPtr<nsIDOMNode> parent;
01640   nsIEditActionListener *listener;
01641   nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious);
01642 
01643   // save node location for selection updating code.
01644   nsresult result = GetNodeLocation(aElement, address_of(parent), &offset);
01645   if (NS_FAILED(result)) return result;
01646 
01647   if (mActionListeners)
01648   {
01649     for (i = 0; i < mActionListeners->Count(); i++)
01650     {
01651       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01652       if (listener)
01653         listener->WillDeleteNode(aElement);
01654     }
01655   }
01656 
01657   nsRefPtr<DeleteElementTxn> txn;
01658   result = CreateTxnForDeleteElement(aElement, getter_AddRefs(txn));
01659   if (NS_SUCCEEDED(result))  {
01660     result = DoTransaction(txn);  
01661   }
01662 
01663   if (mActionListeners)
01664   {
01665     for (i = 0; i < mActionListeners->Count(); i++)
01666     {
01667       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
01668       if (listener)
01669         listener->DidDeleteNode(aElement, result);
01670     }
01671   }
01672 
01673   return result;
01674 }
01675 
01677 // ReplaceContainer: replace inNode with a new node (outNode) which is contructed 
01678 //                   to be of type aNodeType.  Put inNodes children into outNode.
01679 //                   Callers responsibility to make sure inNode's children can 
01680 //                   go in outNode.
01681 nsresult
01682 nsEditor::ReplaceContainer(nsIDOMNode *inNode, 
01683                            nsCOMPtr<nsIDOMNode> *outNode, 
01684                            const nsAString &aNodeType,
01685                            const nsAString *aAttribute,
01686                            const nsAString *aValue,
01687                            PRBool aCloneAttributes)
01688 {
01689   if (!inNode || !outNode)
01690     return NS_ERROR_NULL_POINTER;
01691   nsCOMPtr<nsIDOMNode> parent;
01692   PRInt32 offset;
01693   nsresult res = GetNodeLocation(inNode, address_of(parent), &offset);
01694   if (NS_FAILED(res)) return res;
01695 
01696   // create new container
01697   nsCOMPtr<nsIContent> newContent;
01698 
01699   //new call to use instead to get proper HTML element, bug# 39919
01700   res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent));
01701   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(newContent);
01702   if (NS_FAILED(res)) return res;
01703     *outNode = do_QueryInterface(elem);
01704   
01705   // set attribute if needed
01706   if (aAttribute && aValue && !aAttribute->IsEmpty())
01707   {
01708     res = elem->SetAttribute(*aAttribute, *aValue);
01709     if (NS_FAILED(res)) return res;
01710   }
01711   if (aCloneAttributes)
01712   {
01713     nsCOMPtr<nsIDOMNode>newNode = do_QueryInterface(elem);
01714     res = CloneAttributes(newNode, inNode);
01715     if (NS_FAILED(res)) return res;
01716   }
01717   
01718   // notify our internal selection state listener
01719   // (Note: A nsAutoSelectionReset object must be created
01720   //  before calling this to initialize mRangeUpdater)
01721   nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, *outNode);
01722   
01723   nsCOMPtr<nsIDOMNode> child;
01724   PRBool bHasMoreChildren;
01725   inNode->HasChildNodes(&bHasMoreChildren);
01726   while (bHasMoreChildren)
01727   {
01728     inNode->GetFirstChild(getter_AddRefs(child));
01729     res = DeleteNode(child);
01730     if (NS_FAILED(res)) return res;
01731     res = InsertNode(child, *outNode, -1);
01732     if (NS_FAILED(res)) return res;
01733     inNode->HasChildNodes(&bHasMoreChildren);
01734   }
01735 
01736   // insert new container into tree
01737   res = InsertNode( *outNode, parent, offset);
01738   if (NS_FAILED(res)) return res;
01739   
01740   // delete old container
01741   return DeleteNode(inNode);
01742 }
01743 
01745 // RemoveContainer: remove inNode, reparenting its children into their
01746 //                  the parent of inNode
01747 //
01748 nsresult
01749 nsEditor::RemoveContainer(nsIDOMNode *inNode)
01750 {
01751   if (!inNode)
01752     return NS_ERROR_NULL_POINTER;
01753   nsCOMPtr<nsIDOMNode> parent;
01754   PRInt32 offset;
01755   
01756   nsresult res = GetNodeLocation(inNode, address_of(parent), &offset);
01757   if (NS_FAILED(res)) return res;
01758   
01759   // loop through the child nodes of inNode and promote them
01760   // into inNode's parent.
01761   PRBool bHasMoreChildren;
01762   inNode->HasChildNodes(&bHasMoreChildren);
01763   nsCOMPtr<nsIDOMNodeList> nodeList;
01764   res = inNode->GetChildNodes(getter_AddRefs(nodeList));
01765   if (NS_FAILED(res)) return res;
01766   if (!nodeList) return NS_ERROR_NULL_POINTER;
01767   PRUint32 nodeOrigLen;
01768   nodeList->GetLength(&nodeOrigLen);
01769 
01770   // notify our internal selection state listener
01771   nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, inNode, parent, offset, nodeOrigLen);
01772                                    
01773   nsCOMPtr<nsIDOMNode> child;
01774   while (bHasMoreChildren)
01775   {
01776     inNode->GetLastChild(getter_AddRefs(child));
01777     res = DeleteNode(child);
01778     if (NS_FAILED(res)) return res;
01779     res = InsertNode(child, parent, offset);
01780     if (NS_FAILED(res)) return res;
01781     inNode->HasChildNodes(&bHasMoreChildren);
01782   }
01783   return DeleteNode(inNode);
01784 }
01785 
01786 
01788 // InsertContainerAbove:  insert a new parent for inNode, returned in outNode,
01789 //                   which is contructed to be of type aNodeType.  outNode becomes
01790 //                   a child of inNode's earlier parent.
01791 //                   Callers responsibility to make sure inNode's can be child
01792 //                   of outNode, and outNode can be child of old parent.
01793 nsresult
01794 nsEditor::InsertContainerAbove( nsIDOMNode *inNode, 
01795                                 nsCOMPtr<nsIDOMNode> *outNode, 
01796                                 const nsAString &aNodeType,
01797                                 const nsAString *aAttribute,
01798                                 const nsAString *aValue)
01799 {
01800   if (!inNode || !outNode)
01801     return NS_ERROR_NULL_POINTER;
01802   nsCOMPtr<nsIDOMNode> parent;
01803   PRInt32 offset;
01804   nsresult res = GetNodeLocation(inNode, address_of(parent), &offset);
01805   if (NS_FAILED(res)) return res;
01806 
01807   // create new container
01808   nsCOMPtr<nsIContent> newContent;
01809 
01810   //new call to use instead to get proper HTML element, bug# 39919
01811   res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent));
01812   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(newContent);
01813   if (NS_FAILED(res)) return res;
01814   *outNode = do_QueryInterface(elem);
01815   
01816   // set attribute if needed
01817   if (aAttribute && aValue && !aAttribute->IsEmpty())
01818   {
01819     res = elem->SetAttribute(*aAttribute, *aValue);
01820     if (NS_FAILED(res)) return res;
01821   }
01822   
01823   // notify our internal selection state listener
01824   nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
01825   
01826   // put inNode in new parent, outNode
01827   res = DeleteNode(inNode);
01828   if (NS_FAILED(res)) return res;
01829   res = InsertNode(inNode, *outNode, 0);
01830   if (NS_FAILED(res)) return res;
01831   
01832   // put new parent in doc
01833   return InsertNode(*outNode, parent, offset);
01834 }
01835 
01837 // MoveNode:  move aNode to {aParent,aOffset}
01838 nsresult
01839 nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
01840 {
01841   if (!aNode || !aParent)
01842     return NS_ERROR_NULL_POINTER;
01843 
01844   nsCOMPtr<nsIDOMNode> oldParent;
01845   PRInt32 oldOffset;
01846   nsresult res = GetNodeLocation(aNode, address_of(oldParent), &oldOffset);
01847   
01848   if (aOffset == -1)
01849   {
01850     PRUint32 unsignedOffset;
01851     // magic value meaning "move to end of aParent"
01852     res = GetLengthOfDOMNode(aParent, unsignedOffset);
01853     if (NS_FAILED(res)) return res;
01854     aOffset = (PRInt32)unsignedOffset;
01855   }
01856   
01857   // don't do anything if it's already in right place
01858   if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK;
01859   
01860   // notify our internal selection state listener
01861   nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset, aParent, aOffset);
01862   
01863   // need to adjust aOffset if we are moving aNode further along in its current parent
01864   if ((aParent == oldParent.get()) && (oldOffset < aOffset)) 
01865   {
01866     aOffset--;  // this is because when we delete aNode, it will make the offsets after it off by one
01867   }
01868 
01869   // put aNode in new parent
01870   res = DeleteNode(aNode);
01871   if (NS_FAILED(res)) return res;
01872   return InsertNode(aNode, aParent, aOffset);
01873 }
01874 
01875 #ifdef XP_MAC
01876 #pragma mark -
01877 #pragma mark  editor observer maintainance
01878 #pragma mark -
01879 #endif
01880 
01881 NS_IMETHODIMP
01882 nsEditor::AddEditorObserver(nsIEditorObserver *aObserver)
01883 {
01884   // we don't keep ownership of the observers.  They must
01885   // remove themselves as observers before they are destroyed.
01886   
01887   if (!aObserver)
01888     return NS_ERROR_NULL_POINTER;
01889 
01890   if (!mEditorObservers)
01891   {
01892     mEditorObservers = new nsVoidArray();
01893 
01894     if (!mEditorObservers)
01895       return NS_ERROR_OUT_OF_MEMORY;
01896   }
01897 
01898   // Make sure the listener isn't already on the list
01899   if (mEditorObservers->IndexOf(aObserver) == -1) 
01900   {
01901     if (!mEditorObservers->AppendElement((void *)aObserver))
01902       return NS_ERROR_FAILURE;
01903   }
01904 
01905   return NS_OK;
01906 }
01907 
01908 
01909 NS_IMETHODIMP
01910 nsEditor::RemoveEditorObserver(nsIEditorObserver *aObserver)
01911 {
01912   if (!aObserver || !mEditorObservers)
01913     return NS_ERROR_FAILURE;
01914 
01915   if (!mEditorObservers->RemoveElement((void *)aObserver))
01916     return NS_ERROR_FAILURE;
01917 
01918   return NS_OK;
01919 }
01920 
01921 void nsEditor::NotifyEditorObservers(void)
01922 {
01923   if (mEditorObservers)
01924   {
01925     PRInt32 i;
01926     nsIEditorObserver *observer;
01927     for (i = 0; i < mEditorObservers->Count(); i++)
01928     {
01929       observer = (nsIEditorObserver*)mEditorObservers->ElementAt(i);
01930       if (observer)
01931         observer->EditAction();
01932     }
01933   }
01934 }
01935 
01936 #ifdef XP_MAC
01937 #pragma mark -
01938 #pragma mark  action listener maintainance
01939 #pragma mark -
01940 #endif
01941 
01942 NS_IMETHODIMP
01943 nsEditor::AddEditActionListener(nsIEditActionListener *aListener)
01944 {
01945   if (!aListener)
01946     return NS_ERROR_NULL_POINTER;
01947 
01948   if (!mActionListeners)
01949   {
01950     mActionListeners = new nsVoidArray();
01951 
01952     if (!mActionListeners)
01953       return NS_ERROR_OUT_OF_MEMORY;
01954   }
01955 
01956   // Make sure the listener isn't already on the list
01957   if (mActionListeners->IndexOf(aListener) == -1) 
01958   {
01959     if (!mActionListeners->AppendElement((void *)aListener))
01960       return NS_ERROR_FAILURE;
01961     else
01962       NS_ADDREF(aListener);
01963   }
01964 
01965   return NS_OK;
01966 }
01967 
01968 
01969 NS_IMETHODIMP
01970 nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener)
01971 {
01972   if (!aListener || !mActionListeners)
01973     return NS_ERROR_FAILURE;
01974 
01975   if (!mActionListeners->RemoveElement((void *)aListener))
01976     return NS_ERROR_FAILURE;
01977 
01978   NS_IF_RELEASE(aListener);
01979 
01980   if (mActionListeners->Count() < 1)
01981   {
01982     delete mActionListeners;
01983     mActionListeners = 0;
01984   }
01985 
01986   return NS_OK;
01987 }
01988 
01989 
01990 #ifdef XP_MAC
01991 #pragma mark -
01992 #pragma mark  docstate listener maintainance
01993 #pragma mark -
01994 #endif
01995 
01996 
01997 NS_IMETHODIMP
01998 nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener)
01999 {
02000   if (!aListener)
02001     return NS_ERROR_NULL_POINTER;
02002 
02003   nsresult rv = NS_OK;
02004   
02005   if (!mDocStateListeners)
02006   {
02007     rv = NS_NewISupportsArray(getter_AddRefs(mDocStateListeners));
02008     if (NS_FAILED(rv)) return rv;
02009   }
02010   
02011   nsCOMPtr<nsISupports> iSupports = do_QueryInterface(aListener, &rv);
02012   if (NS_FAILED(rv)) return rv;
02013     
02014   // is it already in the list?
02015   PRInt32 foundIndex;
02016   if (NS_SUCCEEDED(mDocStateListeners->GetIndexOf(iSupports, &foundIndex)) && foundIndex != -1)
02017     return NS_OK;
02018 
02019   return mDocStateListeners->AppendElement(iSupports);
02020 }
02021 
02022 
02023 NS_IMETHODIMP
02024 nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener)
02025 {
02026   if (!aListener || !mDocStateListeners)
02027     return NS_ERROR_NULL_POINTER;
02028 
02029   nsresult rv;
02030   nsCOMPtr<nsISupports> iSupports = do_QueryInterface(aListener, &rv);
02031   if (NS_FAILED(rv)) return rv;
02032 
02033   return mDocStateListeners->RemoveElement(iSupports);
02034 }
02035 
02036 
02037 #ifdef XP_MAC
02038 #pragma mark -
02039 #pragma mark  misc 
02040 #pragma mark -
02041 #endif
02042 
02043 NS_IMETHODIMP nsEditor::OutputToString(const nsAString& aFormatType,
02044                                        PRUint32 aFlags,
02045                                        nsAString& aOutputString)
02046 {
02047   // these should be implemented by derived classes.
02048   return NS_ERROR_NOT_IMPLEMENTED;
02049 }
02050 
02051 NS_IMETHODIMP
02052 nsEditor::OutputToStream(nsIOutputStream* aOutputStream,
02053                          const nsAString& aFormatType,
02054                          const nsACString& aCharsetOverride,
02055                          PRUint32 aFlags)
02056 {
02057   // these should be implemented by derived classes.
02058   return NS_ERROR_NOT_IMPLEMENTED;
02059 }
02060 
02061 NS_IMETHODIMP
02062 nsEditor::DumpContentTree()
02063 {
02064 #ifdef DEBUG
02065   nsCOMPtr<nsIContent> root = do_QueryInterface(mRootElement);
02066   if (root)  root->List(stdout);
02067 #endif
02068   return NS_OK;
02069 }
02070 
02071 
02072 NS_IMETHODIMP
02073 nsEditor::DebugDumpContent()
02074 {
02075 #ifdef DEBUG
02076   nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak);
02077   if (!doc) return NS_ERROR_NOT_INITIALIZED;
02078 
02079   nsCOMPtr<nsIDOMHTMLElement>bodyElem;
02080   doc->GetBody(getter_AddRefs(bodyElem));
02081   nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElem);
02082   if (content)
02083     content->List();
02084 #endif
02085   return NS_OK;
02086 }
02087 
02088 
02089 NS_IMETHODIMP
02090 nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
02091 {
02092 #ifdef DEBUG
02093   NS_NOTREACHED("This should never get called. Overridden by subclasses");
02094 #endif
02095   return NS_OK;
02096 }
02097 
02098 #ifdef XP_MAC
02099 #pragma mark -
02100 #pragma mark  support for selection preservation
02101 #pragma mark -
02102 #endif
02103 
02104 PRBool   
02105 nsEditor::ArePreservingSelection()
02106 {
02107   return !(mSavedSel.IsEmpty());
02108 }
02109 
02110 nsresult 
02111 nsEditor::PreserveSelectionAcrossActions(nsISelection *aSel)
02112 {
02113   mSavedSel.SaveSelection(aSel);
02114   mRangeUpdater.RegisterSelectionState(mSavedSel);
02115   return NS_OK;
02116 }
02117 
02118 nsresult 
02119 nsEditor::RestorePreservedSelection(nsISelection *aSel)
02120 {
02121   if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE;
02122   mSavedSel.RestoreSelection(aSel);
02123   StopPreservingSelection();
02124   return NS_OK;
02125 }
02126 
02127 void     
02128 nsEditor::StopPreservingSelection()
02129 {
02130   mRangeUpdater.DropSelectionState(mSavedSel);
02131   mSavedSel.MakeEmpty();
02132 }
02133 
02134 
02135 #ifdef XP_MAC
02136 #pragma mark -
02137 #pragma mark  nsIEditorIMESupport 
02138 #pragma mark -
02139 #endif
02140 
02141 //
02142 // The BeingComposition method is called from the Editor Composition event listeners.
02143 //
02144 NS_IMETHODIMP
02145 nsEditor::QueryComposition(nsTextEventReply* aReply)
02146 {
02147   nsresult result;
02148   nsCOMPtr<nsISelection> selection;
02149   nsCOMPtr<nsISelectionController> selcon = do_QueryReferent(mSelConWeak);
02150   if (selcon)
02151     selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
02152 
02153   if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
02154   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
02155   if (!ps) return NS_ERROR_NOT_INITIALIZED;
02156   nsCOMPtr<nsICaret> caretP; 
02157   result = ps->GetCaret(getter_AddRefs(caretP));
02158   
02159   if (NS_SUCCEEDED(result) && caretP) {
02160     if (aReply) {
02161       caretP->SetCaretDOMSelection(selection);
02162 
02163       // XXX_kin: BEGIN HACK! HACK! HACK!
02164       // XXX_kin:
02165       // XXX_kin: This is lame! The IME stuff needs caret coordinates
02166       // XXX_kin: synchronously, but the editor could be using async
02167       // XXX_kin: updates (reflows and paints) for performance reasons.
02168       // XXX_kin: In order to give IME what it needs, we have to temporarily
02169       // XXX_kin: switch to sync updating during this call so that the
02170       // XXX_kin: nsAutoUpdateViewBatch can force sync reflows and paints
02171       // XXX_kin: so that we get back accurate caret coordinates.
02172 
02173       PRUint32 flags = 0;
02174 
02175       if (NS_SUCCEEDED(GetFlags(&flags)) &&
02176           (flags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask))
02177       {
02178         PRBool restoreFlags = PR_FALSE;
02179 
02180         if (NS_SUCCEEDED(SetFlags(flags & (~nsIPlaintextEditor::eEditorUseAsyncUpdatesMask))))
02181         {
02182            // Scope the viewBatch within this |if| block so that we
02183            // force synchronous reflows and paints before restoring
02184            // our editor flags below.
02185 
02186            nsAutoUpdateViewBatch viewBatch(this);
02187            restoreFlags = PR_TRUE;
02188         }
02189 
02190         // Restore the previous set of flags!
02191 
02192         if (restoreFlags)
02193           SetFlags(flags);
02194       }
02195 
02196 
02197       // XXX_kin: END HACK! HACK! HACK!
02198 
02199       result = caretP->GetCaretCoordinates(nsICaret::eIMECoordinates, selection,
02200                                     &(aReply->mCursorPosition), &(aReply->mCursorIsCollapsed), nsnull);
02201     }
02202   }
02203   return result;
02204 }
02205 NS_IMETHODIMP
02206 nsEditor::BeginComposition(nsTextEventReply* aReply)
02207 {
02208 #ifdef DEBUG_tague
02209   printf("nsEditor::StartComposition\n");
02210 #endif
02211   nsresult ret = QueryComposition(aReply);
02212   mInIMEMode = PR_TRUE;
02213   if (mPhonetic)
02214     mPhonetic->Truncate(0);
02215 
02216   // If user changes the IME open state, don't recover it.
02217   // Because the user may not want to change the state.
02218   if (mNeedRecoverIMEOpenState)
02219     mNeedRecoverIMEOpenState = PR_FALSE;
02220 
02221   return ret;
02222 }
02223 
02224 NS_IMETHODIMP
02225 nsEditor::EndComposition(void)
02226 {
02227   if (!mInIMEMode) return NS_OK; // nothing to do
02228   
02229   nsresult result = NS_OK;
02230 
02231   // commit the IME transaction..we can get at it via the transaction mgr.
02232   // Note that this means IME won't work without an undo stack!
02233   if (mTxnMgr) 
02234   {
02235     nsCOMPtr<nsITransaction> txn;
02236     result = mTxnMgr->PeekUndoStack(getter_AddRefs(txn));  
02237     nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
02238     if (plcTxn)
02239     {
02240       result = plcTxn->Commit();
02241     }
02242   }
02243 
02244   /* reset the data we need to construct a transaction */
02245   mIMETextNode = do_QueryInterface(nsnull);
02246   mIMETextOffset = 0;
02247   mIMEBufferLength = 0;
02248   mInIMEMode = PR_FALSE;
02249   mIsIMEComposing = PR_FALSE;
02250 
02251   // notify editor observers of action
02252   NotifyEditorObservers();
02253 
02254   return result;
02255 }
02256 
02257 NS_IMETHODIMP
02258 nsEditor::SetCompositionString(const nsAString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply)
02259 {
02260   return NS_ERROR_NOT_IMPLEMENTED;
02261 }
02262 
02263 NS_IMETHODIMP
02264 nsEditor::GetPhonetic(nsAString& aPhonetic)
02265 {
02266   if (mPhonetic)
02267     aPhonetic = *mPhonetic;
02268   else
02269     aPhonetic.Truncate(0);
02270 
02271   return NS_OK;
02272 }
02273 
02274 
02275 static nsresult
02276 GetEditorContentWindow(nsIPresShell *aPresShell, nsIDOMElement *aRoot, nsIWidget **aResult)
02277 {
02278   if (!aPresShell || !aRoot || !aResult)
02279     return NS_ERROR_NULL_POINTER;
02280 
02281   *aResult = 0;
02282 
02283   nsresult result;
02284   nsCOMPtr<nsIContent> content = do_QueryInterface(aRoot);
02285 
02286   if (!content)
02287     return NS_ERROR_FAILURE;
02288 
02289   nsIFrame *frame = 0; // Not ref counted
02290 
02291   result = aPresShell->GetPrimaryFrameFor(content, &frame);
02292 
02293   if (NS_FAILED(result))
02294     return result;
02295 
02296   if (!frame)
02297     return NS_ERROR_FAILURE;
02298 
02299   // Check first to see if this frame contains a view with a native widget.
02300   nsIView *view = frame->GetViewExternal();
02301 
02302   if (view)
02303   {
02304     *aResult = view->GetWidget();
02305 
02306     if (*aResult) {
02307       NS_ADDREF(*aResult);
02308       return NS_OK;
02309     }
02310   }
02311 
02312   // frame doesn't have a view with a widget, so call GetWindow()
02313   // which will traverse it's parent hierarchy till it finds a
02314   // view with a widget.
02315 
02316   *aResult = frame->GetWindow();
02317   if (!*aResult)
02318     return NS_ERROR_FAILURE;
02319 
02320   NS_ADDREF(*aResult);
02321   return NS_OK;
02322 }
02323 
02324 nsresult
02325 nsEditor::GetKBStateControl(nsIKBStateControl **aKBSC)
02326 {
02327   if (!aKBSC)
02328     return NS_ERROR_NULL_POINTER;
02329   *aKBSC = nsnull;
02330   nsCOMPtr<nsIPresShell> shell;
02331   nsresult res = GetPresShell(getter_AddRefs(shell));
02332 
02333   if (NS_FAILED(res))
02334     return res;
02335 
02336   if (!shell)
02337     return NS_ERROR_FAILURE;
02338 
02339   nsCOMPtr<nsIWidget> widget;
02340   res = GetEditorContentWindow(shell, GetRoot(), getter_AddRefs(widget));
02341   if (NS_FAILED(res))
02342     return res;
02343 
02344   nsCOMPtr<nsIKBStateControl> kb = do_QueryInterface(widget);
02345   if (!kb)
02346     return NS_ERROR_NOT_INITIALIZED;
02347 
02348   NS_ADDREF(*aKBSC = kb);
02349 
02350   return NS_OK;
02351 }
02352 
02353 NS_IMETHODIMP
02354 nsEditor::ForceCompositionEnd()
02355 {
02356 
02357 // We can test mInIMEMode and do some optimization for Mac and Window
02358 // Howerver, since UNIX support over-the-spot, we cannot rely on that 
02359 // flag for Unix.
02360 // We should use nsILookAndFeel to resolve this
02361 
02362 #if defined(XP_MAC) || defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_OS2)
02363   if(! mInIMEMode)
02364     return NS_OK;
02365 #endif
02366 
02367 #ifdef XP_UNIX
02368   if(mFlags & nsIPlaintextEditor::eEditorPasswordMask)
02369        return NS_OK;
02370 #endif
02371 
02372   nsCOMPtr<nsIKBStateControl> kb;
02373   nsresult res = GetKBStateControl(getter_AddRefs(kb));
02374   if (NS_FAILED(res))
02375     return res;
02376 
02377   if (kb) {
02378     res = kb->ResetInputState();
02379     if (NS_FAILED(res)) 
02380       return res;
02381   }
02382 
02383   return NS_OK;
02384 }
02385 
02386 NS_IMETHODIMP
02387 nsEditor::NotifyIMEOnFocus()
02388 {
02389   mNeedRecoverIMEOpenState = PR_FALSE;
02390 
02391   nsCOMPtr<nsIKBStateControl> kb;
02392   nsresult res = GetKBStateControl(getter_AddRefs(kb));
02393   if (NS_FAILED(res))
02394     return res;
02395 
02396   if (!kb)
02397     return NS_OK;
02398 
02399   res = kb->CancelIMEComposition();
02400   if (NS_FAILED(res))
02401     kb->ResetInputState();
02402 
02403   if(gDontCareForIMEOnFocusPassword
02404       || !(mFlags & nsIPlaintextEditor::eEditorPasswordMask))
02405     return NS_OK;
02406 
02407   PRBool isOpen;
02408   res = kb->GetIMEOpenState(&isOpen);
02409   if (NS_FAILED(res)) 
02410     return res;
02411 
02412   if (isOpen) {
02413     res = kb->SetIMEOpenState(PR_FALSE);
02414     if (NS_FAILED(res)) 
02415       return res;
02416   }
02417 
02418   mNeedRecoverIMEOpenState = isOpen;
02419 
02420   return NS_OK;
02421 }
02422 
02423 NS_IMETHODIMP
02424 nsEditor::NotifyIMEOnBlur()
02425 {
02426   if (!mNeedRecoverIMEOpenState)
02427     return NS_OK;
02428   mNeedRecoverIMEOpenState = PR_FALSE;
02429 
02430   if (gDontCareForIMEOnBlurPassword
02431       || !(mFlags & nsIPlaintextEditor::eEditorPasswordMask))
02432     return NS_OK;
02433 
02434   nsCOMPtr<nsIKBStateControl> kb;
02435   nsresult res = GetKBStateControl(getter_AddRefs(kb));
02436   if (NS_FAILED(res))
02437     return res;
02438 
02439   if (kb) {
02440     res = kb->SetIMEOpenState(PR_TRUE);
02441     if (NS_FAILED(res)) 
02442       return res;
02443   }
02444 
02445   return NS_OK;
02446 }
02447 
02448 NS_IMETHODIMP
02449 nsEditor::GetReconversionString(nsReconversionEventReply* aReply)
02450 {
02451   return NS_ERROR_NOT_IMPLEMENTED;
02452 }
02453 
02454 NS_IMETHODIMP
02455 nsEditor::GetQueryCaretRect(nsQueryCaretRectEventReply* aReply)
02456 {
02457   nsCOMPtr<nsISelection> selection;
02458   nsresult rv = GetSelection(getter_AddRefs(selection));
02459   if (NS_FAILED(rv))
02460     return rv;
02461 
02462   if (!mPresShellWeak)
02463     return NS_ERROR_NOT_INITIALIZED;
02464 
02465   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
02466   if (!ps)
02467     return NS_ERROR_NOT_INITIALIZED;
02468 
02469   nsCOMPtr<nsICaret> caretP;
02470   rv = ps->GetCaret(getter_AddRefs(caretP));
02471 
02472   if (NS_FAILED(rv) || !caretP)
02473     return rv;
02474 
02475   PRBool cursorIsCollapsed;
02476   rv = caretP->GetCaretCoordinates(nsICaret::eIMECoordinates, selection,
02477                                    &aReply->mCaretRect, &cursorIsCollapsed, nsnull);
02478   if (NS_SUCCEEDED(rv))
02479     aReply->mRectIsValid = PR_TRUE;
02480   return rv;
02481 }
02482 
02483 #ifdef XP_MAC
02484 #pragma mark -
02485 #pragma mark  public nsEditor methods 
02486 #pragma mark -
02487 #endif
02488 /* Non-interface, public methods */
02489 
02490 
02491 NS_IMETHODIMP 
02492 nsEditor::GetRootElement(nsIDOMElement **aRootElement)
02493 {
02494   if (!aRootElement)
02495     return NS_ERROR_NULL_POINTER;
02496 
02497   if (mRootElement)
02498   {
02499     // if we have cached the body element, use that
02500     *aRootElement = mRootElement;
02501     NS_ADDREF(*aRootElement);
02502     return NS_OK;
02503   }
02504 
02505   *aRootElement = 0;
02506 
02507   NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
02508   nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak);
02509   if (!doc) return NS_ERROR_NOT_INITIALIZED;
02510 
02511   // Use the documents body element as the editor root if we didn't
02512   // get a root element during initialization.
02513 
02514   nsCOMPtr<nsIDOMHTMLElement> bodyElement; 
02515   nsresult result = doc->GetBody(getter_AddRefs(bodyElement));
02516   if (NS_FAILED(result))
02517     return result;
02518 
02519   if (!bodyElement)
02520     return NS_ERROR_NULL_POINTER;
02521 
02522   mRootElement = bodyElement;
02523   *aRootElement = bodyElement;
02524   NS_ADDREF(*aRootElement);
02525 
02526   return NS_OK;
02527 }
02528 
02529 
02532 NS_IMETHODIMP
02533 nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
02534 {
02535   mAction = opID;
02536   mDirection = aDirection;
02537   return NS_OK;
02538 }
02539 
02540 
02543 NS_IMETHODIMP
02544 nsEditor::EndOperation()
02545 {
02546   mAction = nsnull;
02547   mDirection = eNone;
02548   return NS_OK;
02549 }
02550 
02551 NS_IMETHODIMP
02552 nsEditor::CloneAttribute(const nsAString & aAttribute,
02553                          nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
02554 {
02555   if (!aDestNode || !aSourceNode)
02556     return NS_ERROR_NULL_POINTER;
02557 
02558   nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
02559   nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
02560   if (!destElement || !sourceElement)
02561     return NS_ERROR_NO_INTERFACE;
02562 
02563   nsAutoString attrValue;
02564   PRBool isAttrSet;
02565   nsresult rv = GetAttributeValue(sourceElement,
02566                                   aAttribute,
02567                                   attrValue,
02568                                   &isAttrSet);
02569   if (NS_FAILED(rv))
02570     return rv;
02571   if (isAttrSet)
02572     rv = SetAttribute(destElement, aAttribute, attrValue);
02573   else
02574     rv = RemoveAttribute(destElement, aAttribute);
02575 
02576   return rv;
02577 }
02578 
02579 // Objects must be DOM elements
02580 NS_IMETHODIMP
02581 nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
02582 {
02583   if (!aDestNode || !aSourceNode)
02584     return NS_ERROR_NULL_POINTER;
02585 
02586   nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
02587   nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
02588   if (!destElement || !sourceElement)
02589     return NS_ERROR_NO_INTERFACE;
02590 
02591   nsCOMPtr<nsIDOMNamedNodeMap> sourceAttributes;
02592   sourceElement->GetAttributes(getter_AddRefs(sourceAttributes));
02593   nsCOMPtr<nsIDOMNamedNodeMap> destAttributes;
02594   destElement->GetAttributes(getter_AddRefs(destAttributes));
02595   if (!sourceAttributes || !destAttributes)
02596     return NS_ERROR_FAILURE;
02597 
02598   nsAutoEditBatch beginBatching(this);
02599 
02600   // Use transaction system for undo only if destination
02601   //   is already in the document
02602   nsIDOMElement *rootElement = GetRoot();
02603   if (!rootElement)
02604     return NS_ERROR_NULL_POINTER;
02605 
02606   PRBool destInBody = PR_TRUE;
02607   nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootElement);
02608   nsCOMPtr<nsIDOMNode> p = aDestNode;
02609   while (p && p != rootNode)
02610   {
02611     nsCOMPtr<nsIDOMNode> tmp;
02612     if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp)
02613     {
02614       destInBody = PR_FALSE;
02615       break;
02616     }
02617     p = tmp;
02618   }
02619 
02620   PRUint32 sourceCount;
02621   sourceAttributes->GetLength(&sourceCount);
02622   PRUint32 i, destCount;
02623   destAttributes->GetLength(&destCount);
02624   nsCOMPtr<nsIDOMNode> attrNode;
02625 
02626   // Clear existing attributes
02627   for (i = 0; i < destCount; i++)
02628   {
02629     // always remove item number 0 (first item in list)
02630     if( NS_SUCCEEDED(destAttributes->Item(0, getter_AddRefs(attrNode))) && attrNode)
02631     {
02632       nsCOMPtr<nsIDOMAttr> destAttribute = do_QueryInterface(attrNode);
02633       if (destAttribute)
02634       {
02635         nsAutoString str;
02636         if (NS_SUCCEEDED(destAttribute->GetName(str)))
02637         {
02638           if (destInBody)
02639             RemoveAttribute(destElement, str);
02640           else
02641             destElement->RemoveAttribute(str);
02642         }
02643       }
02644     }
02645   }
02646 
02647   nsresult result = NS_OK;
02648 
02649   // Set just the attributes that the source element has
02650   for (i = 0; i < sourceCount; i++)
02651   {
02652     if( NS_SUCCEEDED(sourceAttributes->Item(i, getter_AddRefs(attrNode))) && attrNode)
02653     {
02654       nsCOMPtr<nsIDOMAttr> sourceAttribute = do_QueryInterface(attrNode);
02655       if (sourceAttribute)
02656       {
02657         nsAutoString sourceAttrName;
02658         if (NS_SUCCEEDED(sourceAttribute->GetName(sourceAttrName)))
02659         {
02660           nsAutoString sourceAttrValue;
02661           /*
02662           Presence of an attribute in the named node map indicates that it was set on the 
02663           element even if it has no value.
02664           */
02665           if (NS_SUCCEEDED(sourceAttribute->GetValue(sourceAttrValue)))
02666           {
02667             if (destInBody) {
02668               result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, PR_FALSE);
02669             }
02670             else {
02671               // the element is not inserted in the document yet, we don't want to put a
02672               // transaction on the UndoStack
02673               result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, PR_TRUE);
02674             }
02675           } else {
02676             // Do we ever get here?
02677 #if DEBUG_cmanske
02678             printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n");
02679 #endif
02680           }
02681         }        
02682       }
02683     }
02684   }
02685   return result;
02686 }
02687 
02688 #ifdef XP_MAC
02689 #pragma mark -
02690 #pragma mark  Protected and static methods 
02691 #pragma mark -
02692 #endif
02693 
02694 NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(PRBool aScrollToAnchor)
02695 {
02696   nsCOMPtr<nsISelectionController> selCon;
02697   if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
02698   {
02699     PRInt16 region = nsISelectionController::SELECTION_FOCUS_REGION;
02700 
02701     if (aScrollToAnchor)
02702       region = nsISelectionController::SELECTION_ANCHOR_REGION;
02703 
02704     PRBool syncScroll = PR_TRUE;
02705     PRUint32 flags = 0;
02706 
02707     if (NS_SUCCEEDED(GetFlags(&flags)))
02708     {
02709       // If the editor is relying on asynchronous reflows, we have
02710       // to use asynchronous requests to scroll, so that the scrolling happens
02711       // after reflow requests are processed.
02712 
02713       syncScroll = !(flags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask);
02714     }
02715 
02716     selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
02717                                     region, syncScroll);
02718   }
02719 
02720   return NS_OK;
02721 }
02722 
02724 nsresult nsEditor::GetTextNodeTag(nsAString& aOutString)
02725 {
02726   aOutString.Truncate();
02727   static nsString *gTextNodeTag=nsnull;
02728   if (!gTextNodeTag)
02729   {
02730     if ( (gTextNodeTag = new nsString) == 0 )
02731       return NS_ERROR_OUT_OF_MEMORY;
02732     gTextNodeTag->AssignLiteral("special text node tag");
02733   }
02734   aOutString = *gTextNodeTag;
02735   return NS_OK;
02736 }
02737 
02738 
02739 NS_IMETHODIMP nsEditor::InsertTextImpl(const nsAString& aStringToInsert, 
02740                                           nsCOMPtr<nsIDOMNode> *aInOutNode, 
02741                                           PRInt32 *aInOutOffset,
02742                                           nsIDOMDocument *aDoc)
02743 {
02744   // NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based
02745   // class to turn off txn selection updating.  Caller also turned on rules sniffing
02746   // if desired.
02747   
02748   if (!aInOutNode || !*aInOutNode || !aInOutOffset || !aDoc) return NS_ERROR_NULL_POINTER;
02749   if (!mInIMEMode && aStringToInsert.IsEmpty()) return NS_OK;
02750   nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(*aInOutNode);
02751   PRInt32 offset = *aInOutOffset;
02752   nsresult res;
02753   if (mInIMEMode)
02754   {
02755     if (!nodeAsText)
02756     {
02757       // create a text node
02758       res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText));
02759       if (NS_FAILED(res)) return res;
02760       if (!nodeAsText) return NS_ERROR_NULL_POINTER;
02761       nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(nodeAsText);
02762       // then we insert it into the dom tree
02763       res = InsertNode(newNode, *aInOutNode, offset);
02764       if (NS_FAILED(res)) return res;
02765       offset = 0;
02766     }
02767     res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset);
02768     if (NS_FAILED(res)) return res;
02769   }
02770   else
02771   {
02772     if (nodeAsText)
02773     {
02774       // we are inserting text into an existing text node.
02775       res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset);
02776       if (NS_FAILED(res)) return res;
02777       *aInOutOffset += aStringToInsert.Length();
02778     }
02779     else
02780     {
02781       // we are inserting text into a non-text node
02782       // first we have to create a textnode (this also populates it with the text)
02783       res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText));
02784       if (NS_FAILED(res)) return res;
02785       if (!nodeAsText) return NS_ERROR_NULL_POINTER;
02786       nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(nodeAsText);
02787       // then we insert it into the dom tree
02788       res = InsertNode(newNode, *aInOutNode, offset);
02789       if (NS_FAILED(res)) return res;
02790       *aInOutNode = newNode;
02791       *aInOutOffset = aStringToInsert.Length();
02792     }
02793   }
02794   return res;
02795 }
02796 
02797 
02798 NS_IMETHODIMP nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, 
02799                                                      nsIDOMCharacterData *aTextNode, 
02800                                                      PRInt32 aOffset, PRBool suppressIME)
02801 {
02802   nsRefPtr<EditTxn> txn;
02803   nsresult result;
02804   // suppressIME s used when editor must insert text, yet this text is not
02805   // part of current ime operation.  example: adjusting whitespace around an ime insertion.
02806   if (mIMETextRangeList && mInIMEMode && !suppressIME)
02807   {
02808     if (!mIMETextNode)
02809     {
02810       mIMETextNode = aTextNode;
02811       mIMETextOffset = aOffset;
02812     }
02813     PRUint16 len ;
02814     result = mIMETextRangeList->GetLength(&len);
02815     if (NS_SUCCEEDED(result) && len > 0)
02816     {
02817       nsCOMPtr<nsIPrivateTextRange> range;
02818       for (PRUint16 i = 0; i < len; i++) 
02819       {
02820         result = mIMETextRangeList->Item(i, getter_AddRefs(range));
02821         if (NS_SUCCEEDED(result) && range)
02822         {
02823           PRUint16 type;
02824           result = range->GetRangeType(&type);
02825           if (NS_SUCCEEDED(result)) 
02826           {
02827             if (type == nsIPrivateTextRange::TEXTRANGE_RAWINPUT) 
02828             {
02829               PRUint16 start, end;
02830               result = range->GetRangeStart(&start);
02831               if (NS_SUCCEEDED(result)) 
02832               {
02833                 result = range->GetRangeEnd(&end);
02834                 if (NS_SUCCEEDED(result)) 
02835                 {
02836                   if (!mPhonetic)
02837                     mPhonetic = new nsString();
02838                   if (mPhonetic)
02839                   {
02840                     nsAutoString tmp(aStringToInsert);                  
02841                     tmp.Mid(*mPhonetic, start, end-start);
02842                   }
02843                 }
02844               }
02845             } // if
02846           }
02847         } // if
02848       } // for
02849     } // if
02850 
02851     nsRefPtr<IMETextTxn> imeTxn;
02852     result = CreateTxnForIMEText(aStringToInsert, getter_AddRefs(imeTxn));
02853     txn = imeTxn;
02854   }
02855   else
02856   {
02857     nsRefPtr<InsertTextTxn> insertTxn;
02858     result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset,
02859                                     getter_AddRefs(insertTxn));
02860     txn = insertTxn;
02861   }
02862   if (NS_FAILED(result)) return result;
02863 
02864   // let listeners know whats up
02865   PRInt32 i;
02866   nsIEditActionListener *listener;
02867   if (mActionListeners)
02868   {
02869     for (i = 0; i < mActionListeners->Count(); i++)
02870     {
02871       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
02872       if (listener)
02873         listener->WillInsertText(aTextNode, aOffset, aStringToInsert);
02874     }
02875   }
02876   
02877   // XXX we may not need these view batches anymore.  This is handled at a higher level now I believe
02878   BeginUpdateViewBatch();
02879   result = DoTransaction(txn);
02880   EndUpdateViewBatch();
02881 
02882   mRangeUpdater.SelAdjInsertText(aTextNode, aOffset, aStringToInsert);
02883   
02884   // let listeners know what happened
02885   if (mActionListeners)
02886   {
02887     for (i = 0; i < mActionListeners->Count(); i++)
02888     {
02889       listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
02890       if (listener)
02891         listener->DidInsertText(aTextNode, aOffset, aStringToInsert, result);
02892     }
02893   }
02894 
02895   // Added some cruft here for bug 43366.  Layout was crashing because we left an 
02896   // empty text node lying around in the document.  So I delete empty text nodes
02897   // caused by IME.  I have to mark the IME transaction as "fixed", which means
02898   // that furure ime txns won't merge with it.  This is because we don't want
02899   // future ime txns trying to put their text into a node that is no longer in
02900   // the document.  This does not break undo/redo, because all these txns are 
02901   // wrapped in a parent PlaceHolder txn, and placeholder txns are already 
02902   // savvy to having multiple ime txns inside them.
02903   
02904   // delete empty ime text node if there is one
02905   if (mInIMEMode && mIMETextNode)
02906   {
02907     PRUint32 len;
02908     mIMETextNode->GetLength(&len);
02909     if (!len)
02910     {
02911       DeleteNode(mIMETextNode);
02912       mIMETextNode = nsnull;
02913       ((IMETextTxn*)txn.get())->MarkFixed();  // mark the ime txn "fixed"
02914     }
02915   }
02916   
02917   return result;
02918 }
02919 
02920 
02921 NS_IMETHODIMP nsEditor::SelectEntireDocument(nsISelection *aSelection)
02922 {
02923   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
02924 
02925   nsIDOMElement *rootElement = GetRoot();
02926   if (!rootElement) { return NS_ERROR_NOT_INITIALIZED; }
02927 
02928   return aSelection->SelectAllChildren(rootElement);
02929 }
02930 
02931 
02932 nsresult nsEditor::GetFirstEditableNode(nsIDOMNode *aRoot, nsCOMPtr<nsIDOMNode> *outFirstNode)
02933 {
02934   if (!aRoot || !outFirstNode) return NS_ERROR_NULL_POINTER;
02935   nsresult rv = NS_OK;
02936   *outFirstNode = nsnull;
02937 
02938   nsCOMPtr<nsIDOMNode> node = GetLeftmostChild(aRoot);
02939   if (node && !IsEditable(node))
02940   {
02941     nsCOMPtr<nsIDOMNode> next;
02942     rv = GetNextNode(node, PR_TRUE, address_of(next));
02943     node = next;
02944   }
02945   
02946   if (node != aRoot)
02947     *outFirstNode = node;
02948 
02949   return rv;
02950 }
02951 
02952 #ifdef XXX_DEAD_CODE
02953 // jfrancis wants to keep this method around for reference
02954 nsresult nsEditor::GetLastEditableNode(nsIDOMNode *aRoot, nsCOMPtr<nsIDOMNode> *outLastNode)
02955 {
02956   if (!aRoot || !outLastNode) return NS_ERROR_NULL_POINTER;
02957   nsresult rv = NS_OK;
02958   *outLastNode = nsnull;
02959 
02960   nsCOMPtr<nsIDOMNode> node = GetRightmostChild(aRoot);
02961   if (node && !IsEditable(node))
02962   {
02963     nsCOMPtr<nsIDOMNode> next;
02964     rv = GetPriorNode(node, PR_TRUE, address_of(next));
02965     node = next;
02966   }
02967 
02968   if (node != aRoot)
02969     *outLastNode = node;
02970 
02971   return rv;
02972 }
02973 #endif
02974 
02975 
02976 NS_IMETHODIMP
02977 nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType)
02978 {
02979   if (!mDocStateListeners)
02980     return NS_OK;    // maybe there just aren't any.
02981  
02982   PRUint32 numListeners;
02983   nsresult rv = mDocStateListeners->Count(&numListeners);
02984   if (NS_FAILED(rv)) return rv;
02985 
02986   PRUint32 i;
02987   switch (aNotificationType)
02988   {
02989     case eDocumentCreated:
02990       for (i = 0; i < numListeners;i++)
02991       {
02992         nsCOMPtr<nsIDocumentStateListener> thisListener =
02993           do_QueryElementAt(mDocStateListeners, i);
02994         if (thisListener)
02995         {
02996           rv = thisListener->NotifyDocumentCreated();
02997           if (NS_FAILED(rv))
02998             break;
02999         }
03000       }
03001       break;
03002       
03003     case eDocumentToBeDestroyed:
03004       for (i = 0; i < numListeners;i++)
03005       {
03006         nsCOMPtr<nsIDocumentStateListener> thisListener =
03007           do_QueryElementAt(mDocStateListeners, i);
03008         if (thisListener)
03009         {
03010           rv = thisListener->NotifyDocumentWillBeDestroyed();
03011           if (NS_FAILED(rv))
03012             break;
03013         }
03014       }
03015       break;
03016   
03017     case eDocumentStateChanged:
03018       {
03019         PRBool docIsDirty;
03020         rv = GetDocumentModified(&docIsDirty);
03021         if (NS_FAILED(rv)) return rv;
03022         
03023         if (docIsDirty == mDocDirtyState)
03024           return NS_OK;
03025         
03026         mDocDirtyState = (PRInt8)docIsDirty;
03027         
03028         for (i = 0; i < numListeners;i++)
03029         {
03030           nsCOMPtr<nsIDocumentStateListener> thisListener =
03031             do_QueryElementAt(mDocStateListeners, i);
03032           if (thisListener)
03033           {
03034             rv = thisListener->NotifyDocumentStateChanged(mDocDirtyState);
03035             if (NS_FAILED(rv))
03036               break;
03037           }
03038         }
03039       }
03040       break;
03041     
03042     default:
03043       NS_NOTREACHED("Unknown notification");
03044   }
03045 
03046   return rv;
03047 }
03048 
03049 
03050 NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsAString & aStringToInsert,
03051                                                nsIDOMCharacterData *aTextNode,
03052                                                PRInt32 aOffset,
03053                                                InsertTextTxn ** aTxn)
03054 {
03055   if (!aTextNode || !aTxn) return NS_ERROR_NULL_POINTER;
03056   nsresult result;
03057 
03058   result = TransactionFactory::GetNewTransaction(InsertTextTxn::GetCID(), (EditTxn **)aTxn);
03059   if (NS_FAILED(result)) return result;
03060   if (!*aTxn) return NS_ERROR_OUT_OF_MEMORY;
03061   result = (*aTxn)->Init(aTextNode, aOffset, aStringToInsert, this);
03062   return result;
03063 }
03064 
03065 
03066 NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
03067                               PRUint32             aOffset,
03068                               PRUint32             aLength)
03069 {
03070   nsRefPtr<DeleteTextTxn> txn;
03071   nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength,
03072                                            getter_AddRefs(txn));
03073   nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious);
03074   if (NS_SUCCEEDED(result))  
03075   {
03076     // let listeners know whats up
03077     PRInt32 i;
03078     nsIEditActionListener *listener;
03079     if (mActionListeners)
03080     {
03081       for (i = 0; i < mActionListeners->Count(); i++)
03082       {
03083         listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
03084         if (listener)
03085           listener->WillDeleteText(aElement, aOffset, aLength);
03086       }
03087     }
03088     
03089     result = DoTransaction(txn); 
03090     
03091     // let listeners know what happened
03092     if (mActionListeners)
03093     {
03094       for (i = 0; i < mActionListeners->Count(); i++)
03095       {
03096         listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
03097         if (listener)
03098           listener->DidDeleteText(aElement, aOffset, aLength, result);
03099       }
03100     }
03101   }
03102   return result;
03103 }
03104 
03105 
03106 NS_IMETHODIMP nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement,
03107                                                PRUint32             aOffset,
03108                                                PRUint32             aLength,
03109                                                DeleteTextTxn      **aTxn)
03110 {
03111   if (!aElement)
03112     return NS_ERROR_NULL_POINTER;
03113 
03114   nsresult result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)aTxn);
03115   if (NS_SUCCEEDED(result))  {
03116     result = (*aTxn)->Init(this, aElement, aOffset, aLength, &mRangeUpdater);
03117   }
03118   return result;
03119 }
03120 
03121 
03122 
03123 
03124 NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode,
03125                                          PRUint32    aOffset,
03126                                          SplitElementTxn **aTxn)
03127 {
03128   if (!aNode)
03129     return NS_ERROR_NULL_POINTER;
03130 
03131   nsresult result = TransactionFactory::GetNewTransaction(SplitElementTxn::GetCID(), (EditTxn **)aTxn);
03132   if (NS_FAILED(result))
03133     return result;
03134 
03135   return (*aTxn)->Init(this, aNode, aOffset);
03136 }
03137 
03138 NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode  *aLeftNode,
03139                                              nsIDOMNode  *aRightNode,
03140                                              JoinElementTxn **aTxn)
03141 {
03142   if (!aLeftNode || !aRightNode)
03143     return NS_ERROR_NULL_POINTER;
03144 
03145   nsresult result = TransactionFactory::GetNewTransaction(JoinElementTxn::GetCID(), (EditTxn **)aTxn);
03146   if (NS_SUCCEEDED(result))  {
03147     result = (*aTxn)->Init(this, aLeftNode, aRightNode);
03148   }
03149   return result;
03150 }
03151 
03152 
03153 // END nsEditor core implementation
03154 
03155 #ifdef XP_MAC
03156 #pragma mark -
03157 #pragma mark  nsEditor public static helper methods 
03158 #pragma mark -
03159 #endif
03160 
03161 // BEGIN nsEditor public helper methods
03162 
03163 nsresult
03164 nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode,
03165                         PRInt32      aOffset,
03166                         nsIDOMNode*  aNewLeftNode,
03167                         nsIDOMNode*  aParent)
03168 {
03169 #ifdef NS_DEBUG_EDITOR
03170   if (gNoisy) { printf("SplitNodeImpl: left=%p, right=%p, offset=%d\n", (void*)aNewLeftNode, (void*)aExistingRightNode, aOffset); }
03171 #endif
03172 
03173   NS_ASSERTION(((nsnull!=aExistingRightNode) &&
03174                 (nsnull!=aNewLeftNode) &&
03175                 (nsnull!=aParent)),
03176                 "null arg");
03177   nsresult result;
03178   if ((nsnull!=aExistingRightNode) &&
03179       (nsnull!=aNewLeftNode) &&
03180       (nsnull!=aParent))
03181   {
03182     // get selection
03183     nsCOMPtr<nsISelection> selection;
03184     result = GetSelection(getter_AddRefs(selection));
03185     if (NS_FAILED(result)) return result;
03186     if (!selection) return NS_ERROR_NULL_POINTER;
03187 
03188     // remember some selection points
03189     nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
03190     PRInt32 selStartOffset, selEndOffset;
03191     result = GetStartNodeAndOffset(selection, address_of(selStartNode), &selStartOffset);
03192     if (NS_FAILED(result)) selStartNode = nsnull;  // if selection is cleared, remember that
03193     result = GetEndNodeAndOffset(selection, address_of(selEndNode), &selEndOffset);
03194     if (NS_FAILED(result)) selStartNode = nsnull;  // if selection is cleared, remember that
03195 
03196     nsCOMPtr<nsIDOMNode> resultNode;
03197     result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
03198     //printf("  after insert\n"); content->List();  // DEBUG
03199     if (NS_SUCCEEDED(result))
03200     {
03201       // split the children between the 2 nodes
03202       // at this point, aExistingRightNode has all the children
03203       // move all the children whose index is < aOffset to aNewLeftNode
03204       if (0<=aOffset) // don't bother unless we're going to move at least one child
03205       {
03206         // if it's a text node, just shuffle around some text
03207         nsCOMPtr<nsIDOMCharacterData> rightNodeAsText( do_QueryInterface(aExistingRightNode) );
03208         nsCOMPtr<nsIDOMCharacterData> leftNodeAsText( do_QueryInterface(aNewLeftNode) );
03209         if (leftNodeAsText && rightNodeAsText)
03210         {
03211           // fix right node
03212           nsAutoString leftText;
03213           rightNodeAsText->SubstringData(0, aOffset, leftText);
03214           rightNodeAsText->DeleteData(0, aOffset);
03215           // fix left node
03216           leftNodeAsText->SetData(leftText);
03217           // moose          
03218         }
03219         else
03220         {  // otherwise it's an interior node, so shuffle around the children
03221            // go through list backwards so deletes don't interfere with the iteration
03222           nsCOMPtr<nsIDOMNodeList> childNodes;
03223           result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
03224           if ((NS_SUCCEEDED(result)) && (childNodes))
03225           {
03226             PRInt32 i=aOffset-1;
03227             for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
03228             {
03229               nsCOMPtr<nsIDOMNode> childNode;
03230               result = childNodes->Item(i, getter_AddRefs(childNode));
03231               if ((NS_SUCCEEDED(result)) && (childNode))
03232               {
03233                 result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
03234                 //printf("  after remove\n"); content->List();  // DEBUG
03235                 if (NS_SUCCEEDED(result))
03236                 {
03237                   nsCOMPtr<nsIDOMNode> firstChild;
03238                   aNewLeftNode->GetFirstChild(getter_AddRefs(firstChild));
03239                   result = aNewLeftNode->InsertBefore(childNode, firstChild, getter_AddRefs(resultNode));
03240                   //printf("  after append\n"); content->List();  // DEBUG
03241                 }
03242               }
03243             }
03244           }        
03245         }
03246         // handle selection
03247         if (GetShouldTxnSetSelection())
03248         {
03249           // editor wants us to set selection at split point
03250           selection->Collapse(aNewLeftNode, aOffset);
03251         }
03252         else if (selStartNode)   
03253         {
03254           // else adjust the selection if needed.  if selStartNode is null, then there was no selection.
03255           // HACK: this is overly simplified - multi-range selections need more work than this
03256           if (selStartNode.get() == aExistingRightNode)
03257           {
03258             if (selStartOffset < aOffset)
03259             {
03260               selStartNode = aNewLeftNode;
03261             }
03262             else
03263             {
03264               selStartOffset -= aOffset;
03265             }
03266           }
03267           if (selEndNode.get() == aExistingRightNode)
03268           {
03269             if (selEndOffset < aOffset)
03270             {
03271               selEndNode = aNewLeftNode;
03272             }
03273             else
03274             {
03275               selEndOffset -= aOffset;
03276             }
03277           }
03278           selection->Collapse(selStartNode,selStartOffset);
03279           selection->Extend(selEndNode,selEndOffset);
03280         }
03281       }
03282     }
03283   }
03284   else
03285     result = NS_ERROR_INVALID_ARG;
03286 
03287   return result;
03288 }
03289 
03290 nsresult
03291 nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep,
03292                         nsIDOMNode * aNodeToJoin,
03293                         nsIDOMNode * aParent,
03294                         PRBool       aNodeToKeepIsFirst)
03295 {
03296   NS_ASSERTION(aNodeToKeep && aNodeToJoin && aParent, "null arg");
03297   nsresult result;
03298   if (aNodeToKeep && aNodeToJoin && aParent)
03299   {
03300     // get selection
03301     nsCOMPtr<nsISelection> selection;
03302     GetSelection(getter_AddRefs(selection));
03303     if (!selection) return NS_ERROR_NULL_POINTER;
03304 
03305     // remember some selection points
03306     nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
03307     PRInt32 selStartOffset, selEndOffset, joinOffset, keepOffset;
03308     result = GetStartNodeAndOffset(selection, address_of(selStartNode), &selStartOffset);
03309     if (NS_FAILED(result)) selStartNode = nsnull;
03310     result = GetEndNodeAndOffset(selection, address_of(selEndNode), &selEndOffset);
03311     // Joe or Kin should comment here on why the following line is not a copy/paste error
03312     if (NS_FAILED(result)) selStartNode = nsnull;
03313 
03314     nsCOMPtr<nsIDOMNode> leftNode;
03315     if (aNodeToKeepIsFirst)
03316       leftNode = aNodeToKeep;
03317     else
03318       leftNode = aNodeToJoin;
03319 
03320     PRUint32 firstNodeLength;
03321     result = GetLengthOfDOMNode(leftNode, firstNodeLength);
03322     if (NS_FAILED(result)) return result;
03323     nsCOMPtr<nsIDOMNode> parent;
03324     result = GetNodeLocation(aNodeToJoin, address_of(parent), &joinOffset);
03325     if (NS_FAILED(result)) return result;
03326     result = GetNodeLocation(aNodeToKeep, address_of(parent), &keepOffset);
03327     if (NS_FAILED(result)) return result;
03328     
03329     // if selection endpoint is between the nodes, remember it as being
03330     // in the one that is going away instead.  This simplifies later selection
03331     // adjustment logic at end of this method.
03332     if (selStartNode)
03333     {
03334       if (selStartNode == parent)
03335       {
03336         if (aNodeToKeepIsFirst)
03337         {
03338           if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset))
03339           {
03340             selStartNode = aNodeToJoin; 
03341             selStartOffset = 0;
03342           }
03343         }
03344         else
03345         {
03346           if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset))
03347           {
03348             selStartNode = aNodeToJoin; 
03349             selStartOffset = firstNodeLength;
03350           }
03351         }
03352       }
03353       if (selEndNode == parent)
03354       {
03355         if (aNodeToKeepIsFirst)
03356         {
03357           if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset))
03358           {
03359             selEndNode = aNodeToJoin; 
03360             selEndOffset = 0;
03361           }
03362         }
03363         else
03364         {
03365           if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset))
03366           {
03367             selEndNode = aNodeToJoin; 
03368             selEndOffset = firstNodeLength;
03369           }
03370         }
03371       }
03372     }
03373     // ok, ready to do join now.
03374     // if it's a text node, just shuffle around some text
03375     nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) );
03376     nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) );
03377     if (keepNodeAsText && joinNodeAsText)
03378     {
03379       nsAutoString rightText;
03380       nsAutoString leftText;
03381       if (aNodeToKeepIsFirst)
03382       {
03383         keepNodeAsText->GetData(leftText);
03384         joinNodeAsText->GetData(rightText);
03385       }
03386       else
03387       {
03388         keepNodeAsText->GetData(rightText);
03389         joinNodeAsText->GetData(leftText);
03390       }
03391       leftText += rightText;
03392       keepNodeAsText->SetData(leftText);          
03393     }
03394     else
03395     {  // otherwise it's an interior node, so shuffle around the children
03396       nsCOMPtr<nsIDOMNodeList> childNodes;
03397       result = aNodeToJoin->GetChildNodes(getter_AddRefs(childNodes));
03398       if ((NS_SUCCEEDED(result)) && (childNodes))
03399       {
03400         PRInt32 i;  // must be signed int!
03401         PRUint32 childCount=0;
03402         nsCOMPtr<nsIDOMNode> firstNode; //only used if aNodeToKeepIsFirst is false
03403         childNodes->GetLength(&childCount);
03404         if (!aNodeToKeepIsFirst)
03405         { // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
03406           result = aNodeToKeep->GetFirstChild(getter_AddRefs(firstNode));  
03407           // GetFirstChild returns nsnull firstNode if aNodeToKeep has no children, that's ok.
03408         }
03409         nsCOMPtr<nsIDOMNode> resultNode;
03410         // have to go through the list backwards to keep deletes from interfering with iteration
03411         nsCOMPtr<nsIDOMNode> previousChild;
03412         for (i=childCount-1; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
03413         {
03414           nsCOMPtr<nsIDOMNode> childNode;
03415           result = childNodes->Item(i, getter_AddRefs(childNode));
03416           if ((NS_SUCCEEDED(result)) && (childNode))
03417           {
03418             if (aNodeToKeepIsFirst)
03419             { // append children of aNodeToJoin
03420               //was result = aNodeToKeep->AppendChild(childNode, getter_AddRefs(resultNode));
03421               result = aNodeToKeep->InsertBefore(childNode, previousChild, getter_AddRefs(resultNode));
03422               previousChild = do_QueryInterface(childNode);
03423             }
03424             else
03425             { // prepend children of aNodeToJoin
03426               result = aNodeToKeep->InsertBefore(childNode, firstNode, getter_AddRefs(resultNode));
03427               firstNode = do_QueryInterface(childNode);
03428             }
03429           }
03430         }
03431       }
03432       else if (!childNodes) {
03433         result = NS_ERROR_NULL_POINTER;
03434       }
03435     }
03436     if (NS_SUCCEEDED(result))
03437     { // delete the extra node
03438       nsCOMPtr<nsIDOMNode> resultNode;
03439       result = aParent->RemoveChild(aNodeToJoin, getter_AddRefs(resultNode));
03440       
03441       if (GetShouldTxnSetSelection())
03442       {
03443         // editor wants us to set selection at join point
03444         selection->Collapse(aNodeToKeep, firstNodeLength);
03445       }
03446       else if (selStartNode)
03447       {
03448         // and adjust the selection if needed
03449         // HACK: this is overly simplified - multi-range selections need more work than this
03450         PRBool bNeedToAdjust = PR_FALSE;
03451         
03452         // check to see if we joined nodes where selection starts
03453         if (selStartNode.get() == aNodeToJoin)
03454         {
03455           bNeedToAdjust = PR_TRUE;
03456           selStartNode = aNodeToKeep;
03457           if (aNodeToKeepIsFirst)
03458           {
03459             selStartOffset += firstNodeLength;
03460           }
03461         }
03462         else if ((selStartNode.get() == aNodeToKeep) && !aNodeToKeepIsFirst)
03463         {
03464           bNeedToAdjust = PR_TRUE;
03465           selStartOffset += firstNodeLength;
03466         }
03467                 
03468         // check to see if we joined nodes where selection ends
03469         if (selEndNode.get() == aNodeToJoin)
03470         {
03471           bNeedToAdjust = PR_TRUE;
03472           selEndNode = aNodeToKeep;
03473           if (aNodeToKeepIsFirst)
03474           {
03475             selEndOffset += firstNodeLength;
03476           }
03477         }
03478         else if ((selEndNode.get() == aNodeToKeep) && !aNodeToKeepIsFirst)
03479         {
03480           bNeedToAdjust = PR_TRUE;
03481           selEndOffset += firstNodeLength;
03482         }
03483         
03484         // adjust selection if needed
03485         if (bNeedToAdjust)
03486         {
03487           selection->Collapse(selStartNode,selStartOffset);
03488           selection->Extend(selEndNode,selEndOffset);          
03489         }
03490       }
03491     }
03492   }
03493   else
03494     result = NS_ERROR_INVALID_ARG;
03495 
03496   return result;
03497 }
03498 
03499 
03500 nsresult 
03501 nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
03502 {
03503   NS_ASSERTION((aChild && aParent), "bad args");
03504 
03505   nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
03506   nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
03507   if (!cChild || !content)
03508     return NS_ERROR_NULL_POINTER;
03509 
03510   aOffset = content->IndexOf(cChild);
03511 
03512   return NS_OK;
03513 }
03514 
03515 nsresult 
03516 nsEditor::GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, PRInt32 *outOffset)
03517 {
03518   NS_ASSERTION((inChild && outParent && outOffset), "bad args");
03519   nsresult result = NS_ERROR_NULL_POINTER;
03520   if (inChild && outParent && outOffset)
03521   {
03522     result = inChild->GetParentNode(getter_AddRefs(*outParent));
03523     if ((NS_SUCCEEDED(result)) && (*outParent))
03524     {
03525       result = GetChildOffset(inChild, *outParent, *outOffset);
03526     }
03527   }
03528   return result;
03529 }
03530 
03531 // returns the number of things inside aNode.  
03532 // If aNode is text, returns number of characters. If not, returns number of children nodes.
03533 nsresult
03534 nsEditor::GetLengthOfDOMNode(nsIDOMNode *aNode, PRUint32 &aCount) 
03535 {
03536   aCount = 0;
03537   if (!aNode) { return NS_ERROR_NULL_POINTER; }
03538   nsresult result=NS_OK;
03539   nsCOMPtr<nsIDOMCharacterData>nodeAsChar = do_QueryInterface(aNode);
03540   if (nodeAsChar) {
03541     nodeAsChar->GetLength(&aCount);
03542   }
03543   else
03544   {
03545     PRBool hasChildNodes;
03546     aNode->HasChildNodes(&hasChildNodes);
03547     if (hasChildNodes)
03548     {
03549       nsCOMPtr<nsIDOMNodeList>nodeList;
03550       result = aNode->GetChildNodes(getter_AddRefs(nodeList));
03551       if (NS_SUCCEEDED(result) && nodeList) {
03552         nodeList->GetLength(&aCount);
03553       }
03554     }
03555   }
03556   return result;
03557 }
03558 
03559 
03560 nsresult 
03561 nsEditor::GetPriorNode(nsIDOMNode  *aParentNode, 
03562                        PRInt32      aOffset, 
03563                        PRBool       aEditableNode, 
03564                        nsCOMPtr<nsIDOMNode> *aResultNode,
03565                        PRBool       bNoBlockCrossing)
03566 {
03567   // just another version of GetPriorNode that takes a {parent, offset}
03568   // instead of a node
03569   if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
03570   *aResultNode = nsnull;
03571   
03572   // if we are at beginning of node, or it is a textnode, then just look before it
03573   if (!aOffset || IsTextNode(aParentNode))
03574   {
03575     if (bNoBlockCrossing && IsBlockNode(aParentNode))
03576     {
03577       // if we aren't allowed to cross blocks, don't look before this block
03578       return NS_OK;
03579     }
03580     return GetPriorNode(aParentNode, aEditableNode, aResultNode, bNoBlockCrossing);
03581   }
03582 
03583   // else look before the child at 'aOffset'
03584   nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
03585   if (child)
03586     return GetPriorNode(child, aEditableNode, aResultNode, bNoBlockCrossing);
03587 
03588   // unless there isn't one, in which case we are at the end of the node
03589   // and want the deep-right child.
03590   *aResultNode = GetRightmostChild(aParentNode, bNoBlockCrossing);
03591   if (!*aResultNode || !aEditableNode || IsEditable(*aResultNode))
03592     return NS_OK;
03593 
03594   // restart the search from the non-editable node we just found
03595   nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
03596   return GetPriorNode(notEditableNode, aEditableNode, aResultNode, bNoBlockCrossing);
03597 }
03598 
03599 
03600 nsresult 
03601 nsEditor::GetNextNode(nsIDOMNode   *aParentNode, 
03602                        PRInt32      aOffset, 
03603                        PRBool       aEditableNode, 
03604                        nsCOMPtr<nsIDOMNode> *aResultNode,
03605                        PRBool       bNoBlockCrossing)
03606 {
03607   // just another version of GetNextNode that takes a {parent, offset}
03608   // instead of a node
03609   if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
03610   
03611   *aResultNode = nsnull;
03612 
03613   // if aParentNode is a text node, use it's location instead
03614   if (IsTextNode(aParentNode))
03615   {
03616     nsCOMPtr<nsIDOMNode> parent;
03617     nsEditor::GetNodeLocation(aParentNode, address_of(parent), &aOffset);
03618     aParentNode = parent;
03619     aOffset++;  // _after_ the text node
03620   }
03621   // look at the child at 'aOffset'
03622   nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
03623   if (child)
03624   {
03625     if (bNoBlockCrossing && IsBlockNode(child))
03626     {
03627       *aResultNode = child;  // return this block
03628       return NS_OK;
03629     }
03630     *aResultNode = GetLeftmostChild(child, bNoBlockCrossing);
03631     if (!*aResultNode) 
03632     {
03633       *aResultNode = child;
03634       return NS_OK;
03635     }
03636     if (!IsDescendantOfBody(*aResultNode))
03637     {
03638       *aResultNode = nsnull;
03639       return NS_OK;
03640     }
03641 
03642     if (!aEditableNode || IsEditable(*aResultNode))
03643       return NS_OK;
03644 
03645     // restart the search from the non-editable node we just found
03646     nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
03647     return GetNextNode(notEditableNode, aEditableNode, aResultNode, bNoBlockCrossing);
03648   }
03649     
03650   // unless there isn't one, in which case we are at the end of the node
03651   // and want the next one.
03652   if (bNoBlockCrossing && IsBlockNode(aParentNode))
03653   {
03654     // don't cross out of parent block
03655     return NS_OK;
03656   }
03657   return GetNextNode(aParentNode, aEditableNode, aResultNode, bNoBlockCrossing);
03658 }
03659 
03660 
03661 nsresult 
03662 nsEditor::GetPriorNode(nsIDOMNode  *aCurrentNode, 
03663                        PRBool       aEditableNode, 
03664                        nsCOMPtr<nsIDOMNode> *aResultNode,
03665                        PRBool       bNoBlockCrossing)
03666 {
03667   nsresult result;
03668   if (!aCurrentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
03669   
03670   *aResultNode = nsnull;  // init out-param
03671 
03672   if (IsRootNode(aCurrentNode))
03673   {
03674     // Don't allow traversal above the root node! This helps
03675     // prevent us from accidentally editing browser content
03676     // when the editor is in a text widget.
03677 
03678     return NS_OK;
03679   }
03680 
03681   nsCOMPtr<nsIDOMNode> candidate;
03682   result = GetPriorNodeImpl(aCurrentNode, aEditableNode, address_of(candidate), bNoBlockCrossing);
03683   if (NS_FAILED(result)) return result;
03684   
03685   if (!candidate)
03686   {
03687     // we could not find a prior node.  return null.
03688     return NS_OK;
03689   }
03690   else if (!aEditableNode) *aResultNode = candidate;
03691   else if (IsEditable(candidate)) *aResultNode = candidate;
03692   else 
03693   { // restart the search from the non-editable node we just found
03694     nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(candidate);
03695     return GetPriorNode(notEditableNode, aEditableNode, aResultNode, bNoBlockCrossing);
03696   }
03697   return result;
03698 }
03699 
03700 nsresult 
03701 nsEditor::GetPriorNodeImpl(nsIDOMNode  *aCurrentNode, 
03702                            PRBool       aEditableNode, 
03703                            nsCOMPtr<nsIDOMNode> *aResultNode,
03704                            PRBool       bNoBlockCrossing)
03705 {
03706   // called only by GetPriorNode so we don't need to check params.
03707 
03708   // if aCurrentNode has a left sibling, return that sibling's rightmost child (or itself if it has no children)
03709   nsCOMPtr<nsIDOMNode> prevSibling;
03710   nsresult result = aCurrentNode->GetPreviousSibling(getter_AddRefs(prevSibling));
03711   if ((NS_SUCCEEDED(result)) && prevSibling)
03712   {
03713     if (bNoBlockCrossing && IsBlockNode(prevSibling))
03714     {
03715       // don't look inside prevsib, since it is a block
03716       *aResultNode = prevSibling;
03717       return NS_OK;
03718     }
03719     *aResultNode = GetRightmostChild(prevSibling, bNoBlockCrossing);
03720     if (!*aResultNode) 
03721     { 
03722       *aResultNode = prevSibling;
03723       return NS_OK;
03724     }
03725     if (!IsDescendantOfBody(*aResultNode))
03726     {
03727       *aResultNode = nsnull;
03728       return NS_OK;
03729     }
03730   }
03731   else
03732   {
03733     // otherwise, walk up the parent tree until there is a child that comes before 
03734     // the ancestor of aCurrentNode.  Then return that node's rightmost child
03735     nsCOMPtr<nsIDOMNode> parent = do_QueryInterface(aCurrentNode);
03736     nsCOMPtr<nsIDOMNode> node, notEditableNode;
03737     do {
03738       node = parent;
03739       result = node->GetParentNode(getter_AddRefs(parent));
03740       if ((NS_SUCCEEDED(result)) && parent)
03741       {
03742         if (!IsDescendantOfBody(parent))
03743         {
03744           *aResultNode = nsnull;
03745           return NS_OK;
03746         }
03747         if ((bNoBlockCrossing && IsBlockNode(parent)) || IsRootNode(parent))
03748         {
03749           // we are at front of block or root, do not step out
03750           *aResultNode = nsnull;
03751           return NS_OK;
03752         }
03753         result = parent->GetPreviousSibling(getter_AddRefs(node));
03754         if ((NS_SUCCEEDED(result)) && node)
03755         {
03756           if (bNoBlockCrossing && IsBlockNode(node))
03757           {
03758             // prev sibling is a block, do not step into it
03759             *aResultNode = node;
03760             return NS_OK;
03761           }
03762           *aResultNode = GetRightmostChild(node, bNoBlockCrossing);
03763           if (!*aResultNode)  *aResultNode = node;
03764           return NS_OK;
03765         }
03766       }
03767     } while ((NS_SUCCEEDED(result)) && parent && !*aResultNode);
03768   }
03769   return result;
03770 }
03771 
03772 nsresult 
03773 nsEditor::GetNextNode(nsIDOMNode  *aCurrentNode, 
03774                       PRBool       aEditableNode, 
03775                       nsCOMPtr<nsIDOMNode> *aResultNode,
03776                       PRBool       bNoBlockCrossing)
03777 {
03778   if (!aCurrentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
03779   
03780   *aResultNode = nsnull;  // init out-param
03781 
03782   if (IsRootNode(aCurrentNode))
03783   {
03784     // Don't allow traversal above the root node! This helps
03785     // prevent us from accidentally editing browser content
03786     // when the editor is in a text widget.
03787 
03788     return NS_OK;
03789   }
03790 
03791   nsCOMPtr<nsIDOMNode> candidate;
03792   nsresult result = GetNextNodeImpl(aCurrentNode, aEditableNode,
03793                                     address_of(candidate), bNoBlockCrossing);
03794   if (NS_FAILED(result)) return result;
03795   
03796   if (!candidate)
03797   {
03798     // we could not find a next node.  return null.
03799     *aResultNode = nsnull;
03800     return NS_OK;
03801   }
03802   else if (!aEditableNode) *aResultNode = candidate;
03803   else if (IsEditable(candidate)) *aResultNode = candidate;
03804   else 
03805   { // restart the search from the non-editable node we just found
03806     nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(candidate);
03807     return GetNextNode(notEditableNode, aEditableNode, aResultNode, bNoBlockCrossing);
03808   }
03809   return result;
03810 }
03811 
03812 
03813 nsresult 
03814 nsEditor::GetNextNodeImpl(nsIDOMNode  *aCurrentNode, 
03815                           PRBool       aEditableNode, 
03816                           nsCOMPtr<nsIDOMNode> *aResultNode,
03817                           PRBool       bNoBlockCrossing)
03818 {
03819   // called only by GetNextNode so we don't need to check params.
03820 
03821   // if aCurrentNode has a right sibling, return that sibling's leftmost child (or itself if it has no children)
03822   nsCOMPtr<nsIDOMNode> nextSibling;
03823   nsresult result = aCurrentNode->GetNextSibling(getter_AddRefs(nextSibling));
03824   if ((NS_SUCCEEDED(result)) && nextSibling)
03825   {
03826     if (bNoBlockCrossing && IsBlockNode(nextSibling))
03827     {
03828       // next sibling is a block, do not step into it
03829       *aResultNode = nextSibling;
03830       return NS_OK;
03831     }
03832     *aResultNode = GetLeftmostChild(nextSibling, bNoBlockCrossing);
03833     if (!*aResultNode)
03834     { 
03835       *aResultNode = nextSibling;
03836       return NS_OK; 
03837     }
03838     if (!IsDescendantOfBody(*aResultNode))
03839     {
03840       *aResultNode = nsnull;
03841       return NS_OK;
03842     }
03843   }
03844   else
03845   {
03846     // otherwise, walk up the parent tree until there is a child that comes after 
03847     // the ancestor of aCurrentNode.  Then return that node's leftmost child
03848     nsCOMPtr<nsIDOMNode> parent(do_QueryInterface(aCurrentNode));
03849     nsCOMPtr<nsIDOMNode> node, notEditableNode;
03850     do {
03851       node = parent;
03852       result = node->GetParentNode(getter_AddRefs(parent));
03853       if ((NS_SUCCEEDED(result)) && parent)
03854       {
03855         if (!IsDescendantOfBody(parent))
03856         {
03857           *aResultNode = nsnull;
03858           return NS_OK;
03859         }
03860         if ((bNoBlockCrossing && IsBlockNode(parent)) || IsRootNode(parent))
03861         {
03862           // we are at end of block or root, do not step out
03863           *aResultNode = nsnull;
03864           return NS_OK;
03865         }
03866         result = parent->GetNextSibling(getter_AddRefs(node));
03867         if ((NS_SUCCEEDED(result)) && node)
03868         {
03869           if (bNoBlockCrossing && IsBlockNode(node))
03870           {
03871             // next sibling is a block, do not step into it
03872             *aResultNode = node;
03873             return NS_OK;
03874           }
03875           *aResultNode = GetLeftmostChild(node, bNoBlockCrossing);
03876           if (!*aResultNode) *aResultNode = node;
03877           return NS_OK; 
03878         }
03879       }
03880     } while ((NS_SUCCEEDED(result)) && parent);
03881   }
03882   return result;
03883 }
03884 
03885 
03886 nsCOMPtr<nsIDOMNode>
03887 nsEditor::GetRightmostChild(nsIDOMNode *aCurrentNode, 
03888                             PRBool bNoBlockCrossing)
03889 {
03890   if (!aCurrentNode) return nsnull;
03891   nsCOMPtr<nsIDOMNode> resultNode, temp=aCurrentNode;
03892   PRBool hasChildren;
03893   aCurrentNode->HasChildNodes(&hasChildren);
03894   while (hasChildren)
03895   {
03896     temp->GetLastChild(getter_AddRefs(resultNode));
03897     if (resultNode)
03898     {
03899       if (bNoBlockCrossing && IsBlockNode(resultNode))
03900          return resultNode;
03901       resultNode->HasChildNodes(&hasChildren);
03902       temp = resultNode;
03903     }
03904     else 
03905       hasChildren = PR_FALSE;
03906   }
03907 
03908   return resultNode;
03909 }
03910 
03911 nsCOMPtr<nsIDOMNode>
03912 nsEditor::GetLeftmostChild(nsIDOMNode *aCurrentNode,
03913                            PRBool bNoBlockCrossing)
03914 {
03915   if (!aCurrentNode) return nsnull;
03916   nsCOMPtr<nsIDOMNode> resultNode, temp=aCurrentNode;
03917   PRBool hasChildren;
03918   aCurrentNode->HasChildNodes(&hasChildren);
03919   while (hasChildren)
03920   {
03921     temp->GetFirstChild(getter_AddRefs(resultNode));
03922     if (resultNode)
03923     {
03924       if (bNoBlockCrossing && IsBlockNode(resultNode))
03925          return resultNode;
03926       resultNode->HasChildNodes(&hasChildren);
03927       temp = resultNode;
03928     }
03929     else 
03930       hasChildren = PR_FALSE;
03931   }
03932 
03933   return resultNode;
03934 }
03935 
03936 PRBool 
03937 nsEditor::IsBlockNode(nsIDOMNode *aNode)
03938 {
03939   // stub to be overridden in nsHTMLEditor.
03940   // screwing around with the class heirarchy here in order
03941   // to not duplicate the code in GetNextNode/GetPrevNode
03942   // across both nsEditor/nsHTMLEditor.  
03943   return PR_FALSE;
03944 }
03945 
03946 PRBool 
03947 nsEditor::CanContainTag(nsIDOMNode* aParent, const nsAString &aChildTag)
03948 {
03949   nsCOMPtr<nsIDOMElement> parentElement = do_QueryInterface(aParent);
03950   if (!parentElement) return PR_FALSE;
03951   
03952   nsAutoString parentStringTag;
03953   parentElement->GetTagName(parentStringTag);
03954   return TagCanContainTag(parentStringTag, aChildTag);
03955 }
03956 
03957 PRBool 
03958 nsEditor::TagCanContain(const nsAString &aParentTag, nsIDOMNode* aChild)
03959 {
03960   nsAutoString childStringTag;
03961   
03962   if (IsTextNode(aChild)) 
03963   {
03964     childStringTag.AssignLiteral("#text");
03965   }
03966   else
03967   {
03968     nsCOMPtr<nsIDOMElement> childElement = do_QueryInterface(aChild);
03969     if (!childElement) return PR_FALSE;
03970     childElement->GetTagName(childStringTag);
03971   }
03972   return TagCanContainTag(aParentTag, childStringTag);
03973 }
03974 
03975 PRBool 
03976 nsEditor::TagCanContainTag(const nsAString &aParentTag, const nsAString &aChildTag)
03977 {
03978   // if we don't have a dtd then assume we can insert whatever want
03979   if (!mDTD) return PR_TRUE;
03980 
03981   PRInt32 childTagEnum;
03982   // XXX Should this handle #cdata-section too?
03983   if (aChildTag.EqualsLiteral("#text")) {
03984     childTagEnum = eHTMLTag_text;
03985   }
03986   else {
03987     childTagEnum = sParserService->HTMLStringTagToId(aChildTag);
03988   }
03989 
03990   PRInt32 parentTagEnum = sParserService->HTMLStringTagToId(aParentTag);
03991   NS_ASSERTION(parentTagEnum < NS_HTML_TAG_MAX,
03992                "Fix the caller, this type of node can never contain children.");
03993 
03994   return mDTD->CanContain(parentTagEnum, childTagEnum);
03995 }
03996 
03997 PRBool 
03998 nsEditor::IsRootNode(nsIDOMNode *inNode) 
03999 {
04000   if (!inNode)
04001     return PR_FALSE;
04002 
04003   nsIDOMElement *rootElement = GetRoot();
04004 
04005   nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootElement);
04006 
04007   return inNode == rootNode;
04008 }
04009 
04010 PRBool 
04011 nsEditor::IsDescendantOfBody(nsIDOMNode *inNode) 
04012 {
04013   if (!inNode) return PR_FALSE;
04014   nsIDOMElement *rootElement = GetRoot();
04015   if (!rootElement) return PR_FALSE;
04016   nsCOMPtr<nsIDOMNode> root = do_QueryInterface(rootElement);
04017 
04018   if (inNode == root.get()) return PR_TRUE;
04019   
04020   nsCOMPtr<nsIDOMNode> parent, node = do_QueryInterface(inNode);
04021   
04022   do
04023   {
04024     node->GetParentNode(getter_AddRefs(parent));
04025     if (parent == root) return PR_TRUE;
04026     node = parent;
04027   } while (parent);
04028   
04029   return PR_FALSE;
04030 }
04031 
04032 PRBool 
04033 nsEditor::IsContainer(nsIDOMNode *aNode)
04034 {
04035   if (!aNode) return PR_FALSE;
04036   nsAutoString stringTag;
04037   nsresult res = aNode->GetNodeName(stringTag);
04038   if (NS_FAILED(res)) return PR_FALSE;
04039 
04040   PRInt32 tagEnum;
04041   // XXX Should this handle #cdata-section too?
04042   if (stringTag.EqualsLiteral("#text")) {
04043     tagEnum = eHTMLTag_text;
04044   }
04045   else {
04046     tagEnum = sParserService->HTMLStringTagToId(stringTag);
04047   }
04048 
04049   return mDTD->IsContainer(tagEnum);
04050 }
04051 
04052 PRBool
04053 nsEditor::IsTextInDirtyFrameVisible(nsIDOMNode *aNode)
04054 {
04055   // virtual method
04056   //
04057   // If this is a simple non-html editor,
04058   // the best we can do is to assume it's visible.
04059 
04060   return PR_TRUE;
04061 }
04062 
04063 PRBool 
04064 nsEditor::IsEditable(nsIDOMNode *aNode)
04065 {
04066   if (!aNode) return PR_FALSE;
04067   nsCOMPtr<nsIPresShell> shell;
04068   GetPresShell(getter_AddRefs(shell));
04069   if (!shell)  return PR_FALSE;
04070 
04071   if (IsMozEditorBogusNode(aNode)) return PR_FALSE;
04072   
04073   // see if it has a frame.  If so, we'll edit it.
04074   // special case for textnodes: frame must have width.
04075   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
04076   if (content)
04077   {
04078     nsIFrame *resultFrame;
04079     nsresult result = shell->GetPrimaryFrameFor(content, &resultFrame);
04080     if (NS_FAILED(result) || !resultFrame)   // if it has no frame, it is not editable
04081       return PR_FALSE;
04082     nsCOMPtr<nsITextContent> text(do_QueryInterface(content));
04083     if (!text)
04084       return PR_TRUE;  // not a text node; has a frame
04085     if (resultFrame->GetStateBits() & NS_FRAME_IS_DIRTY) // we can only trust width data for undirty frames
04086     {
04087       // In the past a comment said:
04088       //   "assume all text nodes with dirty frames are editable"
04089       // Nowadays we use a virtual function, that assumes TRUE
04090       // in the simple editor world,
04091       // and uses enhanced logic to find out in the HTML world.
04092       return IsTextInDirtyFrameVisible(aNode);
04093     }
04094     if (resultFrame->GetSize().width > 0) 
04095       return PR_TRUE;  // text node has width
04096   }
04097   return PR_FALSE;  // didn't pass any editability test
04098 }
04099 
04100 PRBool
04101 nsEditor::IsMozEditorBogusNode(nsIDOMNode *aNode)
04102 {
04103   if (!aNode)
04104     return PR_FALSE;
04105 
04106   nsCOMPtr<nsIDOMElement>element = do_QueryInterface(aNode);
04107   if (element)
04108   {
04109     nsAutoString val;
04110     (void)element->GetAttribute(kMOZEditorBogusNodeAttr, val);
04111     if (val.Equals(kMOZEditorBogusNodeValue)) {
04112       return PR_TRUE;
04113     }
04114   }
04115     
04116   return PR_FALSE;
04117 }
04118 
04119 PRBool
04120 nsEditor::IsEmptyTextContent(nsIContent* aContent)
04121 {
04122   PRBool result = PR_FALSE;
04123   nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent));
04124   if (tc) {
04125     result = tc->IsOnlyWhitespace();
04126   }
04127   return result;
04128 }
04129 
04130 nsresult
04131 nsEditor::CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount) 
04132 {
04133   outCount = 0;
04134   if (!aNode) { return NS_ERROR_NULL_POINTER; }
04135   nsresult res=NS_OK;
04136   PRBool hasChildNodes;
04137   aNode->HasChildNodes(&hasChildNodes);
04138   if (hasChildNodes)
04139   {
04140     nsCOMPtr<nsIDOMNodeList>nodeList;
04141     res = aNode->GetChildNodes(getter_AddRefs(nodeList));
04142     if (NS_SUCCEEDED(res) && nodeList) 
04143     {
04144       PRUint32 i;
04145       PRUint32 len;
04146       nodeList->GetLength(&len);
04147       for (i=0 ; i<len; i++)
04148       {
04149         nsCOMPtr<nsIDOMNode> child;
04150         res = nodeList->Item((PRInt32)i, getter_AddRefs(child));
04151         if ((NS_SUCCEEDED(res)) && (child))
04152         {
04153           if (IsEditable(child))
04154           {
04155             outCount++;
04156           }
04157         }
04158       }
04159     }
04160     else if (!nodeList)
04161       res = NS_ERROR_NULL_POINTER;
04162   }
04163   return res;
04164 }
04165 
04166 //END nsEditor static utility methods
04167 
04168 
04169 NS_IMETHODIMP nsEditor::IncrementModificationCount(PRInt32 inNumMods)
04170 {
04171   PRUint32 oldModCount = mModCount;
04172 
04173   mModCount += inNumMods;
04174 
04175   if ((oldModCount == 0 && mModCount != 0)
04176    || (oldModCount != 0 && mModCount == 0))
04177     NotifyDocumentListeners(eDocumentStateChanged);
04178   return NS_OK;
04179 }
04180 
04181 
04182 NS_IMETHODIMP nsEditor::GetModificationCount(PRInt32 *outModCount)
04183 {
04184   NS_ENSURE_ARG_POINTER(outModCount);
04185   *outModCount = mModCount;
04186   return NS_OK;
04187 }
04188 
04189 
04190 NS_IMETHODIMP nsEditor::ResetModificationCount()
04191 {
04192   PRBool doNotify = (mModCount != 0);
04193 
04194   mModCount = 0;
04195 
04196   if (doNotify)
04197     NotifyDocumentListeners(eDocumentStateChanged);
04198   return NS_OK;
04199 }
04200 
04201 //END nsEditor Private methods
04202 
04203 
04204 
04206 // GetTag: digs out the atom for the tag of this node
04207 //                    
04208 nsIAtom *
04209 nsEditor::GetTag(nsIDOMNode *aNode)
04210 {
04211   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
04212 
04213   if (!content) 
04214   {
04215     NS_ASSERTION(aNode, "null node passed to nsEditor::Tag()");
04216 
04217     return nsnull;
04218   }
04219   
04220   return content->Tag();
04221 }
04222 
04223 
04225 // GetTagString: digs out string for the tag of this node
04226 //                    
04227 nsresult 
04228 nsEditor::GetTagString(nsIDOMNode *aNode, nsAString& outString)
04229 {
04230   if (!aNode) 
04231   {
04232     NS_NOTREACHED("null node passed to nsEditor::GetTag()");
04233     return NS_ERROR_NULL_POINTER;
04234   }
04235   
04236   nsIAtom *atom = GetTag(aNode);
04237   if (!atom)
04238   {
04239     return NS_ERROR_FAILURE;
04240   }
04241 
04242   atom->ToString(outString);
04243   return NS_OK;
04244 }
04245 
04246 
04248 // NodesSameType: do these nodes have the same tag?
04249 //                    
04250 PRBool 
04251 nsEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
04252 {
04253   if (!aNode1 || !aNode2) 
04254   {
04255     NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
04256     return PR_FALSE;
04257   }
04258   
04259   return GetTag(aNode1) == GetTag(aNode2);
04260 }
04261 
04262 
04263 // IsTextOrElementNode: true if node of dom type element or text
04264 //               
04265 PRBool
04266 nsEditor::IsTextOrElementNode(nsIDOMNode *aNode)
04267 {
04268   if (!aNode)
04269   {
04270     NS_NOTREACHED("null node passed to IsTextOrElementNode()");
04271     return PR_FALSE;
04272   }
04273   
04274   PRUint16 nodeType;
04275   aNode->GetNodeType(&nodeType);
04276   return ((nodeType == nsIDOMNode::ELEMENT_NODE) || (nodeType == nsIDOMNode::TEXT_NODE));
04277 }
04278 
04279 
04280 
04282 // IsTextNode: true if node of dom type text
04283 //               
04284 PRBool
04285 nsEditor::IsTextNode(nsIDOMNode *aNode)
04286 {
04287   if (!aNode)
04288   {
04289     NS_NOTREACHED("null node passed to IsTextNode()");
04290     return PR_FALSE;
04291   }
04292   
04293   PRUint16 nodeType;
04294   aNode->GetNodeType(&nodeType);
04295   return (nodeType == nsIDOMNode::TEXT_NODE);
04296 }
04297 
04298 
04300 // GetIndexOf: returns the position index of the node in the parent
04301 //
04302 PRInt32 
04303 nsEditor::GetIndexOf(nsIDOMNode *parent, nsIDOMNode *child)
04304 {
04305   nsCOMPtr<nsIContent> content = do_QueryInterface(parent);
04306   nsCOMPtr<nsIContent> cChild = do_QueryInterface(child);
04307   NS_PRECONDITION(content, "null content in nsEditor::GetIndexOf");
04308   NS_PRECONDITION(cChild, "null content in nsEditor::GetIndexOf");
04309 
04310   return content->IndexOf(cChild);
04311 }
04312   
04313 
04315 // GetChildAt: returns the node at this position index in the parent
04316 //
04317 nsCOMPtr<nsIDOMNode> 
04318 nsEditor::GetChildAt(nsIDOMNode *aParent, PRInt32 aOffset)
04319 {
04320   nsCOMPtr<nsIDOMNode> resultNode;
04321   
04322   nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
04323 
04324   if (!parent) 
04325     return resultNode;
04326 
04327   resultNode = do_QueryInterface(parent->GetChildAt(aOffset));
04328 
04329   return resultNode;
04330 }
04331   
04332 
04333 
04335 // GetStartNodeAndOffset: returns whatever the start parent & offset is of 
04336 //                        the first range in the selection.
04337 nsresult 
04338 nsEditor::GetStartNodeAndOffset(nsISelection *aSelection,
04339                                        nsCOMPtr<nsIDOMNode> *outStartNode,
04340                                        PRInt32 *outStartOffset)
04341 {
04342   if (!outStartNode || !outStartOffset || !aSelection) 
04343     return NS_ERROR_NULL_POINTER;
04344 
04345   // brade:  set outStartNode to null or ?
04346 
04347   nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
04348   nsCOMPtr<nsIEnumerator> enumerator;
04349   nsresult result = selPrivate->GetEnumerator(getter_AddRefs(enumerator));
04350   if (NS_FAILED(result) || !enumerator)
04351     return NS_ERROR_FAILURE;
04352     
04353   enumerator->First(); 
04354   nsCOMPtr<nsISupports> currentItem;
04355   if (NS_FAILED(enumerator->CurrentItem(getter_AddRefs(currentItem))))
04356     return NS_ERROR_FAILURE;
04357 
04358   nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
04359   if (!range)
04360     return NS_ERROR_FAILURE;
04361     
04362   if (NS_FAILED(range->GetStartContainer(getter_AddRefs(*outStartNode))))
04363     return NS_ERROR_FAILURE;
04364     
04365   if (NS_FAILED(range->GetStartOffset(outStartOffset)))
04366     return NS_ERROR_FAILURE;
04367     
04368   return NS_OK;
04369 }
04370 
04371 
04373 // GetEndNodeAndOffset: returns whatever the end parent & offset is of 
04374 //                        the first range in the selection.
04375 nsresult 
04376 nsEditor::GetEndNodeAndOffset(nsISelection *aSelection,
04377                                        nsCOMPtr<nsIDOMNode> *outEndNode,
04378                                        PRInt32 *outEndOffset)
04379 {
04380   if (!outEndNode || !outEndOffset) 
04381     return NS_ERROR_NULL_POINTER;
04382     
04383   nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
04384   nsCOMPtr<nsIEnumerator> enumerator;
04385   nsresult result = selPrivate->GetEnumerator(getter_AddRefs(enumerator));
04386   if (NS_FAILED(result) || !enumerator)
04387     return NS_ERROR_FAILURE;
04388     
04389   enumerator->First(); 
04390   nsCOMPtr<nsISupports> currentItem;
04391   if (NS_FAILED(enumerator->CurrentItem(getter_AddRefs(currentItem))))
04392     return NS_ERROR_FAILURE;
04393 
04394   nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
04395   if (!range)
04396     return NS_ERROR_FAILURE;
04397     
04398   if (NS_FAILED(range->GetEndContainer(getter_AddRefs(*outEndNode))))
04399     return NS_ERROR_FAILURE;
04400     
04401   if (NS_FAILED(range->GetEndOffset(outEndOffset)))
04402     return NS_ERROR_FAILURE;
04403     
04404   return NS_OK;
04405 }
04406 
04407 
04409 // IsPreformatted: checks the style info for the node for the preformatted
04410 //                 text style.
04411 nsresult 
04412 nsEditor::IsPreformatted(nsIDOMNode *aNode, PRBool *aResult)
04413 {
04414   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
04415   
04416   if (!aResult || !content) return NS_ERROR_NULL_POINTER;
04417   
04418   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
04419   if (!ps) return NS_ERROR_NOT_INITIALIZED;
04420   
04421   nsIFrame *frame;
04422   nsresult result = ps->GetPrimaryFrameFor(content, &frame);
04423   if (NS_FAILED(result)) return result;
04424 
04425   NS_ASSERTION(frame, "no frame, see bug #188946");
04426   if (!frame)
04427   {
04428     // Consider nodes without a style context to be NOT preformatted:
04429     // For instance, this is true of JS tags inside the body (which show
04430     // up as #text nodes but have no style context).
04431     *aResult = PR_FALSE;
04432     return NS_OK;
04433   }
04434 
04435   const nsStyleText* styleText = frame->GetStyleText();
04436 
04437   *aResult = NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace ||
04438              NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace;
04439   return NS_OK;
04440 }
04441 
04442 
04444 // SplitNodeDeep: this splits a node "deeply", splitting children as 
04445 //                appropriate.  The place to split is represented by
04446 //                a dom point at {splitPointParent, splitPointOffset}.
04447 //                That dom point must be inside aNode, which is the node to 
04448 //                split.  outOffset is set to the offset in the parent of aNode where
04449 //                the split terminates - where you would want to insert 
04450 //                a new element, for instance, if thats why you were splitting 
04451 //                the node.
04452 //
04453 nsresult
04454 nsEditor::SplitNodeDeep(nsIDOMNode *aNode, 
04455                         nsIDOMNode *aSplitPointParent, 
04456                         PRInt32 aSplitPointOffset,
04457                         PRInt32 *outOffset,
04458                         PRBool  aNoEmptyContainers,
04459                         nsCOMPtr<nsIDOMNode> *outLeftNode,
04460                         nsCOMPtr<nsIDOMNode> *outRightNode)
04461 {
04462   if (!aNode || !aSplitPointParent || !outOffset) return NS_ERROR_NULL_POINTER;
04463   nsCOMPtr<nsIDOMNode> tempNode, parentNode;  
04464   PRInt32 offset = aSplitPointOffset;
04465   nsresult res;
04466   
04467   if (outLeftNode)  *outLeftNode  = nsnull;
04468   if (outRightNode) *outRightNode = nsnull;
04469   
04470   nsCOMPtr<nsIDOMNode> nodeToSplit = do_QueryInterface(aSplitPointParent);
04471   while (nodeToSplit)
04472   {
04473     // need to insert rules code call here to do things like
04474     // not split a list if you are after the last <li> or before the first, etc.
04475     // for now we just have some smarts about unneccessarily splitting
04476     // textnodes, which should be universal enough to put straight in
04477     // this nsEditor routine.
04478     
04479     nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
04480     PRUint32 len;
04481     PRBool bDoSplit = PR_FALSE;
04482     res = GetLengthOfDOMNode(nodeToSplit, len);
04483     if (NS_FAILED(res)) return res;
04484     
04485     if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (PRInt32)len)))
04486     {
04487       bDoSplit = PR_TRUE;
04488       res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));
04489       if (NS_FAILED(res)) return res;
04490       if (outRightNode) *outRightNode = nodeToSplit;
04491       if (outLeftNode)  *outLeftNode  = tempNode;
04492     }
04493 
04494     res = nodeToSplit->GetParentNode(getter_AddRefs(parentNode));
04495     if (NS_FAILED(res)) return res;
04496     if (!parentNode) return NS_ERROR_FAILURE;
04497 
04498     if (!bDoSplit && offset)  // must be "end of text node" case, we didn't split it, just move past it
04499     {
04500       offset = GetIndexOf(parentNode, nodeToSplit) +1;
04501       if (outLeftNode)  *outLeftNode  = nodeToSplit;
04502     }
04503     else
04504     {
04505       offset = GetIndexOf(parentNode, nodeToSplit);
04506       if (outRightNode) *outRightNode = nodeToSplit;
04507     }
04508     
04509     if (nodeToSplit.get() == aNode)  // we split all the way up to (and including) aNode; we're done
04510       break;
04511       
04512     nodeToSplit = parentNode;
04513   }
04514   
04515   if (!nodeToSplit)
04516   {
04517     NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()");
04518     return NS_ERROR_FAILURE;
04519   }
04520   
04521   *outOffset = offset;
04522   
04523   return NS_OK;
04524 }
04525 
04526 
04528 // JoinNodeDeep:  this joins two like nodes "deeply", joining children as 
04529 //                appropriate.  
04530 nsresult
04531 nsEditor::JoinNodeDeep(nsIDOMNode *aLeftNode, 
04532                        nsIDOMNode *aRightNode,
04533                        nsCOMPtr<nsIDOMNode> *aOutJoinNode, 
04534                        PRInt32 *outOffset)
04535 {
04536   if (!aLeftNode || !aRightNode || !aOutJoinNode || !outOffset) return NS_ERROR_NULL_POINTER;
04537 
04538   // while the rightmost children and their descendants of the left node 
04539   // match the leftmost children and their descendants of the right node
04540   // join them up.  Can you say that three times fast?
04541   
04542   nsCOMPtr<nsIDOMNode> leftNodeToJoin = do_QueryInterface(aLeftNode);
04543   nsCOMPtr<nsIDOMNode> rightNodeToJoin = do_QueryInterface(aRightNode);
04544   nsCOMPtr<nsIDOMNode> parentNode,tmp;
04545   nsresult res = NS_OK;
04546   
04547   rightNodeToJoin->GetParentNode(getter_AddRefs(parentNode));
04548   
04549   while (leftNodeToJoin && rightNodeToJoin && parentNode &&
04550           NodesSameType(leftNodeToJoin, rightNodeToJoin))
04551   {
04552     // adjust out params
04553     PRUint32 length;
04554     if (IsTextNode(leftNodeToJoin))
04555     {
04556       nsCOMPtr<nsIDOMCharacterData>nodeAsText;
04557       nodeAsText = do_QueryInterface(leftNodeToJoin);
04558       nodeAsText->GetLength(&length);
04559     }
04560     else
04561     {
04562       res = GetLengthOfDOMNode(leftNodeToJoin, length);
04563       if (NS_FAILED(res)) return res;
04564     }
04565     
04566     *aOutJoinNode = rightNodeToJoin;
04567     *outOffset = length;
04568     
04569     // do the join
04570     res = JoinNodes(leftNodeToJoin, rightNodeToJoin, parentNode);
04571     if (NS_FAILED(res)) return res;
04572     
04573     if (IsTextNode(parentNode)) // we've joined all the way down to text nodes, we're done!
04574       return NS_OK;
04575 
04576     else
04577     {
04578       // get new left and right nodes, and begin anew
04579       parentNode = rightNodeToJoin;
04580       leftNodeToJoin = GetChildAt(parentNode, length-1);
04581       rightNodeToJoin = GetChildAt(parentNode, length);
04582 
04583       // skip over non-editable nodes
04584       while (leftNodeToJoin && !IsEditable(leftNodeToJoin))
04585       {
04586         leftNodeToJoin->GetPreviousSibling(getter_AddRefs(tmp));
04587         leftNodeToJoin = tmp;
04588       }
04589       if (!leftNodeToJoin) break;
04590     
04591       while (rightNodeToJoin && !IsEditable(rightNodeToJoin))
04592       {
04593         rightNodeToJoin->GetNextSibling(getter_AddRefs(tmp));
04594         rightNodeToJoin = tmp;
04595       }
04596       if (!rightNodeToJoin) break;
04597     }
04598   }
04599   
04600   return res;
04601 }
04602 
04603 nsresult nsEditor::BeginUpdateViewBatch()
04604 {
04605   NS_PRECONDITION(mUpdateCount >= 0, "bad state");
04606 
04607 
04608   if (0 == mUpdateCount)
04609   {
04610     // Turn off selection updates and notifications.
04611 
04612     nsCOMPtr<nsISelection> selection;
04613     GetSelection(getter_AddRefs(selection));
04614 
04615     if (selection) 
04616     {
04617       nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
04618       selPrivate->StartBatchChanges();
04619     }
04620 
04621     // Turn off view updating.
04622 
04623     if (mViewManager)
04624       mViewManager->BeginUpdateViewBatch();
04625 
04626     // Turn off reflow.
04627 
04628     nsCOMPtr<nsIPresShell> presShell;
04629     GetPresShell(getter_AddRefs(presShell));
04630 
04631     if (presShell)
04632       presShell->BeginReflowBatching();
04633   }
04634 
04635   mUpdateCount++;
04636 
04637   return NS_OK;
04638 }
04639 
04640 
04641 nsresult nsEditor::EndUpdateViewBatch()
04642 {
04643   NS_PRECONDITION(mUpdateCount > 0, "bad state");
04644   
04645   if (mUpdateCount <= 0)
04646   {
04647     mUpdateCount = 0;
04648     return NS_ERROR_FAILURE;
04649   }
04650 
04651   mUpdateCount--;
04652 
04653   if (0 == mUpdateCount)
04654   {
04655     // Hide the caret with an StCaretHider. By the time it goes out
04656     // of scope and tries to show the caret, reflow and selection changed
04657     // notifications should've happened so the caret should have enough info
04658     // to draw at the correct position.
04659 
04660     nsCOMPtr<nsICaret> caret;
04661     nsCOMPtr<nsIPresShell> presShell;
04662     GetPresShell(getter_AddRefs(presShell));
04663 
04664     if (presShell)
04665       presShell->GetCaret(getter_AddRefs(caret));
04666 
04667     StCaretHider caretHider(caret);
04668         
04669     PRUint32 flags = 0;
04670 
04671     GetFlags(&flags);
04672 
04673     // Turn reflow back on.
04674     //
04675     // Make sure we enable reflowing before we call
04676     // mViewManager->EndUpdateViewBatch().  This will make sure that any
04677     // new updates caused by a reflow, that may happen during the
04678     // EndReflowBatching(), get included if we force a refresh during
04679     // the mViewManager->EndUpdateViewBatch() call.
04680 
04681     if (presShell)
04682     {
04683       PRBool forceReflow = PR_TRUE;
04684 
04685       if (flags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask)
04686         forceReflow = PR_FALSE;
04687 
04688       presShell->EndReflowBatching(forceReflow);
04689     }
04690 
04691     // Turn view updating back on.
04692 
04693     if (mViewManager)
04694     {
04695       PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE;
04696 
04697       // If we're doing async updates, use NS_VMREFRESH_DEFERRED here, so that
04698       // the reflows we caused will get processed before the invalidates.
04699       if (flags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask)
04700         updateFlag = NS_VMREFRESH_DEFERRED;
04701 
04702       mViewManager->EndUpdateViewBatch(updateFlag);
04703     }
04704 
04705     // Turn selection updating and notifications back on.
04706 
04707     nsCOMPtr<nsISelection>selection;
04708     GetSelection(getter_AddRefs(selection));
04709 
04710     if (selection) {
04711       nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
04712       selPrivate->EndBatchChanges();
04713     }
04714   }
04715 
04716   return NS_OK;
04717 }
04718 
04719 PRBool 
04720 nsEditor::GetShouldTxnSetSelection()
04721 {
04722   return mShouldTxnSetSelection;
04723 }
04724 
04725 
04726 #ifdef XP_MAC
04727 #pragma mark -
04728 #pragma mark  protected nsEditor methods 
04729 #pragma mark -
04730 #endif
04731 
04732 
04733 NS_IMETHODIMP 
04734 nsEditor::DeleteSelectionImpl(nsIEditor::EDirection aAction)
04735 {
04736   nsCOMPtr<nsISelection>selection;
04737   nsresult res = GetSelection(getter_AddRefs(selection));
04738   if (NS_FAILED(res)) return res;
04739   nsRefPtr<EditAggregateTxn> txn;
04740   res = CreateTxnForDeleteSelection(aAction, getter_AddRefs(txn));
04741   if (NS_FAILED(res)) return res;
04742   nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
04743 
04744   PRInt32 i;
04745   nsIEditActionListener *listener;
04746   if (NS_SUCCEEDED(res))  
04747   {
04748     if (mActionListeners)
04749     {
04750       for (i = 0; i < mActionListeners->Count(); i++)
04751       {
04752         listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
04753         if (listener)
04754           listener->WillDeleteSelection(selection);
04755       }
04756     }
04757 
04758     res = DoTransaction(txn);  
04759 
04760     if (mActionListeners)
04761     {
04762       for (i = 0; i < mActionListeners->Count(); i++)
04763       {
04764         listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
04765         if (listener)
04766           listener->DidDeleteSelection(selection);
04767       }
04768     }
04769   }
04770 
04771   return res;
04772 }
04773 
04774 // XXX: error handling in this routine needs to be cleaned up!
04775 NS_IMETHODIMP
04776 nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
04777                                            nsIDOMNode ** aNewNode)
04778 {
04779   nsCOMPtr<nsIDOMNode> parentSelectedNode;
04780   PRInt32 offsetOfNewNode;
04781   nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
04782                                                           offsetOfNewNode);
04783   if (NS_FAILED(result))
04784     return result;
04785 
04786   nsCOMPtr<nsIDOMNode> newNode;
04787   result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
04788                       getter_AddRefs(newNode));
04789   // XXX: ERROR_HANDLING  check result, and make sure aNewNode is set correctly in success/failure cases
04790   *aNewNode = newNode;
04791   NS_IF_ADDREF(*aNewNode);
04792 
04793   // we want the selection to be just after the new node
04794   nsCOMPtr<nsISelection> selection;
04795   result = GetSelection(getter_AddRefs(selection));
04796   if (NS_FAILED(result)) return result;
04797   if (!selection) return NS_ERROR_NULL_POINTER;
04798   return selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
04799 }
04800 
04801 
04802 /* Non-interface, protected methods */
04803 
04804 nsresult
04805 nsEditor::GetIMEBufferLength(PRInt32* length)
04806 {
04807   *length = mIMEBufferLength;
04808   return NS_OK;
04809 }
04810 
04811 void
04812 nsEditor::SetIsIMEComposing(){  
04813   // We set mIsIMEComposing according to mIMETextRangeList.
04814   nsCOMPtr<nsIPrivateTextRange> rangePtr;
04815   PRUint16 listlen, type;
04816 
04817   mIsIMEComposing = PR_FALSE;
04818   nsresult result = mIMETextRangeList->GetLength(&listlen);
04819   if (NS_FAILED(result)) return;
04820 
04821   for (PRUint16 i = 0; i < listlen; i++)
04822   {
04823       result = mIMETextRangeList->Item(i, getter_AddRefs(rangePtr));
04824       if (NS_FAILED(result)) continue;
04825       result = rangePtr->GetRangeType(&type);
04826       if (NS_FAILED(result)) continue;
04827       if ( type == nsIPrivateTextRange::TEXTRANGE_RAWINPUT ||
04828            type == nsIPrivateTextRange::TEXTRANGE_CONVERTEDTEXT ||
04829            type == nsIPrivateTextRange::TEXTRANGE_SELECTEDRAWTEXT ||
04830            type == nsIPrivateTextRange::TEXTRANGE_SELECTEDCONVERTEDTEXT )
04831       {
04832         mIsIMEComposing = PR_TRUE;
04833 #ifdef DEBUG_IME
04834         printf("nsEditor::mIsIMEComposing = PR_TRUE\n");
04835 #endif
04836         break;
04837       }
04838   }
04839   return;
04840 }
04841 
04842 PRBool
04843 nsEditor::IsIMEComposing() {
04844   return mIsIMEComposing;
04845 }
04846 
04847 NS_IMETHODIMP
04848 nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
04849 {
04850   nsresult result=NS_ERROR_NOT_INITIALIZED;
04851   nsCOMPtr<nsISelection> selection;
04852   result = GetSelection(getter_AddRefs(selection));
04853   if (NS_FAILED(result)) return result;
04854   if (!selection) return NS_ERROR_NULL_POINTER;
04855 
04856   PRBool collapsed;
04857   result = selection->GetIsCollapsed(&collapsed);
04858   if (NS_SUCCEEDED(result) && !collapsed) 
04859   {
04860     result = DeleteSelection(nsIEditor::eNone);
04861     if (NS_FAILED(result)) {
04862       return result;
04863     }
04864     // get the new selection
04865     result = GetSelection(getter_AddRefs(selection));
04866     if (NS_FAILED(result)) {
04867       return result;
04868     }
04869 #ifdef NS_DEBUG
04870     nsCOMPtr<nsIDOMNode>testSelectedNode;
04871     nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
04872     // no selection is ok.
04873     // if there is a selection, it must be collapsed
04874     if (testSelectedNode)
04875     {
04876       PRBool testCollapsed;
04877       debugResult = selection->GetIsCollapsed(&testCollapsed);
04878       NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
04879       NS_ASSERTION(testCollapsed, "selection not reset after deletion");
04880     }
04881 #endif
04882   }
04883   // split the selected node
04884   PRInt32 offsetOfSelectedNode;
04885   result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
04886   if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
04887   {
04888     nsCOMPtr<nsIDOMNode> selectedNode;
04889     PRUint32 selectedNodeContentCount=0;
04890     nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
04891     selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
04892 
04893     offsetOfNewNode = offsetOfSelectedNode;
04894     
04895     /* if the selection is a text node, split the text node if necesary
04896        and compute where to put the new node
04897     */
04898     if (selectedParentNodeAsText) 
04899     { 
04900       PRInt32 indexOfTextNodeInParent;
04901       selectedNode = do_QueryInterface(parentSelectedNode);
04902       selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
04903       selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
04904       GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
04905 
04906       if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
04907       {
04908         nsCOMPtr<nsIDOMNode> newSiblingNode;
04909         result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
04910         // now get the node's offset in it's parent, and insert the new tag there
04911         if (NS_SUCCEEDED(result)) {
04912           result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
04913         }
04914       }
04915       else 
04916       { // determine where to insert the new node
04917         if (0==offsetOfSelectedNode) {
04918           offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
04919         }
04920         else {                 // insert new node as last child
04921           GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
04922           offsetOfNewNode++;    // offsets are 0-based, and we need the index of the new node
04923         }
04924       }
04925     }
04926     // Here's where the new node was inserted
04927   }
04928 #ifdef DEBUG
04929   else {
04930     printf("InsertLineBreak into an empty document is not yet supported\n");
04931   }
04932 #endif
04933   return result;
04934 }
04935 
04936 
04937 
04938 NS_IMETHODIMP 
04939 nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
04940 {
04941   nsresult rv = NS_OK;
04942   
04943   PRBool  isTransientTransaction;
04944   rv = aTxn->GetIsTransient(&isTransientTransaction);
04945   if (NS_FAILED(rv))
04946     return rv;
04947   
04948   if (!isTransientTransaction)
04949   {
04950     // we need to deal here with the case where the user saved after some
04951     // edits, then undid one or more times. Then, the undo count is -ve,
04952     // but we can't let a do take it back to zero. So we flip it up to
04953     // a +ve number.
04954     PRInt32 modCount;
04955     GetModificationCount(&modCount);
04956     if (modCount < 0)
04957       modCount = -modCount;
04958         
04959     rv = IncrementModificationCount(1);    // don't count transient transactions
04960   }
04961   
04962   return rv;
04963 }
04964 
04965 
04966 NS_IMETHODIMP 
04967 nsEditor::DoAfterUndoTransaction()
04968 {
04969   nsresult rv = NS_OK;
04970 
04971   rv = IncrementModificationCount(-1);    // all undoable transactions are non-transient
04972 
04973   return rv;
04974 }
04975 
04976 NS_IMETHODIMP 
04977 nsEditor::DoAfterRedoTransaction()
04978 {
04979   return IncrementModificationCount(1);    // all redoable transactions are non-transient
04980 }
04981 
04982 NS_IMETHODIMP 
04983 nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, 
04984                                    const nsAString& aAttribute, 
04985                                    const nsAString& aValue,
04986                                    ChangeAttributeTxn ** aTxn)
04987 {
04988   nsresult result = NS_ERROR_NULL_POINTER;
04989   if (nsnull != aElement)
04990   {
04991     result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn);
04992     if (NS_SUCCEEDED(result))  {
04993       result = (*aTxn)->Init(this, aElement, aAttribute, aValue, PR_FALSE);
04994     }
04995   }
04996   return result;
04997 }
04998 
04999 
05000 NS_IMETHODIMP 
05001 nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, 
05002                                       const nsAString& aAttribute,
05003                                       ChangeAttributeTxn ** aTxn)
05004 {
05005   nsresult result = NS_ERROR_NULL_POINTER;
05006   if (nsnull != aElement)
05007   {
05008     result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn);
05009     if (NS_SUCCEEDED(result))  
05010     {
05011       nsAutoString value;
05012       result = (*aTxn)->Init(this, aElement, aAttribute, value, PR_TRUE);
05013     }
05014   }
05015   return result;
05016 }
05017 
05018 
05019 NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsAString& aTag,
05020                                                   nsIDOMNode     *aParent,
05021                                                   PRInt32         aPosition,
05022                                                   CreateElementTxn ** aTxn)
05023 {
05024   nsresult result = NS_ERROR_NULL_POINTER;
05025   if (nsnull != aParent)
05026   {
05027     result = TransactionFactory::GetNewTransaction(CreateElementTxn::GetCID(), (EditTxn **)aTxn);
05028     if (NS_SUCCEEDED(result))  {
05029       result = (*aTxn)->Init(this, aTag, aParent, aPosition);
05030     }
05031   }
05032   return result;
05033 }
05034 
05035 
05036 NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode,
05037                                                   nsIDOMNode * aParent,
05038                                                   PRInt32      aPosition,
05039                                                   InsertElementTxn ** aTxn)
05040 {
05041   nsresult result = NS_ERROR_NULL_POINTER;
05042   if (aNode && aParent && aTxn)
05043   {
05044     result = TransactionFactory::GetNewTransaction(InsertElementTxn::GetCID(), (EditTxn **)aTxn);
05045     if (NS_SUCCEEDED(result)) {
05046       result = (*aTxn)->Init(aNode, aParent, aPosition, this);
05047     }
05048   }
05049   return result;
05050 }
05051 
05052 NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement,
05053                                              DeleteElementTxn ** aTxn)
05054 {
05055   nsresult result = NS_ERROR_NULL_POINTER;
05056   if (nsnull != aElement)
05057   {
05058     result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn);
05059     if (NS_SUCCEEDED(result)) {
05060       result = (*aTxn)->Init(aElement, &mRangeUpdater);
05061     }
05062   }
05063   return result;
05064 }
05065 
05066 /*NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) 
05067 {
05068   nsresult result = NS_ERROR_NULL_POINTER;
05069   if (aAggTxn)
05070   {
05071     *aAggTxn = nsnull;
05072     result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn**)aAggTxn); 
05073 
05074     if (NS_FAILED(result) || !*aAggTxn) {
05075       return NS_ERROR_OUT_OF_MEMORY;
05076     }
05077 
05078     // Set the name for the aggregate transaction  
05079     (*aAggTxn)->SetName(aTxnName);
05080 
05081     // Get current selection and setup txn to delete it,
05082     //  but only if selection exists (is not a collapsed "caret" state)
05083     if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
05084     nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
05085     if (!ps) return NS_ERROR_NOT_INITIALIZED;
05086     nsCOMPtr<nsISelection> selection;
05087     result = ps->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
05088     if (NS_SUCCEEDED(result) && selection)
05089     {
05090       PRBool collapsed;
05091       result = selection->GetIsCollapsed(&collapsed);
05092       if (NS_SUCCEEDED(result) && !collapsed) {
05093         EditAggregateTxn *delSelTxn;
05094         result = CreateTxnForDeleteSelection(eNone, &delSelTxn);
05095         if (NS_SUCCEEDED(result) && delSelTxn) {
05096           (*aAggTxn)->AppendChild(delSelTxn);
05097           NS_RELEASE(delSelTxn);
05098         }
05099       }
05100     }
05101   }
05102   return result;
05103 }
05104 */
05105 
05106 NS_IMETHODIMP 
05107 nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
05108                               IMETextTxn ** aTxn)
05109 {
05110   NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
05111   if(!aTxn) return NS_ERROR_NULL_POINTER;
05112      
05113   nsresult  result;
05114 
05115   result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn);
05116   if (nsnull!=*aTxn) {
05117     result = (*aTxn)->Init(mIMETextNode,mIMETextOffset,mIMEBufferLength,mIMETextRangeList,aStringToInsert,mSelConWeak);
05118   }
05119   else {
05120     result = NS_ERROR_OUT_OF_MEMORY;
05121   }
05122   return result;
05123 }
05124 
05125 
05126 NS_IMETHODIMP 
05127 nsEditor::CreateTxnForAddStyleSheet(nsICSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn)
05128 {
05129   nsresult rv = TransactionFactory::GetNewTransaction(AddStyleSheetTxn::GetCID(), (EditTxn **)aTxn);
05130   if (NS_FAILED(rv))
05131     return rv;
05132     
05133   if (! *aTxn)
05134     return NS_ERROR_OUT_OF_MEMORY;
05135 
05136   return (*aTxn)->Init(this, aSheet);
05137 }
05138 
05139 
05140 
05141 NS_IMETHODIMP 
05142 nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn)
05143 {
05144   nsresult rv = TransactionFactory::GetNewTransaction(RemoveStyleSheetTxn::GetCID(), (EditTxn **)aTxn);
05145   if (NS_FAILED(rv))
05146     return rv;
05147     
05148   if (! *aTxn)
05149     return NS_ERROR_OUT_OF_MEMORY;
05150 
05151   return (*aTxn)->Init(this, aSheet);
05152 }
05153 
05154 
05155 NS_IMETHODIMP
05156 nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction,
05157                                       EditAggregateTxn  ** aTxn)
05158 {
05159   if (!aTxn)
05160     return NS_ERROR_NULL_POINTER;
05161   *aTxn = nsnull;
05162 
05163 #ifdef DEBUG_akkana
05164   NS_ASSERTION(aAction != eNextWord && aAction != ePreviousWord && aAction != eToEndOfLine, "CreateTxnForDeleteSelection: unsupported action!");
05165 #endif
05166 
05167   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
05168   if (!selCon) return NS_ERROR_NOT_INITIALIZED;
05169   nsCOMPtr<nsISelection> selection;
05170   nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
05171                                          getter_AddRefs(selection));
05172   if ((NS_SUCCEEDED(result)) && selection)
05173   {
05174     // Check whether the selection is collapsed and we should do nothing:
05175     PRBool isCollapsed;
05176     result = (selection->GetIsCollapsed(&isCollapsed));
05177     if (NS_SUCCEEDED(result) && isCollapsed && aAction == eNone)
05178       return NS_OK;
05179 
05180     // allocate the out-param transaction
05181     result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn **)aTxn);
05182     if (NS_FAILED(result)) {
05183       return result;
05184     }
05185 
05186     nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
05187     nsCOMPtr<nsIEnumerator> enumerator;
05188     result = selPrivate->GetEnumerator(getter_AddRefs(enumerator));
05189     if (NS_SUCCEEDED(result) && enumerator)
05190     {
05191       for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
05192       {
05193         nsCOMPtr<nsISupports> currentItem;
05194         result = enumerator->CurrentItem(getter_AddRefs(currentItem));
05195         if ((NS_SUCCEEDED(result)) && (currentItem))
05196         {
05197           nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
05198           range->GetCollapsed(&isCollapsed);
05199           if (!isCollapsed)
05200           {
05201             DeleteRangeTxn *txn;
05202             result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn);
05203             if (NS_SUCCEEDED(result) && txn)
05204             {
05205               txn->Init(this, range, &mRangeUpdater);
05206               (*aTxn)->AppendChild(txn);
05207               NS_RELEASE(txn);
05208             }
05209             else
05210               result = NS_ERROR_OUT_OF_MEMORY;
05211           }
05212           else
05213           { // we have an insertion point.  delete the thing in front of it or behind it, depending on aAction
05214             result = CreateTxnForDeleteInsertionPoint(range, aAction, *aTxn);
05215           }
05216         }
05217       }
05218     }
05219   }
05220 
05221   // if we didn't build the transaction correctly, destroy the out-param transaction so we don't leak it.
05222   if (NS_FAILED(result))
05223   {
05224     NS_IF_RELEASE(*aTxn);
05225   }
05226 
05227   return result;
05228 }
05229 
05230 
05231 //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented
05232 NS_IMETHODIMP
05233 nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange          *aRange, 
05234                                            nsIEditor::EDirection aAction,
05235                                            EditAggregateTxn     *aTxn)
05236 {
05237   // get the node and offset of the insertion point
05238   nsCOMPtr<nsIDOMNode> node;
05239   nsresult result = aRange->GetStartContainer(getter_AddRefs(node));
05240   if (NS_FAILED(result))
05241     return result;
05242 
05243   PRInt32 offset;
05244   result = aRange->GetStartOffset(&offset);
05245   if (NS_FAILED(result))
05246     return result;
05247 
05248   // determine if the insertion point is at the beginning, middle, or end of the node
05249   nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(node);
05250 
05251   PRUint32 count=0;
05252 
05253   if (nodeAsText)
05254     nodeAsText->GetLength(&count);
05255   else
05256   { 
05257     // get the child list and count
05258     nsCOMPtr<nsIDOMNodeList>childList;
05259     result = node->GetChildNodes(getter_AddRefs(childList));
05260     if ((NS_SUCCEEDED(result)) && childList)
05261       childList->GetLength(&count);
05262   }
05263 
05264   PRBool isFirst = (0 == offset);
05265   PRBool isLast  = (count == (PRUint32)offset);
05266 
05267   // XXX: if isFirst && isLast, then we'll need to delete the node 
05268   //      as well as the 1 child
05269 
05270   // build a transaction for deleting the appropriate data
05271   // XXX: this has to come from rule section
05272   if ((ePrevious==aAction) && (PR_TRUE==isFirst))
05273   { // we're backspacing from the beginning of the node.  Delete the first thing to our left
05274     nsCOMPtr<nsIDOMNode> priorNode;
05275     result = GetPriorNode(node, PR_TRUE, address_of(priorNode));
05276     if ((NS_SUCCEEDED(result)) && priorNode)
05277     { // there is a priorNode, so delete it's last child (if text content, delete the last char.)
05278       // if it has no children, delete it
05279       nsCOMPtr<nsIDOMCharacterData> priorNodeAsText = do_QueryInterface(priorNode);
05280       if (priorNodeAsText)
05281       {
05282         PRUint32 length=0;
05283         priorNodeAsText->GetLength(&length);
05284         if (0<length)
05285         {
05286           DeleteTextTxn *txn;
05287           result = CreateTxnForDeleteText(priorNodeAsText, length-1, 1, &txn);
05288           if (NS_SUCCEEDED(result)) {
05289             aTxn->AppendChild(txn);
05290             NS_RELEASE(txn);
05291           }
05292         }
05293         else
05294         { // XXX: can you have an empty text node?  If so, what do you do?
05295           printf("ERROR: found a text node with 0 characters\n");
05296           result = NS_ERROR_UNEXPECTED;
05297         }
05298       }
05299       else
05300       { // priorNode is not text, so tell it's parent to delete it
05301         DeleteElementTxn *txn;
05302         result = CreateTxnForDeleteElement(priorNode, &txn);
05303         if (NS_SUCCEEDED(result)) {
05304           aTxn->AppendChild(txn);
05305           NS_RELEASE(txn);
05306         }
05307       }
05308     }
05309   }
05310   else if ((nsIEditor::eNext==aAction) && (PR_TRUE==isLast))
05311   { // we're deleting from the end of the node.  Delete the first thing to our right
05312     nsCOMPtr<nsIDOMNode> nextNode;
05313     result = GetNextNode(node, PR_TRUE, address_of(nextNode));
05314     if ((NS_SUCCEEDED(result)) && nextNode)
05315     { // there is a nextNode, so delete it's first child (if text content, delete the first char.)
05316       // if it has no children, delete it
05317       nsCOMPtr<nsIDOMCharacterData> nextNodeAsText = do_QueryInterface(nextNode);
05318       if (nextNodeAsText)
05319       {
05320         PRUint32 length=0;
05321         nextNodeAsText->GetLength(&length);
05322         if (0<length)
05323         {
05324           DeleteTextTxn *txn;
05325           result = CreateTxnForDeleteText(nextNodeAsText, 0, 1, &txn);
05326           if (NS_SUCCEEDED(result)) {
05327             aTxn->AppendChild(txn);
05328             NS_RELEASE(txn);
05329           }
05330         }
05331         else
05332         { // XXX: can you have an empty text node?  If so, what do you do?
05333           printf("ERROR: found a text node with 0 characters\n");
05334           result = NS_ERROR_UNEXPECTED;
05335         }
05336       }
05337       else
05338       { // nextNode is not text, so tell it's parent to delete it
05339         DeleteElementTxn *txn;
05340         result = CreateTxnForDeleteElement(nextNode, &txn);
05341         if (NS_SUCCEEDED(result)) {
05342           aTxn->AppendChild(txn);
05343           NS_RELEASE(txn);
05344         }
05345       }
05346     }
05347   }
05348   else
05349   {
05350     if (nodeAsText)
05351     { // we have text, so delete a char at the proper offset
05352       if (nsIEditor::ePrevious==aAction) {
05353         offset --;
05354       }
05355       DeleteTextTxn *txn;
05356       result = CreateTxnForDeleteText(nodeAsText, offset, 1, &txn);
05357       if (NS_SUCCEEDED(result)) {
05358         aTxn->AppendChild(txn);
05359         NS_RELEASE(txn);
05360       }
05361     }
05362     else
05363     { // we're either deleting a node or some text, need to dig into the next/prev node to find out
05364       nsCOMPtr<nsIDOMNode> selectedNode;
05365       if (ePrevious==aAction)
05366       {
05367         result = GetPriorNode(node, offset, PR_TRUE, address_of(selectedNode));
05368       }
05369       else if (eNext==aAction)
05370       {
05371         result = GetNextNode(node, offset, PR_TRUE, address_of(selectedNode));
05372       }
05373       if (NS_FAILED(result)) { return result; }
05374       if (selectedNode) 
05375       {
05376         nsCOMPtr<nsIDOMCharacterData> selectedNodeAsText =
05377                                              do_QueryInterface(selectedNode);
05378         if (selectedNodeAsText)
05379         { // we are deleting from a text node, so do a text deletion
05380           PRInt32 begin = 0;    // default for forward delete
05381           if (ePrevious==aAction)
05382           {
05383             PRUint32 length=0;
05384             selectedNodeAsText->GetLength(&length);
05385             if (0<length)
05386               begin = length-1;
05387           }
05388           DeleteTextTxn *delTextTxn;
05389           result = CreateTxnForDeleteText(selectedNodeAsText, begin, 1, &delTextTxn);
05390           if (NS_FAILED(result))  { return result; }
05391           if (!delTextTxn) { return NS_ERROR_NULL_POINTER; }
05392           aTxn->AppendChild(delTextTxn);
05393           NS_RELEASE(delTextTxn);
05394         }
05395         else
05396         {
05397           DeleteElementTxn *delElementTxn;
05398           result = CreateTxnForDeleteElement(selectedNode, &delElementTxn);
05399           if (NS_FAILED(result))  { return result; }
05400           if (!delElementTxn) { return NS_ERROR_NULL_POINTER; }
05401           aTxn->AppendChild(delElementTxn);
05402           NS_RELEASE(delElementTxn);
05403         }
05404       }
05405     }
05406   }
05407   return result;
05408 }
05409 
05410 nsresult 
05411 nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset,
05412                       nsIDOMNode *aEndParent, PRInt32 aEndOffset,
05413                       nsIDOMRange **aRange)
05414 {
05415   nsresult result;
05416   result = CallCreateInstance("@mozilla.org/content/range;1", aRange);
05417   if (NS_FAILED(result))
05418     return result;
05419 
05420   if (!*aRange)
05421     return NS_ERROR_NULL_POINTER;
05422 
05423   result = (*aRange)->SetStart(aStartParent, aStartOffset);
05424 
05425   if (NS_SUCCEEDED(result))
05426     result = (*aRange)->SetEnd(aEndParent, aEndOffset);
05427 
05428   if (NS_FAILED(result))
05429   {
05430     NS_RELEASE((*aRange));
05431     *aRange = 0;
05432   }
05433   return result;
05434 }
05435 
05436 nsresult 
05437 nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode)
05438 {
05439   if (!aNode) return NS_ERROR_NULL_POINTER;
05440   nsCOMPtr<nsISelection> selection;
05441   nsresult res = GetSelection(getter_AddRefs(selection));
05442   if (NS_FAILED(res)) return res;
05443   if(!selection) return NS_ERROR_FAILURE;
05444 
05445   nsCOMPtr<nsIDOMNode> parentNode;
05446   res = aNode->GetParentNode(getter_AddRefs(parentNode));
05447   if (NS_FAILED(res)) return res;
05448   if (!parentNode) return NS_ERROR_NULL_POINTER;
05449   
05450   PRInt32 offset;
05451   res = GetChildOffset(aNode, parentNode, offset);
05452   if (NS_FAILED(res)) return res;
05453   
05454   nsCOMPtr<nsIDOMRange> range;
05455   res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range));
05456   if (NS_FAILED(res)) return res;
05457   if (!range) return NS_ERROR_NULL_POINTER;
05458 
05459   return selection->AddRange(range);
05460 }
05461 
05462 nsresult nsEditor::ClearSelection()
05463 {
05464   nsCOMPtr<nsISelection> selection;
05465   nsresult res = nsEditor::GetSelection(getter_AddRefs(selection));
05466   if (NS_FAILED(res)) return res;
05467   if (!selection) return NS_ERROR_FAILURE;
05468   return selection->RemoveAllRanges();  
05469 }
05470 
05471 nsresult
05472 nsEditor::CreateHTMLContent(const nsAString& aTag, nsIContent** aContent)
05473 {
05474   nsCOMPtr<nsIDOMDocument> tempDoc;
05475   GetDocument(getter_AddRefs(tempDoc));
05476 
05477   nsCOMPtr<nsIDocument> doc = do_QueryInterface(tempDoc);
05478   if (!doc)
05479     return NS_ERROR_FAILURE;
05480 
05481   // XXX Wallpaper over editor bug (editor tries to create elements with an
05482   //     empty nodename).
05483   if (aTag.IsEmpty()) {
05484     NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, "
05485              "check caller.");
05486     return NS_ERROR_FAILURE;
05487   }
05488 
05489   nsCOMPtr<nsIAtom> tag = do_GetAtom(aTag);
05490   if (!tag)
05491     return NS_ERROR_OUT_OF_MEMORY;
05492 
05493   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(tempDoc);
05494   if (htmlDoc) {
05495       return doc->CreateElem(tag, nsnull, doc->GetDefaultNamespaceID(),
05496                              PR_TRUE, aContent);
05497   }
05498 
05499   return doc->CreateElem(tag, nsnull, kNameSpaceID_XHTML, PR_FALSE, aContent);
05500 }
05501 
05502 nsresult
05503 nsEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
05504                                    const nsAString & aAttribute,
05505                                    const nsAString & aValue,
05506                                    PRBool aSuppressTransaction)
05507 {
05508   return SetAttribute(aElement, aAttribute, aValue);
05509 }
05510 
05511 nsresult
05512 nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
05513                                       const nsAString & aAttribute,
05514                                       PRBool aSuppressTransaction)
05515 {
05516   return RemoveAttribute(aElement, aAttribute);
05517 }
05518 
05519 nsresult
05520 nsEditor::HandleInlineSpellCheck(PRInt32 action,
05521                                    nsISelection *aSelection,
05522                                    nsIDOMNode *previousSelectedNode,
05523                                    PRInt32 previousSelectedOffset,
05524                                    nsIDOMNode *aStartNode,
05525                                    PRInt32 aStartOffset,
05526                                    nsIDOMNode *aEndNode,
05527                                    PRInt32 aEndOffset)
05528 {
05529   return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(action,
05530                                                        aSelection,
05531                                                        previousSelectedNode,
05532                                                        previousSelectedOffset,
05533                                                        aStartNode,
05534                                                        aStartOffset,
05535                                                        aEndNode,
05536                                                        aEndOffset) : NS_OK;
05537 }
05538 
05539 already_AddRefed<nsIDOMEventReceiver>
05540 nsEditor::GetDOMEventReceiver()
05541 {
05542   nsIDOMEventReceiver *erp = mDOMEventReceiver;
05543   if (erp)
05544   {
05545     NS_ADDREF(erp);
05546     return erp;
05547   }
05548 
05549   nsIDOMElement *rootElement = GetRoot();
05550 
05551   // Now hack to make sure we are not anonymous content.
05552   // If we are grab the parent of root element for our observer.
05553 
05554   nsCOMPtr<nsIContent> content = do_QueryInterface(rootElement);
05555 
05556   if (content && content->IsNativeAnonymous())
05557   {
05558     mDOMEventReceiver = do_QueryInterface(content->GetParent());
05559     erp = mDOMEventReceiver;
05560     NS_IF_ADDREF(erp);
05561   }
05562   else
05563   {
05564     // Don't use getDocument here, because we have no way of knowing
05565     // if Init() was ever called.  So we need to get the document
05566     // ourselves, if it exists.
05567     if (mDocWeak)
05568     {
05569       CallQueryReferent(mDocWeak.get(), &erp);
05570     }
05571     else
05572     {
05573       NS_ERROR("not initialized yet");
05574     }
05575   }
05576 
05577   return erp;
05578 }
05579 
05580 nsIDOMElement *
05581 nsEditor::GetRoot()
05582 {
05583   if (!mRootElement)
05584   {
05585     nsCOMPtr<nsIDOMElement> root;
05586 
05587     // Let GetRootElement() do the work
05588     GetRootElement(getter_AddRefs(root));
05589   }
05590 
05591   return mRootElement;
05592 }
05593 
05594 NS_IMETHODIMP
05595 nsEditor::SwitchTextDirection()
05596 {
05597   // Get the current root direction from its frame
05598   nsIDOMElement *rootElement = GetRoot();
05599 
05600   nsresult rv;
05601   nsCOMPtr<nsIContent> content = do_QueryInterface(rootElement, &rv);
05602   if (NS_FAILED(rv))
05603     return rv;
05604 
05605   nsCOMPtr<nsIPresShell> presShell;
05606   rv = GetPresShell(getter_AddRefs(presShell));
05607   if (NS_FAILED(rv))
05608     return rv;  
05609 
05610   nsIFrame *frame = nsnull;
05611   presShell->GetPrimaryFrameFor(content, &frame);
05612   if (NS_FAILED(rv))
05613     return rv; 
05614 
05615   // Apply the opposite direction
05616   if (frame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
05617     rv = rootElement->SetAttribute(NS_LITERAL_STRING("dir"), NS_LITERAL_STRING("ltr"));
05618   else
05619     rv = rootElement->SetAttribute(NS_LITERAL_STRING("dir"), NS_LITERAL_STRING("rtl"));
05620 
05621   return rv;
05622 }
05623 
05624 #if DEBUG_JOE
05625 void
05626 nsEditor::DumpNode(nsIDOMNode *aNode, PRInt32 indent)
05627 {
05628   PRInt32 i;
05629   for (i=0; i<indent; i++)
05630     printf("  ");
05631   
05632   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
05633   nsCOMPtr<nsIDOMDocumentFragment> docfrag = do_QueryInterface(aNode);
05634   
05635   if (element || docfrag)
05636   { 
05637     if (element)
05638     {
05639       nsAutoString tag;
05640       char ctag[40];
05641       element->GetTagName(tag);
05642       tag.ToCString(ctag,40);
05643       ctag[40]=0;
05644       printf("<%s>\n", ctag);
05645     }
05646     else
05647     {
05648       printf("<document fragment>\n");
05649     }
05650     nsCOMPtr<nsIDOMNodeList> childList;
05651     aNode->GetChildNodes(getter_AddRefs(childList));
05652     if (!childList) return NS_ERROR_NULL_POINTER;
05653     PRUint32 numChildren;
05654     childList->GetLength(&numChildren);
05655     nsCOMPtr<nsIDOMNode> child, tmp;
05656     aNode->GetFirstChild(getter_AddRefs(child));
05657     for (i=0; i<numChildren; i++)
05658     {
05659       DumpNode(child, indent+1);
05660       child->GetNextSibling(getter_AddRefs(tmp));
05661       child = tmp;
05662     }
05663   }
05664   else if (IsTextNode(aNode))
05665   {
05666     nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aNode);
05667     nsAutoString str;
05668     textNode->GetData(str);
05669     char theText[30], *c;
05670     str.ToCString(theText,30);
05671     theText[30]=0;
05672     while (c=strchr(theText,'\n')) (*c) = ' ';
05673     printf("<textnode> %s\n", theText);
05674   }
05675 }
05676 #endif