editor/libeditor/base/nsEditor.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/DebugOnly.h"          // for DebugOnly
     8 #include <stdio.h>                      // for nullptr, stdout
     9 #include <string.h>                     // for strcmp
    11 #include "ChangeAttributeTxn.h"         // for ChangeAttributeTxn
    12 #include "CreateElementTxn.h"           // for CreateElementTxn
    13 #include "DeleteNodeTxn.h"              // for DeleteNodeTxn
    14 #include "DeleteRangeTxn.h"             // for DeleteRangeTxn
    15 #include "DeleteTextTxn.h"              // for DeleteTextTxn
    16 #include "EditAggregateTxn.h"           // for EditAggregateTxn
    17 #include "EditTxn.h"                    // for EditTxn
    18 #include "IMETextTxn.h"                 // for IMETextTxn
    19 #include "InsertElementTxn.h"           // for InsertElementTxn
    20 #include "InsertTextTxn.h"              // for InsertTextTxn
    21 #include "JoinElementTxn.h"             // for JoinElementTxn
    22 #include "PlaceholderTxn.h"             // for PlaceholderTxn
    23 #include "SplitElementTxn.h"            // for SplitElementTxn
    24 #include "mozFlushType.h"               // for mozFlushType::Flush_Frames
    25 #include "mozISpellCheckingEngine.h"
    26 #include "mozInlineSpellChecker.h"      // for mozInlineSpellChecker
    27 #include "mozilla/IMEStateManager.h"    // for IMEStateManager
    28 #include "mozilla/Preferences.h"        // for Preferences
    29 #include "mozilla/dom/Selection.h"      // for Selection, etc
    30 #include "mozilla/Services.h"           // for GetObserverService
    31 #include "mozilla/TextComposition.h"    // for TextComposition
    32 #include "mozilla/TextEvents.h"
    33 #include "mozilla/dom/Element.h"        // for Element, nsINode::AsElement
    34 #include "mozilla/mozalloc.h"           // for operator new, etc
    35 #include "nsAString.h"                  // for nsAString_internal::Length, etc
    36 #include "nsCCUncollectableMarker.h"    // for nsCCUncollectableMarker
    37 #include "nsCaret.h"                    // for nsCaret
    38 #include "nsCaseTreatment.h"
    39 #include "nsCharTraits.h"               // for NS_IS_HIGH_SURROGATE, etc
    40 #include "nsComponentManagerUtils.h"    // for do_CreateInstance
    41 #include "nsComputedDOMStyle.h"         // for nsComputedDOMStyle
    42 #include "nsContentUtils.h"             // for nsContentUtils
    43 #include "nsDOMString.h"                // for DOMStringIsNull
    44 #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
    45 #include "nsEditProperty.h"             // for nsEditProperty, etc
    46 #include "nsEditor.h"
    47 #include "nsEditorEventListener.h"      // for nsEditorEventListener
    48 #include "nsEditorUtils.h"              // for nsAutoRules, etc
    49 #include "nsError.h"                    // for NS_OK, etc
    50 #include "nsFocusManager.h"             // for nsFocusManager
    51 #include "nsFrameSelection.h"           // for nsFrameSelection
    52 #include "nsGkAtoms.h"                  // for nsGkAtoms, nsGkAtoms::dir
    53 #include "nsIAbsorbingTransaction.h"    // for nsIAbsorbingTransaction
    54 #include "nsIAtom.h"                    // for nsIAtom
    55 #include "nsIContent.h"                 // for nsIContent
    56 #include "nsIDOMAttr.h"                 // for nsIDOMAttr
    57 #include "nsIDOMCharacterData.h"        // for nsIDOMCharacterData
    58 #include "nsIDOMDocument.h"             // for nsIDOMDocument
    59 #include "nsIDOMElement.h"              // for nsIDOMElement
    60 #include "nsIDOMEvent.h"                // for nsIDOMEvent
    61 #include "nsIDOMEventListener.h"        // for nsIDOMEventListener
    62 #include "nsIDOMEventTarget.h"          // for nsIDOMEventTarget
    63 #include "nsIDOMHTMLElement.h"          // for nsIDOMHTMLElement
    64 #include "nsIDOMKeyEvent.h"             // for nsIDOMKeyEvent, etc
    65 #include "nsIDOMMozNamedAttrMap.h"      // for nsIDOMMozNamedAttrMap
    66 #include "nsIDOMMouseEvent.h"           // for nsIDOMMouseEvent
    67 #include "nsIDOMNode.h"                 // for nsIDOMNode, etc
    68 #include "nsIDOMNodeList.h"             // for nsIDOMNodeList
    69 #include "nsIDOMRange.h"                // for nsIDOMRange
    70 #include "nsIDOMText.h"                 // for nsIDOMText
    71 #include "nsIDocument.h"                // for nsIDocument
    72 #include "nsIDocumentStateListener.h"   // for nsIDocumentStateListener
    73 #include "nsIEditActionListener.h"      // for nsIEditActionListener
    74 #include "nsIEditorObserver.h"          // for nsIEditorObserver
    75 #include "nsIEditorSpellCheck.h"        // for nsIEditorSpellCheck
    76 #include "nsIFrame.h"                   // for nsIFrame
    77 #include "nsIHTMLDocument.h"            // for nsIHTMLDocument
    78 #include "nsIInlineSpellChecker.h"      // for nsIInlineSpellChecker, etc
    79 #include "nsNameSpaceManager.h"        // for kNameSpaceID_None, etc
    80 #include "nsINode.h"                    // for nsINode, etc
    81 #include "nsIObserverService.h"         // for nsIObserverService
    82 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc
    83 #include "nsIPresShell.h"               // for nsIPresShell
    84 #include "nsISelection.h"               // for nsISelection, etc
    85 #include "nsISelectionController.h"     // for nsISelectionController, etc
    86 #include "nsISelectionDisplay.h"        // for nsISelectionDisplay, etc
    87 #include "nsISelectionPrivate.h"        // for nsISelectionPrivate, etc
    88 #include "nsISupportsBase.h"            // for nsISupports
    89 #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_IF_ADDREF
    90 #include "nsITransaction.h"             // for nsITransaction
    91 #include "nsITransactionManager.h"
    92 #include "nsIWeakReference.h"           // for nsISupportsWeakReference
    93 #include "nsIWidget.h"                  // for nsIWidget, IMEState, etc
    94 #include "nsPIDOMWindow.h"              // for nsPIDOMWindow
    95 #include "nsPresContext.h"              // for nsPresContext
    96 #include "nsRange.h"                    // for nsRange
    97 #include "nsReadableUtils.h"            // for EmptyString, ToNewCString
    98 #include "nsString.h"                   // for nsAutoString, nsString, etc
    99 #include "nsStringFwd.h"                // for nsAFlatString
   100 #include "nsStyleConsts.h"              // for NS_STYLE_DIRECTION_RTL, etc
   101 #include "nsStyleContext.h"             // for nsStyleContext
   102 #include "nsStyleSheetTxns.h"           // for AddStyleSheetTxn, etc
   103 #include "nsStyleStruct.h"              // for nsStyleDisplay, nsStyleText, etc
   104 #include "nsStyleStructFwd.h"           // for nsIFrame::StyleUIReset, etc
   105 #include "nsTextEditUtils.h"            // for nsTextEditUtils
   106 #include "nsTextNode.h"                 // for nsTextNode
   107 #include "nsThreadUtils.h"              // for nsRunnable
   108 #include "nsTransactionManager.h"       // for nsTransactionManager
   109 #include "prtime.h"                     // for PR_Now
   111 class nsIOutputStream;
   112 class nsIParserService;
   113 class nsITransferable;
   115 #ifdef DEBUG
   116 #include "nsIDOMHTMLDocument.h"         // for nsIDOMHTMLDocument
   117 #endif
   119 using namespace mozilla;
   120 using namespace mozilla::dom;
   121 using namespace mozilla::widget;
   123 // Defined in nsEditorRegistration.cpp
   124 extern nsIParserService *sParserService;
   126 //---------------------------------------------------------------------------
   127 //
   128 // nsEditor: base editor class implementation
   129 //
   130 //---------------------------------------------------------------------------
   132 nsEditor::nsEditor()
   133 :  mPlaceHolderName(nullptr)
   134 ,  mSelState(nullptr)
   135 ,  mPhonetic(nullptr)
   136 ,  mModCount(0)
   137 ,  mFlags(0)
   138 ,  mUpdateCount(0)
   139 ,  mPlaceHolderBatch(0)
   140 ,  mAction(EditAction::none)
   141 ,  mIMETextOffset(0)
   142 ,  mDirection(eNone)
   143 ,  mDocDirtyState(-1)
   144 ,  mSpellcheckCheckboxState(eTriUnset)
   145 ,  mShouldTxnSetSelection(true)
   146 ,  mDidPreDestroy(false)
   147 ,  mDidPostCreate(false)
   148 ,  mDispatchInputEvent(true)
   149 {
   150 }
   152 nsEditor::~nsEditor()
   153 {
   154   NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
   156   mTxnMgr = nullptr;
   158   delete mPhonetic;
   159 }
   161 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor)
   163 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor)
   164  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
   165  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
   166  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTxnMgr)
   167  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMETextNode)
   168  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
   169  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers)
   170  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
   171  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
   172  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventListener)
   173 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   175 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEditor)
   176  nsIDocument* currentDoc =
   177    tmp->mRootElement ? tmp->mRootElement->GetCurrentDoc() : nullptr;
   178  if (currentDoc &&
   179      nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
   180    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   181  }
   182  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
   183  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
   184  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTxnMgr)
   185  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMETextNode)
   186  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
   187  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers)
   188  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
   189  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
   190  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
   191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   193 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor)
   194  NS_INTERFACE_MAP_ENTRY(nsIPhonetic)
   195  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   196  NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport)
   197  NS_INTERFACE_MAP_ENTRY(nsIEditor)
   198  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   199  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
   200 NS_INTERFACE_MAP_END
   202 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor)
   203 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
   206 NS_IMETHODIMP
   207 nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
   208                nsISelectionController *aSelCon, uint32_t aFlags,
   209                const nsAString& aValue)
   210 {
   211   NS_PRECONDITION(aDoc, "bad arg");
   212   if (!aDoc)
   213     return NS_ERROR_NULL_POINTER;
   215   // First only set flags, but other stuff shouldn't be initialized now.
   216   // Don't move this call after initializing mDocWeak.
   217   // SetFlags() can check whether it's called during initialization or not by
   218   // them.  Note that SetFlags() will be called by PostCreate().
   219 #ifdef DEBUG
   220   nsresult rv =
   221 #endif
   222   SetFlags(aFlags);
   223   NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
   225   mDocWeak = do_GetWeakReference(aDoc);  // weak reference to doc
   226   // HTML editors currently don't have their own selection controller,
   227   // so they'll pass null as aSelCon, and we'll get the selection controller
   228   // off of the presshell.
   229   nsCOMPtr<nsISelectionController> selCon;
   230   if (aSelCon) {
   231     mSelConWeak = do_GetWeakReference(aSelCon);   // weak reference to selectioncontroller
   232     selCon = aSelCon;
   233   } else {
   234     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   235     selCon = do_QueryInterface(presShell);
   236   }
   237   NS_ASSERTION(selCon, "Selection controller should be available at this point");
   239   //set up root element if we are passed one.  
   240   if (aRoot)
   241     mRootElement = do_QueryInterface(aRoot);
   243   mUpdateCount=0;
   245   /* initialize IME stuff */
   246   mIMETextNode = nullptr;
   247   mIMETextOffset = 0;
   248   /* Show the caret */
   249   selCon->SetCaretReadOnly(false);
   250   selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
   252   selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
   254   NS_POSTCONDITION(mDocWeak, "bad state");
   256   // Make sure that the editor will be destroyed properly
   257   mDidPreDestroy = false;
   258   // Make sure that the ediotr will be created properly
   259   mDidPostCreate = false;
   261   return NS_OK;
   262 }
   265 NS_IMETHODIMP
   266 nsEditor::PostCreate()
   267 {
   268   // Synchronize some stuff for the flags.  SetFlags() will initialize
   269   // something by the flag difference.  This is first time of that, so, all
   270   // initializations must be run.  For such reason, we need to invert mFlags
   271   // value first.
   272   mFlags = ~mFlags;
   273   nsresult rv = SetFlags(~mFlags);
   274   NS_ENSURE_SUCCESS(rv, rv);
   276   // These operations only need to happen on the first PostCreate call
   277   if (!mDidPostCreate) {
   278     mDidPostCreate = true;
   280     // Set up listeners
   281     CreateEventListeners();
   282     rv = InstallEventListeners();
   283     NS_ENSURE_SUCCESS(rv, rv);
   285     // nuke the modification count, so the doc appears unmodified
   286     // do this before we notify listeners
   287     ResetModificationCount();
   289     // update the UI with our state
   290     NotifyDocumentListeners(eDocumentCreated);
   291     NotifyDocumentListeners(eDocumentStateChanged);
   293     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   294     if (obs) {
   295       obs->AddObserver(this,
   296                        SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
   297                        false);
   298     }
   299   }
   301   // update nsTextStateManager and caret if we have focus
   302   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
   303   if (focusedContent) {
   304     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(focusedContent);
   305     if (target) {
   306       InitializeSelection(target);
   307     }
   309     // If the text control gets reframed during focus, Focus() would not be
   310     // called, so take a chance here to see if we need to spell check the text
   311     // control.
   312     nsEditorEventListener* listener =
   313       reinterpret_cast<nsEditorEventListener*> (mEventListener.get());
   314     listener->SpellCheckIfNeeded();
   316     IMEState newState;
   317     rv = GetPreferredIMEState(&newState);
   318     NS_ENSURE_SUCCESS(rv, NS_OK);
   319     nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
   320     IMEStateManager::UpdateIMEState(newState, content);
   321   }
   322   return NS_OK;
   323 }
   325 /* virtual */
   326 void
   327 nsEditor::CreateEventListeners()
   328 {
   329   // Don't create the handler twice
   330   if (!mEventListener) {
   331     mEventListener = new nsEditorEventListener();
   332   }
   333 }
   335 nsresult
   336 nsEditor::InstallEventListeners()
   337 {
   338   NS_ENSURE_TRUE(mDocWeak && mEventListener,
   339                  NS_ERROR_NOT_INITIALIZED);
   341   // Initialize the event target.
   342   nsCOMPtr<nsIContent> rootContent = GetRoot();
   343   NS_ENSURE_TRUE(rootContent, NS_ERROR_NOT_AVAILABLE);
   344   mEventTarget = do_QueryInterface(rootContent->GetParent());
   345   NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_AVAILABLE);
   347   nsEditorEventListener* listener =
   348     reinterpret_cast<nsEditorEventListener*>(mEventListener.get());
   349   return listener->Connect(this);
   350 }
   352 void
   353 nsEditor::RemoveEventListeners()
   354 {
   355   if (!mDocWeak || !mEventListener) {
   356     return;
   357   }
   358   reinterpret_cast<nsEditorEventListener*>(mEventListener.get())->Disconnect();
   359   if (mComposition) {
   360     mComposition->EndHandlingComposition(this);
   361     mComposition = nullptr;
   362   }
   363   mEventTarget = nullptr;
   364 }
   366 bool
   367 nsEditor::GetDesiredSpellCheckState()
   368 {
   369   // Check user override on this element
   370   if (mSpellcheckCheckboxState != eTriUnset) {
   371     return (mSpellcheckCheckboxState == eTriTrue);
   372   }
   374   // Check user preferences
   375   int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
   377   if (spellcheckLevel == 0) {
   378     return false;                    // Spellchecking forced off globally
   379   }
   381   if (!CanEnableSpellCheck()) {
   382     return false;
   383   }
   385   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   386   if (presShell) {
   387     nsPresContext* context = presShell->GetPresContext();
   388     if (context && !context->IsDynamic()) {
   389       return false;
   390     }
   391   }
   393   // Check DOM state
   394   nsCOMPtr<nsIContent> content = GetExposedRoot();
   395   if (!content) {
   396     return false;
   397   }
   399   nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(content);
   400   if (!element) {
   401     return false;
   402   }
   404   if (!IsPlaintextEditor()) {
   405     // Some of the page content might be editable and some not, if spellcheck=
   406     // is explicitly set anywhere, so if there's anything editable on the page,
   407     // return true and let the spellchecker figure it out.
   408     nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetCurrentDoc());
   409     return doc && doc->IsEditingOn();
   410   }
   412   bool enable;
   413   element->GetSpellcheck(&enable);
   415   return enable;
   416 }
   418 NS_IMETHODIMP
   419 nsEditor::PreDestroy(bool aDestroyingFrames)
   420 {
   421   if (mDidPreDestroy)
   422     return NS_OK;
   424   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   425   if (obs) {
   426     obs->RemoveObserver(this,
   427                         SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION);
   428   }
   430   // Let spellchecker clean up its observers etc. It is important not to
   431   // actually free the spellchecker here, since the spellchecker could have
   432   // caused flush notifications, which could have gotten here if a textbox
   433   // is being removed. Setting the spellchecker to nullptr could free the
   434   // object that is still in use! It will be freed when the editor is
   435   // destroyed.
   436   if (mInlineSpellChecker)
   437     mInlineSpellChecker->Cleanup(aDestroyingFrames);
   439   // tell our listeners that the doc is going away
   440   NotifyDocumentListeners(eDocumentToBeDestroyed);
   442   // Unregister event listeners
   443   RemoveEventListeners();
   444   mActionListeners.Clear();
   445   mEditorObservers.Clear();
   446   mDocStateListeners.Clear();
   447   mInlineSpellChecker = nullptr;
   448   mSpellcheckCheckboxState = eTriUnset;
   449   mRootElement = nullptr;
   451   mDidPreDestroy = true;
   452   return NS_OK;
   453 }
   455 NS_IMETHODIMP
   456 nsEditor::GetFlags(uint32_t *aFlags)
   457 {
   458   *aFlags = mFlags;
   459   return NS_OK;
   460 }
   462 NS_IMETHODIMP
   463 nsEditor::SetFlags(uint32_t aFlags)
   464 {
   465   if (mFlags == aFlags) {
   466     return NS_OK;
   467   }
   469   bool spellcheckerWasEnabled = CanEnableSpellCheck();
   470   mFlags = aFlags;
   472   if (!mDocWeak) {
   473     // If we're initializing, we shouldn't do anything now.
   474     // SetFlags() will be called by PostCreate(),
   475     // we should synchronize some stuff for the flags at that time.
   476     return NS_OK;
   477   }
   479   // The flag change may cause the spellchecker state change
   480   if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
   481     nsresult rv = SyncRealTimeSpell();
   482     NS_ENSURE_SUCCESS(rv, rv);
   483   }
   485   // If this is called from PostCreate(), it will update the IME state if it's
   486   // necessary.
   487   if (!mDidPostCreate) {
   488     return NS_OK;
   489   }
   491   // Might be changing editable state, so, we need to reset current IME state
   492   // if we're focused and the flag change causes IME state change.
   493   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
   494   if (focusedContent) {
   495     IMEState newState;
   496     nsresult rv = GetPreferredIMEState(&newState);
   497     if (NS_SUCCEEDED(rv)) {
   498       // NOTE: When the enabled state isn't going to be modified, this method
   499       // is going to do nothing.
   500       nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
   501       IMEStateManager::UpdateIMEState(newState, content);
   502     }
   503   }
   505   return NS_OK;
   506 }
   508 NS_IMETHODIMP
   509 nsEditor::GetIsSelectionEditable(bool *aIsSelectionEditable)
   510 {
   511   NS_ENSURE_ARG_POINTER(aIsSelectionEditable);
   513   // get current selection
   514   nsCOMPtr<nsISelection> selection;
   515   nsresult res = GetSelection(getter_AddRefs(selection));
   516   NS_ENSURE_SUCCESS(res, res);
   517   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   519   // XXX we just check that the anchor node is editable at the moment
   520   //     we should check that all nodes in the selection are editable
   521   nsCOMPtr<nsIDOMNode> anchorNode;
   522   selection->GetAnchorNode(getter_AddRefs(anchorNode));
   523   *aIsSelectionEditable = anchorNode && IsEditable(anchorNode);
   525   return NS_OK;
   526 }
   528 NS_IMETHODIMP
   529 nsEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
   530 {
   531   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
   532   nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
   533   *aIsDocumentEditable = !!doc;
   535   return NS_OK;
   536 }
   538 already_AddRefed<nsIDocument>
   539 nsEditor::GetDocument()
   540 {
   541   NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
   542   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
   543   return doc.forget();
   544 }
   546 already_AddRefed<nsIDOMDocument>
   547 nsEditor::GetDOMDocument()
   548 {
   549   NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
   550   nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
   551   return doc.forget();
   552 }
   554 NS_IMETHODIMP 
   555 nsEditor::GetDocument(nsIDOMDocument **aDoc)
   556 {
   557   nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
   558   doc.forget(aDoc);
   559   return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED;
   560 }
   562 already_AddRefed<nsIPresShell>
   563 nsEditor::GetPresShell()
   564 {
   565   NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
   566   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
   567   NS_ENSURE_TRUE(doc, nullptr);
   568   nsCOMPtr<nsIPresShell> ps = doc->GetShell();
   569   return ps.forget();
   570 }
   572 already_AddRefed<nsIWidget>
   573 nsEditor::GetWidget()
   574 {
   575   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   576   NS_ENSURE_TRUE(ps, nullptr);
   577   nsPresContext* pc = ps->GetPresContext();
   578   NS_ENSURE_TRUE(pc, nullptr);
   579   nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
   580   NS_ENSURE_TRUE(widget.get(), nullptr);
   581   return widget.forget();
   582 }
   584 /* attribute string contentsMIMEType; */
   585 NS_IMETHODIMP
   586 nsEditor::GetContentsMIMEType(char * *aContentsMIMEType)
   587 {
   588   NS_ENSURE_ARG_POINTER(aContentsMIMEType);
   589   *aContentsMIMEType = ToNewCString(mContentMIMEType);
   590   return NS_OK;
   591 }
   593 NS_IMETHODIMP
   594 nsEditor::SetContentsMIMEType(const char * aContentsMIMEType)
   595 {
   596   mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : "");
   597   return NS_OK;
   598 }
   600 NS_IMETHODIMP
   601 nsEditor::GetSelectionController(nsISelectionController **aSel)
   602 {
   603   NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
   604   *aSel = nullptr; // init out param
   605   nsCOMPtr<nsISelectionController> selCon;
   606   if (mSelConWeak) {
   607     selCon = do_QueryReferent(mSelConWeak);
   608   } else {
   609     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   610     selCon = do_QueryInterface(presShell);
   611   }
   612   NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
   613   NS_ADDREF(*aSel = selCon);
   614   return NS_OK;
   615 }
   618 NS_IMETHODIMP
   619 nsEditor::DeleteSelection(EDirection aAction, EStripWrappers aStripWrappers)
   620 {
   621   MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   622   return DeleteSelectionImpl(aAction, aStripWrappers);
   623 }
   627 NS_IMETHODIMP
   628 nsEditor::GetSelection(nsISelection **aSelection)
   629 {
   630   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
   631   *aSelection = nullptr;
   632   nsCOMPtr<nsISelectionController> selcon;
   633   GetSelectionController(getter_AddRefs(selcon));
   634   NS_ENSURE_TRUE(selcon, NS_ERROR_NOT_INITIALIZED);
   635   return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection);  // does an addref
   636 }
   638 Selection*
   639 nsEditor::GetSelection()
   640 {
   641   nsCOMPtr<nsISelection> sel;
   642   nsresult res = GetSelection(getter_AddRefs(sel));
   643   NS_ENSURE_SUCCESS(res, nullptr);
   645   return static_cast<Selection*>(sel.get());
   646 }
   648 NS_IMETHODIMP
   649 nsEditor::DoTransaction(nsITransaction* aTxn)
   650 {
   651   if (mPlaceHolderBatch && !mPlaceHolderTxn) {
   652     nsCOMPtr<nsIAbsorbingTransaction> plcTxn = new PlaceholderTxn();
   654     // save off weak reference to placeholder txn
   655     mPlaceHolderTxn = do_GetWeakReference(plcTxn);
   656     plcTxn->Init(mPlaceHolderName, mSelState, this);
   657     // placeholder txn took ownership of this pointer
   658     mSelState = nullptr;
   660     // QI to an nsITransaction since that's what DoTransaction() expects
   661     nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
   662     // we will recurse, but will not hit this case in the nested call
   663     DoTransaction(theTxn);
   665     if (mTxnMgr) {
   666       nsCOMPtr<nsITransaction> topTxn = mTxnMgr->PeekUndoStack();
   667       if (topTxn) {
   668         plcTxn = do_QueryInterface(topTxn);
   669         if (plcTxn) {
   670           // there is a placeholder transaction on top of the undo stack.  It
   671           // is either the one we just created, or an earlier one that we are
   672           // now merging into.  From here on out remember this placeholder
   673           // instead of the one we just created.
   674           mPlaceHolderTxn = do_GetWeakReference(plcTxn);
   675         }
   676       }
   677     }
   678   }
   680   if (aTxn) {
   681     // XXX: Why are we doing selection specific batching stuff here?
   682     // XXX: Most entry points into the editor have auto variables that
   683     // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
   684     // XXX: these selection batch calls no-ops.
   685     // XXX:
   686     // XXX: I suspect that this was placed here to avoid multiple
   687     // XXX: selection changed notifications from happening until after
   688     // XXX: the transaction was done. I suppose that can still happen
   689     // XXX: if an embedding application called DoTransaction() directly
   690     // XXX: to pump its own transactions through the system, but in that
   691     // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
   692     // XXX: its auto equivalent nsAutoUpdateViewBatch to ensure that
   693     // XXX: selection listeners have access to accurate frame data?
   694     // XXX:
   695     // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
   696     // XXX: we will need to make sure that they are disabled during
   697     // XXX: the init of the editor for text widgets to avoid layout
   698     // XXX: re-entry during initial reflow. - kin
   700     // get the selection and start a batch change
   701     nsRefPtr<Selection> selection = GetSelection();
   702     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   704     selection->StartBatchChanges();
   706     nsresult res;
   707     if (mTxnMgr) {
   708       res = mTxnMgr->DoTransaction(aTxn);
   709     } else {
   710       res = aTxn->DoTransaction();
   711     }
   712     if (NS_SUCCEEDED(res)) {
   713       DoAfterDoTransaction(aTxn);
   714     }
   716     // no need to check res here, don't lose result of operation
   717     selection->EndBatchChanges();
   719     NS_ENSURE_SUCCESS(res, res);
   720   }
   722   return NS_OK;
   723 }
   726 NS_IMETHODIMP
   727 nsEditor::EnableUndo(bool aEnable)
   728 {
   729   if (aEnable) {
   730     if (!mTxnMgr) {
   731       mTxnMgr = new nsTransactionManager();
   732     }
   733     mTxnMgr->SetMaxTransactionCount(-1);
   734   } else if (mTxnMgr) {
   735     // disable the transaction manager if it is enabled
   736     mTxnMgr->Clear();
   737     mTxnMgr->SetMaxTransactionCount(0);
   738   }
   740   return NS_OK;
   741 }
   743 NS_IMETHODIMP
   744 nsEditor::GetNumberOfUndoItems(int32_t* aNumItems)
   745 {
   746   *aNumItems = 0;
   747   return mTxnMgr ? mTxnMgr->GetNumberOfUndoItems(aNumItems) : NS_OK;
   748 }
   750 NS_IMETHODIMP
   751 nsEditor::GetNumberOfRedoItems(int32_t* aNumItems)
   752 {
   753   *aNumItems = 0;
   754   return mTxnMgr ? mTxnMgr->GetNumberOfRedoItems(aNumItems) : NS_OK;
   755 }
   757 NS_IMETHODIMP
   758 nsEditor::GetTransactionManager(nsITransactionManager* *aTxnManager)
   759 {
   760   NS_ENSURE_ARG_POINTER(aTxnManager);
   762   *aTxnManager = nullptr;
   763   NS_ENSURE_TRUE(mTxnMgr, NS_ERROR_FAILURE);
   765   NS_ADDREF(*aTxnManager = mTxnMgr);
   766   return NS_OK;
   767 }
   769 NS_IMETHODIMP
   770 nsEditor::SetTransactionManager(nsITransactionManager *aTxnManager)
   771 {
   772   NS_ENSURE_TRUE(aTxnManager, NS_ERROR_FAILURE);
   774   // nsITransactionManager is builtinclass, so this is safe
   775   mTxnMgr = static_cast<nsTransactionManager*>(aTxnManager);
   776   return NS_OK;
   777 }
   779 NS_IMETHODIMP 
   780 nsEditor::Undo(uint32_t aCount)
   781 {
   782   ForceCompositionEnd();
   784   bool hasTxnMgr, hasTransaction = false;
   785   CanUndo(&hasTxnMgr, &hasTransaction);
   786   NS_ENSURE_TRUE(hasTransaction, NS_OK);
   788   nsAutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
   790   if (!mTxnMgr) {
   791     return NS_OK;
   792   }
   794   for (uint32_t i = 0; i < aCount; ++i) {
   795     nsresult rv = mTxnMgr->UndoTransaction();
   796     NS_ENSURE_SUCCESS(rv, rv);
   798     DoAfterUndoTransaction();
   799   }
   801   return NS_OK;
   802 }
   805 NS_IMETHODIMP nsEditor::CanUndo(bool *aIsEnabled, bool *aCanUndo)
   806 {
   807   NS_ENSURE_TRUE(aIsEnabled && aCanUndo, NS_ERROR_NULL_POINTER);
   808   *aIsEnabled = !!mTxnMgr;
   809   if (*aIsEnabled) {
   810     int32_t numTxns = 0;
   811     mTxnMgr->GetNumberOfUndoItems(&numTxns);
   812     *aCanUndo = !!numTxns;
   813   } else {
   814     *aCanUndo = false;
   815   }
   816   return NS_OK;
   817 }
   820 NS_IMETHODIMP 
   821 nsEditor::Redo(uint32_t aCount)
   822 {
   823   bool hasTxnMgr, hasTransaction = false;
   824   CanRedo(&hasTxnMgr, &hasTransaction);
   825   NS_ENSURE_TRUE(hasTransaction, NS_OK);
   827   nsAutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
   829   if (!mTxnMgr) {
   830     return NS_OK;
   831   }
   833   for (uint32_t i = 0; i < aCount; ++i) {
   834     nsresult rv = mTxnMgr->RedoTransaction();
   835     NS_ENSURE_SUCCESS(rv, rv);
   837     DoAfterRedoTransaction();
   838   }
   840   return NS_OK;
   841 }
   844 NS_IMETHODIMP nsEditor::CanRedo(bool *aIsEnabled, bool *aCanRedo)
   845 {
   846   NS_ENSURE_TRUE(aIsEnabled && aCanRedo, NS_ERROR_NULL_POINTER);
   848   *aIsEnabled = !!mTxnMgr;
   849   if (*aIsEnabled) {
   850     int32_t numTxns = 0;
   851     mTxnMgr->GetNumberOfRedoItems(&numTxns);
   852     *aCanRedo = !!numTxns;
   853   } else {
   854     *aCanRedo = false;
   855   }
   856   return NS_OK;
   857 }
   860 NS_IMETHODIMP 
   861 nsEditor::BeginTransaction()
   862 {
   863   BeginUpdateViewBatch();
   865   if (mTxnMgr) {
   866     mTxnMgr->BeginBatch(nullptr);
   867   }
   869   return NS_OK;
   870 }
   872 NS_IMETHODIMP 
   873 nsEditor::EndTransaction()
   874 {
   875   if (mTxnMgr) {
   876     mTxnMgr->EndBatch(false);
   877   }
   879   EndUpdateViewBatch();
   881   return NS_OK;
   882 }
   885 // These two routines are similar to the above, but do not use
   886 // the transaction managers batching feature.  Instead we use
   887 // a placeholder transaction to wrap up any further transaction
   888 // while the batch is open.  The advantage of this is that
   889 // placeholder transactions can later merge, if needed.  Merging
   890 // is unavailable between transaction manager batches.
   892 NS_IMETHODIMP 
   893 nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
   894 {
   895   NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
   896   if (!mPlaceHolderBatch)
   897   {
   898     // time to turn on the batch
   899     BeginUpdateViewBatch();
   900     mPlaceHolderTxn = nullptr;
   901     mPlaceHolderName = aName;
   902     nsRefPtr<Selection> selection = GetSelection();
   903     if (selection) {
   904       mSelState = new nsSelectionState();
   905       mSelState->SaveSelection(selection);
   906     }
   907   }
   908   mPlaceHolderBatch++;
   910   return NS_OK;
   911 }
   913 NS_IMETHODIMP 
   914 nsEditor::EndPlaceHolderTransaction()
   915 {
   916   NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
   917   if (mPlaceHolderBatch == 1)
   918   {
   919     nsCOMPtr<nsISelection>selection;
   920     GetSelection(getter_AddRefs(selection));
   922     nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
   924     // By making the assumption that no reflow happens during the calls
   925     // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
   926     // allow the selection to cache a frame offset which is used by the
   927     // caret drawing code. We only enable this cache here; at other times,
   928     // we have no way to know whether reflow invalidates it
   929     // See bugs 35296 and 199412.
   930     if (selPrivate) {
   931       selPrivate->SetCanCacheFrameOffset(true);
   932     }
   934     {
   935       // Hide the caret here to avoid hiding it twice, once in EndUpdateViewBatch
   936       // and once in ScrollSelectionIntoView.
   937       nsRefPtr<nsCaret> caret;
   938       nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   940       if (presShell)
   941         caret = presShell->GetCaret();
   943       // time to turn off the batch
   944       EndUpdateViewBatch();
   945       // make sure selection is in view
   947       // After ScrollSelectionIntoView(), the pending notifications might be
   948       // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
   949       ScrollSelectionIntoView(false);
   950     }
   952     // cached for frame offset are Not available now
   953     if (selPrivate) {
   954       selPrivate->SetCanCacheFrameOffset(false);
   955     }
   957     if (mSelState)
   958     {
   959       // we saved the selection state, but never got to hand it to placeholder 
   960       // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
   961       delete mSelState;
   962       mSelState = nullptr;
   963     }
   964     if (mPlaceHolderTxn)  // we might have never made a placeholder if no action took place
   965     {
   966       nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
   967       if (plcTxn) 
   968       {
   969         plcTxn->EndPlaceHolderBatch();
   970       }
   971       else  
   972       {
   973         // in the future we will check to make sure undo is off here,
   974         // since that is the only known case where the placeholdertxn would disappear on us.
   975         // For now just removing the assert.
   976       }
   977       // notify editor observers of action but if composing, it's done by
   978       // text event handler.
   979       if (!mComposition) {
   980         NotifyEditorObservers();
   981       }
   982     }
   983   }
   984   mPlaceHolderBatch--;
   986   return NS_OK;
   987 }
   989 NS_IMETHODIMP
   990 nsEditor::ShouldTxnSetSelection(bool *aResult)
   991 {
   992   NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
   993   *aResult = mShouldTxnSetSelection;
   994   return NS_OK;
   995 }
   997 NS_IMETHODIMP  
   998 nsEditor::SetShouldTxnSetSelection(bool aShould)
   999 {
  1000   mShouldTxnSetSelection = aShould;
  1001   return NS_OK;
  1004 NS_IMETHODIMP
  1005 nsEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty)
  1007   *aDocumentIsEmpty = true;
  1009   dom::Element* root = GetRoot();
  1010   NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER); 
  1012   *aDocumentIsEmpty = !root->HasChildren();
  1013   return NS_OK;
  1017 // XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
  1018 NS_IMETHODIMP nsEditor::SelectAll()
  1020   if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
  1021   ForceCompositionEnd();
  1023   nsCOMPtr<nsISelectionController> selCon;
  1024   GetSelectionController(getter_AddRefs(selCon));
  1025   NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
  1026   nsCOMPtr<nsISelection> selection;
  1027   nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1028   if (NS_SUCCEEDED(result) && selection)
  1030     result = SelectEntireDocument(selection);
  1032   return result;
  1035 NS_IMETHODIMP nsEditor::BeginningOfDocument()
  1037   if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
  1039   // get the selection
  1040   nsCOMPtr<nsISelection> selection;
  1041   nsresult result = GetSelection(getter_AddRefs(selection));
  1042   NS_ENSURE_SUCCESS(result, result);
  1043   NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
  1045   // get the root element 
  1046   dom::Element* rootElement = GetRoot();
  1047   NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER); 
  1049   // find first editable thingy
  1050   nsCOMPtr<nsINode> firstNode = GetFirstEditableNode(rootElement);
  1051   if (!firstNode) {
  1052     // just the root node, set selection to inside the root
  1053     return selection->CollapseNative(rootElement, 0);
  1056   if (firstNode->NodeType() == nsIDOMNode::TEXT_NODE) {
  1057     // If firstNode is text, set selection to beginning of the text node.
  1058     return selection->CollapseNative(firstNode, 0);
  1061   // Otherwise, it's a leaf node and we set the selection just in front of it.
  1062   nsCOMPtr<nsIContent> parent = firstNode->GetParent();
  1063   if (!parent) {
  1064     return NS_ERROR_NULL_POINTER;
  1067   int32_t offsetInParent = parent->IndexOf(firstNode);
  1068   return selection->CollapseNative(parent, offsetInParent);
  1071 NS_IMETHODIMP
  1072 nsEditor::EndOfDocument()
  1074   NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
  1076   // get selection 
  1077   nsCOMPtr<nsISelection> selection; 
  1078   nsresult rv = GetSelection(getter_AddRefs(selection)); 
  1079   NS_ENSURE_SUCCESS(rv, rv); 
  1080   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 
  1082   // get the root element 
  1083   nsINode* node = GetRoot();
  1084   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 
  1085   nsINode* child = node->GetLastChild();
  1087   while (child && IsContainer(child->AsDOMNode())) {
  1088     node = child;
  1089     child = node->GetLastChild();
  1092   uint32_t length = node->Length();
  1093   return selection->CollapseNative(node, int32_t(length));
  1096 NS_IMETHODIMP
  1097 nsEditor::GetDocumentModified(bool *outDocModified)
  1099   NS_ENSURE_TRUE(outDocModified, NS_ERROR_NULL_POINTER);
  1101   int32_t  modCount = 0;
  1102   GetModificationCount(&modCount);
  1104   *outDocModified = (modCount != 0);
  1105   return NS_OK;
  1108 NS_IMETHODIMP
  1109 nsEditor::GetDocumentCharacterSet(nsACString &characterSet)
  1111   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
  1112   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
  1114   characterSet = doc->GetDocumentCharacterSet();
  1115   return NS_OK;
  1118 NS_IMETHODIMP
  1119 nsEditor::SetDocumentCharacterSet(const nsACString& characterSet)
  1121   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
  1122   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
  1124   doc->SetDocumentCharacterSet(characterSet);
  1125   return NS_OK;
  1128 NS_IMETHODIMP
  1129 nsEditor::Cut()
  1131   return NS_ERROR_NOT_IMPLEMENTED; 
  1134 NS_IMETHODIMP
  1135 nsEditor::CanCut(bool *aCanCut)
  1137   return NS_ERROR_NOT_IMPLEMENTED; 
  1140 NS_IMETHODIMP
  1141 nsEditor::Copy()
  1143   return NS_ERROR_NOT_IMPLEMENTED; 
  1146 NS_IMETHODIMP
  1147 nsEditor::CanCopy(bool *aCanCut)
  1149   return NS_ERROR_NOT_IMPLEMENTED; 
  1152 NS_IMETHODIMP
  1153 nsEditor::Paste(int32_t aSelectionType)
  1155   return NS_ERROR_NOT_IMPLEMENTED; 
  1158 NS_IMETHODIMP
  1159 nsEditor::PasteTransferable(nsITransferable *aTransferable)
  1161   return NS_ERROR_NOT_IMPLEMENTED; 
  1164 NS_IMETHODIMP
  1165 nsEditor::CanPaste(int32_t aSelectionType, bool *aCanPaste)
  1167   return NS_ERROR_NOT_IMPLEMENTED; 
  1170 NS_IMETHODIMP
  1171 nsEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste)
  1173   return NS_ERROR_NOT_IMPLEMENTED; 
  1176 NS_IMETHODIMP 
  1177 nsEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & aAttribute, const nsAString & aValue)
  1179   nsRefPtr<ChangeAttributeTxn> txn;
  1180   nsresult result = CreateTxnForSetAttribute(aElement, aAttribute, aValue,
  1181                                              getter_AddRefs(txn));
  1182   if (NS_SUCCEEDED(result))  {
  1183     result = DoTransaction(txn);  
  1185   return result;
  1188 NS_IMETHODIMP 
  1189 nsEditor::GetAttributeValue(nsIDOMElement *aElement, 
  1190                             const nsAString & aAttribute, 
  1191                             nsAString & aResultValue, 
  1192                             bool *aResultIsSet)
  1194   NS_ENSURE_TRUE(aResultIsSet, NS_ERROR_NULL_POINTER);
  1195   *aResultIsSet = false;
  1196   if (!aElement) {
  1197     return NS_OK;
  1199   nsAutoString value;
  1200   nsresult rv = aElement->GetAttribute(aAttribute, value);
  1201   NS_ENSURE_SUCCESS(rv, rv);
  1202   if (!DOMStringIsNull(value)) {
  1203     *aResultIsSet = true;
  1204     aResultValue = value;
  1206   return rv;
  1209 NS_IMETHODIMP 
  1210 nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString& aAttribute)
  1212   nsRefPtr<ChangeAttributeTxn> txn;
  1213   nsresult result = CreateTxnForRemoveAttribute(aElement, aAttribute,
  1214                                                 getter_AddRefs(txn));
  1215   if (NS_SUCCEEDED(result))  {
  1216     result = DoTransaction(txn);  
  1218   return result;
  1222 bool
  1223 nsEditor::OutputsMozDirty()
  1225   // Return true for Composer (!eEditorAllowInteraction) or mail
  1226   // (eEditorMailMask), but false for webpages.
  1227   return !(mFlags & nsIPlaintextEditor::eEditorAllowInteraction) ||
  1228           (mFlags & nsIPlaintextEditor::eEditorMailMask);
  1232 NS_IMETHODIMP
  1233 nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
  1235   // Mark the node dirty, but not for webpages (bug 599983)
  1236   if (!OutputsMozDirty()) {
  1237     return NS_OK;
  1239   nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
  1240   if (element) {
  1241     element->SetAttr(kNameSpaceID_None, nsEditProperty::mozdirty,
  1242                      EmptyString(), false);
  1244   return NS_OK;
  1247 NS_IMETHODIMP nsEditor::GetInlineSpellChecker(bool autoCreate,
  1248                                               nsIInlineSpellChecker ** aInlineSpellChecker)
  1250   NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
  1252   if (mDidPreDestroy) {
  1253     // Don't allow people to get or create the spell checker once the editor
  1254     // is going away.
  1255     *aInlineSpellChecker = nullptr;
  1256     return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
  1259   // We don't want to show the spell checking UI if there are no spell check dictionaries available.
  1260   bool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking();
  1261   if (!canSpell) {
  1262     *aInlineSpellChecker = nullptr;
  1263     return NS_ERROR_FAILURE;
  1266   nsresult rv;
  1267   if (!mInlineSpellChecker && autoCreate) {
  1268     mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
  1269     NS_ENSURE_SUCCESS(rv, rv);
  1272   if (mInlineSpellChecker) {
  1273     rv = mInlineSpellChecker->Init(this);
  1274     if (NS_FAILED(rv))
  1275       mInlineSpellChecker = nullptr;
  1276     NS_ENSURE_SUCCESS(rv, rv);
  1279   NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
  1281   return NS_OK;
  1284 NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic,
  1285                                 const char16_t *aData)
  1287   NS_ASSERTION(!strcmp(aTopic,
  1288                        SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION),
  1289                "Unexpected observer topic");
  1291   // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes
  1292   SyncRealTimeSpell();
  1294   // When nsIEditorSpellCheck::GetCurrentDictionary changes
  1295   if (mInlineSpellChecker) {
  1296     // if the current dictionary is no longer available, find another one
  1297     nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck;
  1298     mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck));
  1299     if (editorSpellCheck) {
  1300       // Note: This might change the current dictionary, which may call
  1301       // this observer recursively.
  1302       editorSpellCheck->CheckCurrentDictionary();
  1305     // update the inline spell checker to reflect the new current dictionary
  1306     mInlineSpellChecker->SpellCheckRange(nullptr); // causes recheck
  1309   return NS_OK;
  1312 NS_IMETHODIMP nsEditor::SyncRealTimeSpell()
  1314   bool enable = GetDesiredSpellCheckState();
  1316   // Initializes mInlineSpellChecker
  1317   nsCOMPtr<nsIInlineSpellChecker> spellChecker;
  1318   GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
  1320   if (mInlineSpellChecker) {
  1321     // We might have a mInlineSpellChecker even if there are no dictionaries
  1322     // available since we don't destroy the mInlineSpellChecker when the last
  1323     // dictionariy is removed, but in that case spellChecker is null
  1324     mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
  1327   return NS_OK;
  1330 NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(bool enable)
  1332   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
  1334   return SyncRealTimeSpell();
  1337 NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag,
  1338                                    nsIDOMNode *    aParent,
  1339                                    int32_t         aPosition,
  1340                                    nsIDOMNode **   aNewNode)
  1342   int32_t i;
  1344   nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
  1346   for (i = 0; i < mActionListeners.Count(); i++)
  1347     mActionListeners[i]->WillCreateNode(aTag, aParent, aPosition);
  1349   nsRefPtr<CreateElementTxn> txn;
  1350   nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition,
  1351                                               getter_AddRefs(txn));
  1352   if (NS_SUCCEEDED(result)) 
  1354     result = DoTransaction(txn);  
  1355     if (NS_SUCCEEDED(result)) 
  1357       result = txn->GetNewNode(aNewNode);
  1358       NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::DoTransaction succeeded.");
  1362   mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
  1364   for (i = 0; i < mActionListeners.Count(); i++)
  1365     mActionListeners[i]->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result);
  1367   return result;
  1371 NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
  1372                                    nsIDOMNode * aParent,
  1373                                    int32_t      aPosition)
  1375   int32_t i;
  1376   nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
  1378   for (i = 0; i < mActionListeners.Count(); i++)
  1379     mActionListeners[i]->WillInsertNode(aNode, aParent, aPosition);
  1381   nsRefPtr<InsertElementTxn> txn;
  1382   nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition,
  1383                                               getter_AddRefs(txn));
  1384   if (NS_SUCCEEDED(result))  {
  1385     result = DoTransaction(txn);  
  1388   mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
  1390   for (i = 0; i < mActionListeners.Count(); i++)
  1391     mActionListeners[i]->DidInsertNode(aNode, aParent, aPosition, result);
  1393   return result;
  1397 NS_IMETHODIMP 
  1398 nsEditor::SplitNode(nsIDOMNode * aNode,
  1399                     int32_t      aOffset,
  1400                     nsIDOMNode **aNewLeftNode)
  1402   int32_t i;
  1403   nsAutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext);
  1405   for (i = 0; i < mActionListeners.Count(); i++)
  1406     mActionListeners[i]->WillSplitNode(aNode, aOffset);
  1408   nsRefPtr<SplitElementTxn> txn;
  1409   nsresult result = CreateTxnForSplitNode(aNode, aOffset, getter_AddRefs(txn));
  1410   if (NS_SUCCEEDED(result))  
  1412     result = DoTransaction(txn);
  1413     if (NS_SUCCEEDED(result))
  1415       result = txn->GetNewNode(aNewLeftNode);
  1416       NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
  1420   mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
  1422   for (i = 0; i < mActionListeners.Count(); i++)
  1424     nsIDOMNode *ptr = *aNewLeftNode;
  1425     mActionListeners[i]->DidSplitNode(aNode, aOffset, ptr, result);
  1428   return result;
  1432 nsresult
  1433 nsEditor::JoinNodes(nsINode* aNodeToKeep, nsIContent* aNodeToMove)
  1435   // We don't really need aNodeToMove's parent to be non-null -- we could just
  1436   // skip adjusting any ranges in aNodeToMove's parent if there is none.  But
  1437   // the current implementation requires it.
  1438   MOZ_ASSERT(aNodeToKeep && aNodeToMove && aNodeToMove->GetParentNode());
  1439   nsresult res = JoinNodes(aNodeToKeep->AsDOMNode(), aNodeToMove->AsDOMNode(),
  1440                            aNodeToMove->GetParentNode()->AsDOMNode());
  1441   NS_ASSERTION(NS_SUCCEEDED(res), "JoinNodes failed");
  1442   NS_ENSURE_SUCCESS(res, res);
  1443   return NS_OK;
  1446 NS_IMETHODIMP
  1447 nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
  1448                     nsIDOMNode * aRightNode,
  1449                     nsIDOMNode * aParent)
  1451   int32_t i;
  1452   nsAutoRules beginRulesSniffing(this, EditAction::joinNode, nsIEditor::ePrevious);
  1454   // remember some values; later used for saved selection updating.
  1455   // find the offset between the nodes to be joined.
  1456   int32_t offset = GetChildOffset(aRightNode, aParent);
  1457   // find the number of children of the lefthand node
  1458   uint32_t oldLeftNodeLen;
  1459   nsresult result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen);
  1460   NS_ENSURE_SUCCESS(result, result);
  1462   for (i = 0; i < mActionListeners.Count(); i++)
  1463     mActionListeners[i]->WillJoinNodes(aLeftNode, aRightNode, aParent);
  1465   nsRefPtr<JoinElementTxn> txn;
  1466   result = CreateTxnForJoinNode(aLeftNode, aRightNode, getter_AddRefs(txn));
  1467   if (NS_SUCCEEDED(result))  {
  1468     result = DoTransaction(txn);  
  1471   mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (int32_t)oldLeftNodeLen);
  1473   for (i = 0; i < mActionListeners.Count(); i++)
  1474     mActionListeners[i]->DidJoinNodes(aLeftNode, aRightNode, aParent, result);
  1476   return result;
  1480 NS_IMETHODIMP
  1481 nsEditor::DeleteNode(nsIDOMNode* aNode)
  1483   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1484   NS_ENSURE_STATE(node);
  1485   return DeleteNode(node);
  1488 nsresult
  1489 nsEditor::DeleteNode(nsINode* aNode)
  1491   nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::ePrevious);
  1493   // save node location for selection updating code.
  1494   for (int32_t i = 0; i < mActionListeners.Count(); i++) {
  1495     mActionListeners[i]->WillDeleteNode(aNode->AsDOMNode());
  1498   nsRefPtr<DeleteNodeTxn> txn;
  1499   nsresult res = CreateTxnForDeleteNode(aNode, getter_AddRefs(txn));
  1500   if (NS_SUCCEEDED(res))  {
  1501     res = DoTransaction(txn);
  1504   for (int32_t i = 0; i < mActionListeners.Count(); i++) {
  1505     mActionListeners[i]->DidDeleteNode(aNode->AsDOMNode(), res);
  1508   NS_ENSURE_SUCCESS(res, res);
  1509   return NS_OK;
  1512 ///////////////////////////////////////////////////////////////////////////
  1513 // ReplaceContainer: replace inNode with a new node (outNode) which is contructed 
  1514 //                   to be of type aNodeType.  Put inNodes children into outNode.
  1515 //                   Callers responsibility to make sure inNode's children can 
  1516 //                   go in outNode.
  1517 nsresult
  1518 nsEditor::ReplaceContainer(nsIDOMNode *inNode, 
  1519                            nsCOMPtr<nsIDOMNode> *outNode, 
  1520                            const nsAString &aNodeType,
  1521                            const nsAString *aAttribute,
  1522                            const nsAString *aValue,
  1523                            bool aCloneAttributes)
  1525   NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
  1527   nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
  1528   NS_ENSURE_STATE(node);
  1530   nsCOMPtr<dom::Element> element;
  1531   nsresult rv = ReplaceContainer(node, getter_AddRefs(element), aNodeType,
  1532                                  aAttribute, aValue, aCloneAttributes);
  1533   *outNode = element ? element->AsDOMNode() : nullptr;
  1534   return rv;
  1537 nsresult
  1538 nsEditor::ReplaceContainer(nsINode* aNode,
  1539                            dom::Element** outNode,
  1540                            const nsAString& aNodeType,
  1541                            const nsAString* aAttribute,
  1542                            const nsAString* aValue,
  1543                            bool aCloneAttributes)
  1545   MOZ_ASSERT(aNode);
  1546   MOZ_ASSERT(outNode);
  1548   *outNode = nullptr;
  1550   nsCOMPtr<nsIContent> parent = aNode->GetParent();
  1551   NS_ENSURE_STATE(parent);
  1553   int32_t offset = parent->IndexOf(aNode);
  1555   // create new container
  1556   //new call to use instead to get proper HTML element, bug# 39919
  1557   nsresult res = CreateHTMLContent(aNodeType, outNode);
  1558   NS_ENSURE_SUCCESS(res, res);
  1560   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(*outNode);
  1562   nsIDOMNode* inNode = aNode->AsDOMNode();
  1564   // set attribute if needed
  1565   if (aAttribute && aValue && !aAttribute->IsEmpty()) {
  1566     res = elem->SetAttribute(*aAttribute, *aValue);
  1567     NS_ENSURE_SUCCESS(res, res);
  1569   if (aCloneAttributes) {
  1570     res = CloneAttributes(elem, inNode);
  1571     NS_ENSURE_SUCCESS(res, res);
  1574   // notify our internal selection state listener
  1575   // (Note: A nsAutoSelectionReset object must be created
  1576   //  before calling this to initialize mRangeUpdater)
  1577   nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, elem);
  1579     nsAutoTxnsConserveSelection conserveSelection(this);
  1580     while (aNode->HasChildren()) {
  1581       nsCOMPtr<nsIDOMNode> child = aNode->GetFirstChild()->AsDOMNode();
  1583       res = DeleteNode(child);
  1584       NS_ENSURE_SUCCESS(res, res);
  1586       res = InsertNode(child, elem, -1);
  1587       NS_ENSURE_SUCCESS(res, res);
  1591   // insert new container into tree
  1592   res = InsertNode(elem, parent->AsDOMNode(), offset);
  1593   NS_ENSURE_SUCCESS(res, res);
  1595   // delete old container
  1596   return DeleteNode(inNode);
  1599 ///////////////////////////////////////////////////////////////////////////
  1600 // RemoveContainer: remove inNode, reparenting its children into their
  1601 //                  the parent of inNode
  1602 //
  1603 nsresult
  1604 nsEditor::RemoveContainer(nsIDOMNode* aNode)
  1606   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1607   return RemoveContainer(node);
  1610 nsresult
  1611 nsEditor::RemoveContainer(nsINode* aNode)
  1613   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  1615   nsCOMPtr<nsINode> parent = aNode->GetParentNode();
  1616   NS_ENSURE_STATE(parent);
  1618   int32_t offset = parent->IndexOf(aNode);
  1620   // loop through the child nodes of inNode and promote them
  1621   // into inNode's parent.
  1622   uint32_t nodeOrigLen = aNode->GetChildCount();
  1624   // notify our internal selection state listener
  1625   nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent, offset, nodeOrigLen);
  1627   while (aNode->HasChildren()) {
  1628     nsCOMPtr<nsIContent> child = aNode->GetLastChild();
  1629     nsresult rv = DeleteNode(child->AsDOMNode());
  1630     NS_ENSURE_SUCCESS(rv, rv);
  1632     rv = InsertNode(child->AsDOMNode(), parent->AsDOMNode(), offset);
  1633     NS_ENSURE_SUCCESS(rv, rv);
  1636   return DeleteNode(aNode->AsDOMNode());
  1640 ///////////////////////////////////////////////////////////////////////////
  1641 // InsertContainerAbove:  insert a new parent for inNode, returned in outNode,
  1642 //                   which is contructed to be of type aNodeType.  outNode becomes
  1643 //                   a child of inNode's earlier parent.
  1644 //                   Callers responsibility to make sure inNode's can be child
  1645 //                   of outNode, and outNode can be child of old parent.
  1646 nsresult
  1647 nsEditor::InsertContainerAbove( nsIDOMNode *inNode, 
  1648                                 nsCOMPtr<nsIDOMNode> *outNode, 
  1649                                 const nsAString &aNodeType,
  1650                                 const nsAString *aAttribute,
  1651                                 const nsAString *aValue)
  1653   NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
  1655   nsCOMPtr<nsIContent> node = do_QueryInterface(inNode);
  1656   NS_ENSURE_STATE(node);
  1658   nsCOMPtr<dom::Element> element;
  1659   nsresult rv = InsertContainerAbove(node, getter_AddRefs(element), aNodeType,
  1660                                      aAttribute, aValue);
  1661   *outNode = element ? element->AsDOMNode() : nullptr;
  1662   return rv;
  1665 nsresult
  1666 nsEditor::InsertContainerAbove(nsIContent* aNode,
  1667                                dom::Element** aOutNode,
  1668                                const nsAString& aNodeType,
  1669                                const nsAString* aAttribute,
  1670                                const nsAString* aValue)
  1672   MOZ_ASSERT(aNode);
  1674   nsCOMPtr<nsIContent> parent = aNode->GetParent();
  1675   NS_ENSURE_STATE(parent);
  1676   int32_t offset = parent->IndexOf(aNode);
  1678   // create new container
  1679   nsCOMPtr<dom::Element> newContent;
  1681   //new call to use instead to get proper HTML element, bug# 39919
  1682   nsresult res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent));
  1683   NS_ENSURE_SUCCESS(res, res);
  1685   // set attribute if needed
  1686   if (aAttribute && aValue && !aAttribute->IsEmpty()) {
  1687     nsIDOMNode* elem = newContent->AsDOMNode();
  1688     res = static_cast<nsIDOMElement*>(elem)->SetAttribute(*aAttribute, *aValue);
  1689     NS_ENSURE_SUCCESS(res, res);
  1692   // notify our internal selection state listener
  1693   nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
  1695   // put inNode in new parent, outNode
  1696   res = DeleteNode(aNode->AsDOMNode());
  1697   NS_ENSURE_SUCCESS(res, res);
  1700     nsAutoTxnsConserveSelection conserveSelection(this);
  1701     res = InsertNode(aNode->AsDOMNode(), newContent->AsDOMNode(), 0);
  1702     NS_ENSURE_SUCCESS(res, res);
  1705   // put new parent in doc
  1706   res = InsertNode(newContent->AsDOMNode(), parent->AsDOMNode(), offset);
  1707   newContent.forget(aOutNode);
  1708   return res;  
  1711 ///////////////////////////////////////////////////////////////////////////
  1712 // MoveNode:  move aNode to {aParent,aOffset}
  1713 nsresult
  1714 nsEditor::MoveNode(nsIDOMNode* aNode, nsIDOMNode* aParent, int32_t aOffset)
  1716   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1717   NS_ENSURE_STATE(node);
  1719   nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
  1720   NS_ENSURE_STATE(parent);
  1722   return MoveNode(node, parent, aOffset);
  1725 nsresult
  1726 nsEditor::MoveNode(nsINode* aNode, nsINode* aParent, int32_t aOffset)
  1728   MOZ_ASSERT(aNode);
  1729   MOZ_ASSERT(aParent);
  1730   MOZ_ASSERT(aOffset == -1 ||
  1731              (0 <= aOffset && SafeCast<uint32_t>(aOffset) <= aParent->Length()));
  1733   int32_t oldOffset;
  1734   nsCOMPtr<nsINode> oldParent = GetNodeLocation(aNode, &oldOffset);
  1736   if (aOffset == -1) {
  1737     // Magic value meaning "move to end of aParent".
  1738     aOffset = SafeCast<int32_t>(aParent->Length());
  1741   // Don't do anything if it's already in right place.
  1742   if (aParent == oldParent && aOffset == oldOffset) {
  1743     return NS_OK;
  1746   // Notify our internal selection state listener.
  1747   nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset,
  1748                                     aParent, aOffset);
  1750   // Need to adjust aOffset if we are moving aNode further along in its current
  1751   // parent.
  1752   if (aParent == oldParent && oldOffset < aOffset) {
  1753     // This is because when we delete aNode, it will make the offsets after it
  1754     // off by one.
  1755     aOffset--;
  1758   // Hold a reference so aNode doesn't go away when we remove it (bug 772282).
  1759   nsCOMPtr<nsINode> kungFuDeathGrip = aNode;
  1761   nsresult rv = DeleteNode(aNode);
  1762   NS_ENSURE_SUCCESS(rv, rv);
  1764   return InsertNode(aNode->AsDOMNode(), aParent->AsDOMNode(), aOffset);
  1768 NS_IMETHODIMP
  1769 nsEditor::AddEditorObserver(nsIEditorObserver *aObserver)
  1771   // we don't keep ownership of the observers.  They must
  1772   // remove themselves as observers before they are destroyed.
  1774   NS_ENSURE_TRUE(aObserver, NS_ERROR_NULL_POINTER);
  1776   // Make sure the listener isn't already on the list
  1777   if (mEditorObservers.IndexOf(aObserver) == -1) 
  1779     if (!mEditorObservers.AppendObject(aObserver))
  1780       return NS_ERROR_FAILURE;
  1783   return NS_OK;
  1787 NS_IMETHODIMP
  1788 nsEditor::RemoveEditorObserver(nsIEditorObserver *aObserver)
  1790   NS_ENSURE_TRUE(aObserver, NS_ERROR_FAILURE);
  1792   if (!mEditorObservers.RemoveObject(aObserver))
  1793     return NS_ERROR_FAILURE;
  1795   return NS_OK;
  1798 class EditorInputEventDispatcher : public nsRunnable
  1800 public:
  1801   EditorInputEventDispatcher(nsEditor* aEditor,
  1802                              nsIContent* aTarget,
  1803                              bool aIsComposing)
  1804     : mEditor(aEditor)
  1805     , mTarget(aTarget)
  1806     , mIsComposing(aIsComposing)
  1810   NS_IMETHOD Run()
  1812     // Note that we don't need to check mDispatchInputEvent here.  We need
  1813     // to check it only when the editor requests to dispatch the input event.
  1815     if (!mTarget->IsInDoc()) {
  1816       return NS_OK;
  1819     nsCOMPtr<nsIPresShell> ps = mEditor->GetPresShell();
  1820     if (!ps) {
  1821       return NS_OK;
  1824     nsCOMPtr<nsIWidget> widget = mEditor->GetWidget();
  1825     if (!widget) {
  1826       return NS_OK;
  1829     // Even if the change is caused by untrusted event, we need to dispatch
  1830     // trusted input event since it's a fact.
  1831     InternalEditorInputEvent inputEvent(true, NS_EDITOR_INPUT, widget);
  1832     inputEvent.time = static_cast<uint64_t>(PR_Now() / 1000);
  1833     inputEvent.mIsComposing = mIsComposing;
  1834     nsEventStatus status = nsEventStatus_eIgnore;
  1835     nsresult rv =
  1836       ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status);
  1837     NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error
  1838     return NS_OK;
  1841 private:
  1842   nsRefPtr<nsEditor> mEditor;
  1843   nsCOMPtr<nsIContent> mTarget;
  1844   bool mIsComposing;
  1845 };
  1847 void nsEditor::NotifyEditorObservers(void)
  1849   for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
  1850     mEditorObservers[i]->EditAction();
  1853   if (!mDispatchInputEvent) {
  1854     return;
  1857   FireInputEvent();
  1860 void
  1861 nsEditor::FireInputEvent()
  1863   // We don't need to dispatch multiple input events if there is a pending
  1864   // input event.  However, it may have different event target.  If we resolved
  1865   // this issue, we need to manage the pending events in an array.  But it's
  1866   // overwork.  We don't need to do it for the very rare case.
  1868   nsCOMPtr<nsIContent> target = GetInputEventTargetContent();
  1869   NS_ENSURE_TRUE_VOID(target);
  1871   // NOTE: Don't refer IsIMEComposing() because it returns false even before
  1872   //       compositionend.  However, DOM Level 3 Events defines it should be
  1873   //       true after compositionstart and before compositionend.
  1874   nsContentUtils::AddScriptRunner(
  1875     new EditorInputEventDispatcher(this, target, !!GetComposition()));
  1878 NS_IMETHODIMP
  1879 nsEditor::AddEditActionListener(nsIEditActionListener *aListener)
  1881   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
  1883   // Make sure the listener isn't already on the list
  1884   if (mActionListeners.IndexOf(aListener) == -1) 
  1886     if (!mActionListeners.AppendObject(aListener))
  1887       return NS_ERROR_FAILURE;
  1890   return NS_OK;
  1894 NS_IMETHODIMP
  1895 nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener)
  1897   NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
  1899   if (!mActionListeners.RemoveObject(aListener))
  1900     return NS_ERROR_FAILURE;
  1902   return NS_OK;
  1906 NS_IMETHODIMP
  1907 nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener)
  1909   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
  1911   if (mDocStateListeners.IndexOf(aListener) == -1)
  1913     if (!mDocStateListeners.AppendObject(aListener))
  1914       return NS_ERROR_FAILURE;
  1917   return NS_OK;
  1921 NS_IMETHODIMP
  1922 nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener)
  1924   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
  1926   if (!mDocStateListeners.RemoveObject(aListener))
  1927     return NS_ERROR_FAILURE;
  1929   return NS_OK;
  1933 NS_IMETHODIMP nsEditor::OutputToString(const nsAString& aFormatType,
  1934                                        uint32_t aFlags,
  1935                                        nsAString& aOutputString)
  1937   // these should be implemented by derived classes.
  1938   return NS_ERROR_NOT_IMPLEMENTED;
  1941 NS_IMETHODIMP
  1942 nsEditor::OutputToStream(nsIOutputStream* aOutputStream,
  1943                          const nsAString& aFormatType,
  1944                          const nsACString& aCharsetOverride,
  1945                          uint32_t aFlags)
  1947   // these should be implemented by derived classes.
  1948   return NS_ERROR_NOT_IMPLEMENTED;
  1951 NS_IMETHODIMP
  1952 nsEditor::DumpContentTree()
  1954 #ifdef DEBUG
  1955   if (mRootElement) {
  1956     mRootElement->List(stdout);
  1958 #endif
  1959   return NS_OK;
  1963 NS_IMETHODIMP
  1964 nsEditor::DebugDumpContent()
  1966 #ifdef DEBUG
  1967   nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak);
  1968   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
  1970   nsCOMPtr<nsIDOMHTMLElement>bodyElem;
  1971   doc->GetBody(getter_AddRefs(bodyElem));
  1972   nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElem);
  1973   if (content)
  1974     content->List();
  1975 #endif
  1976   return NS_OK;
  1980 NS_IMETHODIMP
  1981 nsEditor::DebugUnitTests(int32_t *outNumTests, int32_t *outNumTestsFailed)
  1983 #ifdef DEBUG
  1984   NS_NOTREACHED("This should never get called. Overridden by subclasses");
  1985 #endif
  1986   return NS_OK;
  1990 bool     
  1991 nsEditor::ArePreservingSelection()
  1993   return !(mSavedSel.IsEmpty());
  1996 void
  1997 nsEditor::PreserveSelectionAcrossActions(Selection* aSel)
  1999   mSavedSel.SaveSelection(aSel);
  2000   mRangeUpdater.RegisterSelectionState(mSavedSel);
  2003 nsresult 
  2004 nsEditor::RestorePreservedSelection(nsISelection *aSel)
  2006   if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE;
  2007   mSavedSel.RestoreSelection(aSel);
  2008   StopPreservingSelection();
  2009   return NS_OK;
  2012 void     
  2013 nsEditor::StopPreservingSelection()
  2015   mRangeUpdater.DropSelectionState(mSavedSel);
  2016   mSavedSel.MakeEmpty();
  2019 void
  2020 nsEditor::EnsureComposition(mozilla::WidgetGUIEvent* aEvent)
  2022   if (mComposition) {
  2023     return;
  2025   // The compositionstart event must cause creating new TextComposition
  2026   // instance at being dispatched by IMEStateManager.
  2027   mComposition = IMEStateManager::GetTextCompositionFor(aEvent);
  2028   if (!mComposition) {
  2029     MOZ_CRASH("IMEStateManager doesn't return proper composition");
  2031   mComposition->StartHandlingComposition(this);
  2034 nsresult
  2035 nsEditor::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent)
  2037   MOZ_ASSERT(!mComposition, "There is composition already");
  2038   EnsureComposition(aCompositionEvent);
  2039   if (mPhonetic) {
  2040     mPhonetic->Truncate(0);
  2042   return NS_OK;
  2045 void
  2046 nsEditor::EndIMEComposition()
  2048   NS_ENSURE_TRUE_VOID(mComposition); // nothing to do
  2050   // commit the IME transaction..we can get at it via the transaction mgr.
  2051   // Note that this means IME won't work without an undo stack!
  2052   if (mTxnMgr) {
  2053     nsCOMPtr<nsITransaction> txn = mTxnMgr->PeekUndoStack();
  2054     nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
  2055     if (plcTxn) {
  2056       DebugOnly<nsresult> rv = plcTxn->Commit();
  2057       NS_ASSERTION(NS_SUCCEEDED(rv),
  2058                    "nsIAbsorbingTransaction::Commit() failed");
  2062   /* reset the data we need to construct a transaction */
  2063   mIMETextNode = nullptr;
  2064   mIMETextOffset = 0;
  2065   mComposition->EndHandlingComposition(this);
  2066   mComposition = nullptr;
  2068   // notify editor observers of action
  2069   NotifyEditorObservers();
  2073 NS_IMETHODIMP
  2074 nsEditor::GetPhonetic(nsAString& aPhonetic)
  2076   if (mPhonetic)
  2077     aPhonetic = *mPhonetic;
  2078   else
  2079     aPhonetic.Truncate(0);
  2081   return NS_OK;
  2084 NS_IMETHODIMP
  2085 nsEditor::ForceCompositionEnd()
  2087   nsCOMPtr<nsIPresShell> ps = GetPresShell();
  2088   if (!ps) {
  2089     return NS_ERROR_NOT_AVAILABLE;
  2091   nsPresContext* pc = ps->GetPresContext();
  2092   if (!pc) {
  2093     return NS_ERROR_NOT_AVAILABLE;
  2096   if (!mComposition) {
  2097     // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
  2098     // "commit the composition" and "cursor is moved".  This method name is
  2099     // "ForceCompositionEnd", so, ResetInputState() should be used only for the
  2100     // former here.  However, ResetInputState() is also used for the latter here
  2101     // because even if we don't have composition, we call ResetInputState() on
  2102     // Linux.  Currently, nsGtkIMModule can know the timing of the cursor move,
  2103     // so, the latter meaning should be gone.
  2104     // XXX This may commit a composition in another editor.
  2105     return IMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED, pc);
  2108   return IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc);
  2111 NS_IMETHODIMP
  2112 nsEditor::GetPreferredIMEState(IMEState *aState)
  2114   NS_ENSURE_ARG_POINTER(aState);
  2115   aState->mEnabled = IMEState::ENABLED;
  2116   aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
  2118   if (IsReadonly() || IsDisabled()) {
  2119     aState->mEnabled = IMEState::DISABLED;
  2120     return NS_OK;
  2123   nsCOMPtr<nsIContent> content = GetRoot();
  2124   NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
  2126   nsIFrame* frame = content->GetPrimaryFrame();
  2127   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
  2129   switch (frame->StyleUIReset()->mIMEMode) {
  2130     case NS_STYLE_IME_MODE_AUTO:
  2131       if (IsPasswordEditor())
  2132         aState->mEnabled = IMEState::PASSWORD;
  2133       break;
  2134     case NS_STYLE_IME_MODE_DISABLED:
  2135       // we should use password state for |ime-mode: disabled;|.
  2136       aState->mEnabled = IMEState::PASSWORD;
  2137       break;
  2138     case NS_STYLE_IME_MODE_ACTIVE:
  2139       aState->mOpen = IMEState::OPEN;
  2140       break;
  2141     case NS_STYLE_IME_MODE_INACTIVE:
  2142       aState->mOpen = IMEState::CLOSED;
  2143       break;
  2146   return NS_OK;
  2149 NS_IMETHODIMP
  2150 nsEditor::GetComposing(bool* aResult)
  2152   NS_ENSURE_ARG_POINTER(aResult);
  2153   *aResult = IsIMEComposing();
  2154   return NS_OK;
  2158 /* Non-interface, public methods */
  2160 NS_IMETHODIMP
  2161 nsEditor::GetRootElement(nsIDOMElement **aRootElement)
  2163   NS_ENSURE_ARG_POINTER(aRootElement);
  2164   NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE);
  2165   nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mRootElement);
  2166   rootElement.forget(aRootElement);
  2167   return NS_OK;
  2171 /** All editor operations which alter the doc should be prefaced
  2172  *  with a call to StartOperation, naming the action and direction */
  2173 NS_IMETHODIMP
  2174 nsEditor::StartOperation(EditAction opID, nsIEditor::EDirection aDirection)
  2176   mAction = opID;
  2177   mDirection = aDirection;
  2178   return NS_OK;
  2182 /** All editor operations which alter the doc should be followed
  2183  *  with a call to EndOperation */
  2184 NS_IMETHODIMP
  2185 nsEditor::EndOperation()
  2187   mAction = EditAction::none;
  2188   mDirection = eNone;
  2189   return NS_OK;
  2192 NS_IMETHODIMP
  2193 nsEditor::CloneAttribute(const nsAString & aAttribute,
  2194                          nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
  2196   NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER);
  2198   nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
  2199   nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
  2200   NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE);
  2202   nsAutoString attrValue;
  2203   bool isAttrSet;
  2204   nsresult rv = GetAttributeValue(sourceElement,
  2205                                   aAttribute,
  2206                                   attrValue,
  2207                                   &isAttrSet);
  2208   NS_ENSURE_SUCCESS(rv, rv);
  2209   if (isAttrSet)
  2210     rv = SetAttribute(destElement, aAttribute, attrValue);
  2211   else
  2212     rv = RemoveAttribute(destElement, aAttribute);
  2214   return rv;
  2217 // Objects must be DOM elements
  2218 NS_IMETHODIMP
  2219 nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
  2221   NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER);
  2223   nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
  2224   nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
  2225   NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE);
  2227   nsCOMPtr<nsIDOMMozNamedAttrMap> sourceAttributes;
  2228   sourceElement->GetAttributes(getter_AddRefs(sourceAttributes));
  2229   nsCOMPtr<nsIDOMMozNamedAttrMap> destAttributes;
  2230   destElement->GetAttributes(getter_AddRefs(destAttributes));
  2231   NS_ENSURE_TRUE(sourceAttributes && destAttributes, NS_ERROR_FAILURE);
  2233   nsAutoEditBatch beginBatching(this);
  2235   // Use transaction system for undo only if destination
  2236   //   is already in the document
  2237   nsCOMPtr<nsIDOMNode> p = aDestNode;
  2238   nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
  2239   NS_ENSURE_TRUE(rootNode, NS_ERROR_NULL_POINTER);
  2240   bool destInBody = true;
  2241   while (p && p != rootNode)
  2243     nsCOMPtr<nsIDOMNode> tmp;
  2244     if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp)
  2246       destInBody = false;
  2247       break;
  2249     p = tmp;
  2252   uint32_t sourceCount;
  2253   sourceAttributes->GetLength(&sourceCount);
  2254   uint32_t destCount;
  2255   destAttributes->GetLength(&destCount);
  2256   nsCOMPtr<nsIDOMAttr> attr;
  2258   // Clear existing attributes
  2259   for (uint32_t i = 0; i < destCount; i++) {
  2260     // always remove item number 0 (first item in list)
  2261     if (NS_SUCCEEDED(destAttributes->Item(0, getter_AddRefs(attr))) && attr) {
  2262       nsString str;
  2263       if (NS_SUCCEEDED(attr->GetName(str))) {
  2264         if (destInBody) {
  2265           RemoveAttribute(destElement, str);
  2266         } else {
  2267           destElement->RemoveAttribute(str);
  2273   nsresult result = NS_OK;
  2275   // Set just the attributes that the source element has
  2276   for (uint32_t i = 0; i < sourceCount; i++)
  2278     if (NS_SUCCEEDED(sourceAttributes->Item(i, getter_AddRefs(attr))) && attr) {
  2279       nsString sourceAttrName;
  2280       if (NS_SUCCEEDED(attr->GetName(sourceAttrName))) {
  2281         nsString sourceAttrValue;
  2282         /* Presence of an attribute in the named node map indicates that it was
  2283          * set on the element even if it has no value.
  2284          */
  2285         if (NS_SUCCEEDED(attr->GetValue(sourceAttrValue))) {
  2286           if (destInBody) {
  2287             result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, false);
  2288           } else {
  2289             // the element is not inserted in the document yet, we don't want to put a
  2290             // transaction on the UndoStack
  2291             result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, true);
  2293         } else {
  2294           // Do we ever get here?
  2295 #if DEBUG_cmanske
  2296           printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n");
  2297 #endif
  2302   return result;
  2306 NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor)
  2308   nsCOMPtr<nsISelectionController> selCon;
  2309   if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
  2311     int16_t region = nsISelectionController::SELECTION_FOCUS_REGION;
  2313     if (aScrollToAnchor)
  2314       region = nsISelectionController::SELECTION_ANCHOR_REGION;
  2316     selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
  2317       region, nsISelectionController::SCROLL_OVERFLOW_HIDDEN);
  2320   return NS_OK;
  2323 NS_IMETHODIMP
  2324 nsEditor::InsertTextImpl(const nsAString& aStringToInsert,
  2325                          nsCOMPtr<nsIDOMNode>* aInOutNode,
  2326                          int32_t* aInOutOffset,
  2327                          nsIDOMDocument* aDoc)
  2329   // NOTE: caller *must* have already used nsAutoTxnsConserveSelection
  2330   // stack-based class to turn off txn selection updating.  Caller also turned
  2331   // on rules sniffing if desired.
  2333   NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc,
  2334                  NS_ERROR_NULL_POINTER);
  2335   if (!mComposition && aStringToInsert.IsEmpty()) {
  2336     return NS_OK;
  2339   nsCOMPtr<nsINode> node = do_QueryInterface(*aInOutNode);
  2340   NS_ENSURE_STATE(node);
  2341   uint32_t offset = static_cast<uint32_t>(*aInOutOffset);
  2343   if (!node->IsNodeOfType(nsINode::eTEXT) && IsPlaintextEditor()) {
  2344     nsCOMPtr<nsINode> root = GetRoot();
  2345     // In some cases, node is the anonymous DIV, and offset is 0.  To avoid
  2346     // injecting unneeded text nodes, we first look to see if we have one
  2347     // available.  In that case, we'll just adjust node and offset accordingly.
  2348     if (node == root && offset == 0 && node->HasChildren() &&
  2349         node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) {
  2350       node = node->GetFirstChild();
  2352     // In some other cases, node is the anonymous DIV, and offset points to the
  2353     // terminating mozBR.  In that case, we'll adjust aInOutNode and
  2354     // aInOutOffset to the preceding text node, if any.
  2355     if (node == root && offset > 0 && node->GetChildAt(offset - 1) &&
  2356         node->GetChildAt(offset - 1)->IsNodeOfType(nsINode::eTEXT)) {
  2357       node = node->GetChildAt(offset - 1);
  2358       offset = node->Length();
  2360     // Sometimes, node is the mozBR element itself.  In that case, we'll adjust
  2361     // the insertion point to the previous text node, if one exists, or to the
  2362     // parent anonymous DIV.
  2363     if (nsTextEditUtils::IsMozBR(node) && offset == 0) {
  2364       if (node->GetPreviousSibling() &&
  2365           node->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) {
  2366         node = node->GetPreviousSibling();
  2367         offset = node->Length();
  2368       } else if (node->GetParentNode() && node->GetParentNode() == root) {
  2369         node = node->GetParentNode();
  2374   nsresult res;
  2375   if (mComposition) {
  2376     if (!node->IsNodeOfType(nsINode::eTEXT)) {
  2377       // create a text node
  2378       nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
  2379       NS_ENSURE_STATE(doc);
  2380       nsRefPtr<nsTextNode> newNode = doc->CreateTextNode(EmptyString());
  2381       // then we insert it into the dom tree
  2382       res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset);
  2383       NS_ENSURE_SUCCESS(res, res);
  2384       node = newNode;
  2385       offset = 0;
  2387     nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(node);
  2388     NS_ENSURE_STATE(charDataNode);
  2389     res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset);
  2390     NS_ENSURE_SUCCESS(res, res);
  2391     offset += aStringToInsert.Length();
  2392   } else {
  2393     if (node->IsNodeOfType(nsINode::eTEXT)) {
  2394       // we are inserting text into an existing text node.
  2395       nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(node);
  2396       NS_ENSURE_STATE(charDataNode);
  2397       res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset);
  2398       NS_ENSURE_SUCCESS(res, res);
  2399       offset += aStringToInsert.Length();
  2400     } else {
  2401       // we are inserting text into a non-text node.  first we have to create a
  2402       // textnode (this also populates it with the text)
  2403       nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
  2404       NS_ENSURE_STATE(doc);
  2405       nsRefPtr<nsTextNode> newNode = doc->CreateTextNode(aStringToInsert);
  2406       // then we insert it into the dom tree
  2407       res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset);
  2408       NS_ENSURE_SUCCESS(res, res);
  2409       node = newNode;
  2410       offset = aStringToInsert.Length();
  2414   *aInOutNode = node->AsDOMNode();
  2415   *aInOutOffset = static_cast<int32_t>(offset);
  2416   return NS_OK;
  2420 nsresult nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, 
  2421                                               nsIDOMCharacterData *aTextNode, 
  2422                                               int32_t aOffset,
  2423                                               bool aSuppressIME)
  2425   nsRefPtr<EditTxn> txn;
  2426   nsresult result = NS_OK;
  2427   bool isIMETransaction = false;
  2428   // aSuppressIME is used when editor must insert text, yet this text is not
  2429   // part of current ime operation.  example: adjusting whitespace around an ime insertion.
  2430   if (mComposition && !aSuppressIME) {
  2431     if (!mIMETextNode) {
  2432       mIMETextNode = aTextNode;
  2433       mIMETextOffset = aOffset;
  2435     // Modify mPhonetic with raw text input clauses.
  2436     const TextRangeArray* ranges = mComposition->GetRanges();
  2437     for (uint32_t i = 0; i < (ranges ? ranges->Length() : 0); ++i) {
  2438       const TextRange& textRange = ranges->ElementAt(i);
  2439       if (!textRange.Length() ||
  2440           textRange.mRangeType != NS_TEXTRANGE_RAWINPUT) {
  2441         continue;
  2443       if (!mPhonetic) {
  2444         mPhonetic = new nsString();
  2446       nsAutoString stringToInsert(aStringToInsert);
  2447       stringToInsert.Mid(*mPhonetic,
  2448                          textRange.mStartOffset, textRange.Length());
  2451     nsRefPtr<IMETextTxn> imeTxn;
  2452     result = CreateTxnForIMEText(aStringToInsert, getter_AddRefs(imeTxn));
  2453     txn = imeTxn;
  2454     isIMETransaction = true;
  2456   else
  2458     nsRefPtr<InsertTextTxn> insertTxn;
  2459     result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset,
  2460                                     getter_AddRefs(insertTxn));
  2461     txn = insertTxn;
  2463   NS_ENSURE_SUCCESS(result, result);
  2465   // let listeners know what's up
  2466   int32_t i;
  2467   for (i = 0; i < mActionListeners.Count(); i++)
  2468     mActionListeners[i]->WillInsertText(aTextNode, aOffset, aStringToInsert);
  2470   // XXX we may not need these view batches anymore.  This is handled at a higher level now I believe
  2471   BeginUpdateViewBatch();
  2472   result = DoTransaction(txn);
  2473   EndUpdateViewBatch();
  2475   mRangeUpdater.SelAdjInsertText(aTextNode, aOffset, aStringToInsert);
  2477   // let listeners know what happened
  2478   for (i = 0; i < mActionListeners.Count(); i++)
  2479     mActionListeners[i]->DidInsertText(aTextNode, aOffset, aStringToInsert, result);
  2481   // Added some cruft here for bug 43366.  Layout was crashing because we left an 
  2482   // empty text node lying around in the document.  So I delete empty text nodes
  2483   // caused by IME.  I have to mark the IME transaction as "fixed", which means
  2484   // that furure ime txns won't merge with it.  This is because we don't want
  2485   // future ime txns trying to put their text into a node that is no longer in
  2486   // the document.  This does not break undo/redo, because all these txns are 
  2487   // wrapped in a parent PlaceHolder txn, and placeholder txns are already 
  2488   // savvy to having multiple ime txns inside them.
  2490   // delete empty ime text node if there is one
  2491   if (isIMETransaction && mIMETextNode)
  2493     uint32_t len;
  2494     mIMETextNode->GetLength(&len);
  2495     if (!len)
  2497       DeleteNode(mIMETextNode);
  2498       mIMETextNode = nullptr;
  2499       static_cast<IMETextTxn*>(txn.get())->MarkFixed();  // mark the ime txn "fixed"
  2503   return result;
  2507 NS_IMETHODIMP nsEditor::SelectEntireDocument(nsISelection *aSelection)
  2509   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
  2511   nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
  2512   if (!rootElement) { return NS_ERROR_NOT_INITIALIZED; }
  2514   return aSelection->SelectAllChildren(rootElement);
  2518 nsINode*
  2519 nsEditor::GetFirstEditableNode(nsINode* aRoot)
  2521   MOZ_ASSERT(aRoot);
  2523   nsIContent* node = GetLeftmostChild(aRoot);
  2524   if (node && !IsEditable(node)) {
  2525     node = GetNextNode(node, /* aEditableNode = */ true);
  2528   return (node != aRoot) ? node : nullptr;
  2532 NS_IMETHODIMP
  2533 nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType)
  2535   int32_t numListeners = mDocStateListeners.Count();
  2536   if (!numListeners)    // maybe there just aren't any.
  2537     return NS_OK;
  2539   nsCOMArray<nsIDocumentStateListener> listeners(mDocStateListeners);
  2540   nsresult rv = NS_OK;
  2541   int32_t i;
  2543   switch (aNotificationType)
  2545     case eDocumentCreated:
  2546       for (i = 0; i < numListeners;i++)
  2548         rv = listeners[i]->NotifyDocumentCreated();
  2549         if (NS_FAILED(rv))
  2550           break;
  2552       break;
  2554     case eDocumentToBeDestroyed:
  2555       for (i = 0; i < numListeners;i++)
  2557         rv = listeners[i]->NotifyDocumentWillBeDestroyed();
  2558         if (NS_FAILED(rv))
  2559           break;
  2561       break;
  2563     case eDocumentStateChanged:
  2565         bool docIsDirty;
  2566         rv = GetDocumentModified(&docIsDirty);
  2567         NS_ENSURE_SUCCESS(rv, rv);
  2569         if (static_cast<int8_t>(docIsDirty) == mDocDirtyState)
  2570           return NS_OK;
  2572         mDocDirtyState = docIsDirty;
  2574         for (i = 0; i < numListeners;i++)
  2576           rv = listeners[i]->NotifyDocumentStateChanged(mDocDirtyState);
  2577           if (NS_FAILED(rv))
  2578             break;
  2581       break;
  2583     default:
  2584       NS_NOTREACHED("Unknown notification");
  2587   return rv;
  2591 NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsAString & aStringToInsert,
  2592                                                nsIDOMCharacterData *aTextNode,
  2593                                                int32_t aOffset,
  2594                                                InsertTextTxn ** aTxn)
  2596   NS_ENSURE_TRUE(aTextNode && aTxn, NS_ERROR_NULL_POINTER);
  2597   nsresult rv;
  2599   nsRefPtr<InsertTextTxn> txn = new InsertTextTxn();
  2600   rv = txn->Init(aTextNode, aOffset, aStringToInsert, this);
  2601   if (NS_SUCCEEDED(rv))
  2603     txn.forget(aTxn);
  2606   return rv;
  2610 NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
  2611                               uint32_t             aOffset,
  2612                               uint32_t             aLength)
  2614   nsRefPtr<DeleteTextTxn> txn;
  2615   nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength,
  2616                                            getter_AddRefs(txn));
  2617   nsAutoRules beginRulesSniffing(this, EditAction::deleteText, nsIEditor::ePrevious);
  2618   if (NS_SUCCEEDED(result))  
  2620     // let listeners know what's up
  2621     int32_t i;
  2622     for (i = 0; i < mActionListeners.Count(); i++)
  2623       mActionListeners[i]->WillDeleteText(aElement, aOffset, aLength);
  2625     result = DoTransaction(txn); 
  2627     // let listeners know what happened
  2628     for (i = 0; i < mActionListeners.Count(); i++)
  2629       mActionListeners[i]->DidDeleteText(aElement, aOffset, aLength, result);
  2631   return result;
  2635 nsresult
  2636 nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData* aElement,
  2637                                  uint32_t             aOffset,
  2638                                  uint32_t             aLength,
  2639                                  DeleteTextTxn**      aTxn)
  2641   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
  2643   nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
  2645   nsresult res = txn->Init(this, aElement, aOffset, aLength, &mRangeUpdater);
  2646   NS_ENSURE_SUCCESS(res, res);
  2648   txn.forget(aTxn);
  2649   return NS_OK;
  2655 NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode,
  2656                                          uint32_t    aOffset,
  2657                                          SplitElementTxn **aTxn)
  2659   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  2661   nsRefPtr<SplitElementTxn> txn = new SplitElementTxn();
  2663   nsresult rv = txn->Init(this, aNode, aOffset);
  2664   if (NS_SUCCEEDED(rv))
  2666     txn.forget(aTxn);
  2669   return rv;
  2672 NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode  *aLeftNode,
  2673                                              nsIDOMNode  *aRightNode,
  2674                                              JoinElementTxn **aTxn)
  2676   NS_ENSURE_TRUE(aLeftNode && aRightNode, NS_ERROR_NULL_POINTER);
  2678   nsRefPtr<JoinElementTxn> txn = new JoinElementTxn();
  2680   nsresult rv = txn->Init(this, aLeftNode, aRightNode);
  2681   if (NS_SUCCEEDED(rv))
  2683     txn.forget(aTxn);
  2686   return rv;
  2690 // END nsEditor core implementation
  2693 // BEGIN nsEditor public helper methods
  2695 nsresult
  2696 nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode,
  2697                         int32_t      aOffset,
  2698                         nsIDOMNode*  aNewLeftNode,
  2699                         nsIDOMNode*  aParent)
  2701   NS_ASSERTION(((nullptr!=aExistingRightNode) &&
  2702                 (nullptr!=aNewLeftNode) &&
  2703                 (nullptr!=aParent)),
  2704                 "null arg");
  2705   nsresult result;
  2706   if ((nullptr!=aExistingRightNode) &&
  2707       (nullptr!=aNewLeftNode) &&
  2708       (nullptr!=aParent))
  2710     // get selection
  2711     nsCOMPtr<nsISelection> selection;
  2712     result = GetSelection(getter_AddRefs(selection));
  2713     NS_ENSURE_SUCCESS(result, result);
  2714     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  2716     // remember some selection points
  2717     nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
  2718     int32_t selStartOffset, selEndOffset;
  2719     result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
  2720     if (NS_FAILED(result)) selStartNode = nullptr;  // if selection is cleared, remember that
  2721     result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
  2722     if (NS_FAILED(result)) selStartNode = nullptr;  // if selection is cleared, remember that
  2724     nsCOMPtr<nsIDOMNode> resultNode;
  2725     result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
  2726     //printf("  after insert\n"); content->List();  // DEBUG
  2727     if (NS_SUCCEEDED(result))
  2729       // split the children between the 2 nodes
  2730       // at this point, aExistingRightNode has all the children
  2731       // move all the children whose index is < aOffset to aNewLeftNode
  2732       if (0<=aOffset) // don't bother unless we're going to move at least one child
  2734         // if it's a text node, just shuffle around some text
  2735         nsCOMPtr<nsIDOMCharacterData> rightNodeAsText( do_QueryInterface(aExistingRightNode) );
  2736         nsCOMPtr<nsIDOMCharacterData> leftNodeAsText( do_QueryInterface(aNewLeftNode) );
  2737         if (leftNodeAsText && rightNodeAsText)
  2739           // fix right node
  2740           nsAutoString leftText;
  2741           rightNodeAsText->SubstringData(0, aOffset, leftText);
  2742           rightNodeAsText->DeleteData(0, aOffset);
  2743           // fix left node
  2744           leftNodeAsText->SetData(leftText);
  2745           // moose          
  2747         else
  2748         {  // otherwise it's an interior node, so shuffle around the children
  2749            // go through list backwards so deletes don't interfere with the iteration
  2750           nsCOMPtr<nsIDOMNodeList> childNodes;
  2751           result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
  2752           if ((NS_SUCCEEDED(result)) && (childNodes))
  2754             int32_t i=aOffset-1;
  2755             for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
  2757               nsCOMPtr<nsIDOMNode> childNode;
  2758               result = childNodes->Item(i, getter_AddRefs(childNode));
  2759               if ((NS_SUCCEEDED(result)) && (childNode))
  2761                 result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
  2762                 //printf("  after remove\n"); content->List();  // DEBUG
  2763                 if (NS_SUCCEEDED(result))
  2765                   nsCOMPtr<nsIDOMNode> firstChild;
  2766                   aNewLeftNode->GetFirstChild(getter_AddRefs(firstChild));
  2767                   result = aNewLeftNode->InsertBefore(childNode, firstChild, getter_AddRefs(resultNode));
  2768                   //printf("  after append\n"); content->List();  // DEBUG
  2774         // handle selection
  2775         nsCOMPtr<nsIPresShell> ps = GetPresShell();
  2776         if (ps)
  2777           ps->FlushPendingNotifications(Flush_Frames);
  2779         if (GetShouldTxnSetSelection())
  2781           // editor wants us to set selection at split point
  2782           selection->Collapse(aNewLeftNode, aOffset);
  2784         else if (selStartNode)   
  2786           // else adjust the selection if needed.  if selStartNode is null, then there was no selection.
  2787           // HACK: this is overly simplified - multi-range selections need more work than this
  2788           if (selStartNode.get() == aExistingRightNode)
  2790             if (selStartOffset < aOffset)
  2792               selStartNode = aNewLeftNode;
  2794             else
  2796               selStartOffset -= aOffset;
  2799           if (selEndNode.get() == aExistingRightNode)
  2801             if (selEndOffset < aOffset)
  2803               selEndNode = aNewLeftNode;
  2805             else
  2807               selEndOffset -= aOffset;
  2810           selection->Collapse(selStartNode,selStartOffset);
  2811           selection->Extend(selEndNode,selEndOffset);
  2816   else
  2817     result = NS_ERROR_INVALID_ARG;
  2819   return result;
  2822 nsresult
  2823 nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
  2824                         nsINode* aNodeToJoin,
  2825                         nsINode* aParent)
  2827   MOZ_ASSERT(aNodeToKeep);
  2828   MOZ_ASSERT(aNodeToJoin);
  2829   MOZ_ASSERT(aParent);
  2831   nsRefPtr<Selection> selection = GetSelection();
  2832   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  2834   // remember some selection points
  2835   nsCOMPtr<nsINode> selStartNode;
  2836   int32_t selStartOffset;
  2837   nsresult result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
  2838   if (NS_FAILED(result)) {
  2839     selStartNode = nullptr;
  2842   nsCOMPtr<nsINode> selEndNode;
  2843   int32_t selEndOffset;
  2844   result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
  2845   // Joe or Kin should comment here on why the following line is not a copy/paste error
  2846   if (NS_FAILED(result)) {
  2847     selStartNode = nullptr;
  2850   uint32_t firstNodeLength = aNodeToJoin->Length();
  2852   int32_t joinOffset;
  2853   GetNodeLocation(aNodeToJoin, &joinOffset);
  2854   int32_t keepOffset;
  2855   nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset);
  2857   // if selection endpoint is between the nodes, remember it as being
  2858   // in the one that is going away instead.  This simplifies later selection
  2859   // adjustment logic at end of this method.
  2860   if (selStartNode) {
  2861     if (selStartNode == parent &&
  2862         joinOffset < selStartOffset && selStartOffset <= keepOffset) {
  2863       selStartNode = aNodeToJoin;
  2864       selStartOffset = firstNodeLength;
  2866     if (selEndNode == parent &&
  2867         joinOffset < selEndOffset && selEndOffset <= keepOffset) {
  2868       selEndNode = aNodeToJoin;
  2869       selEndOffset = firstNodeLength;
  2873   // ok, ready to do join now.
  2874   // if it's a text node, just shuffle around some text
  2875   nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) );
  2876   nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) );
  2877   if (keepNodeAsText && joinNodeAsText) {
  2878     nsAutoString rightText;
  2879     nsAutoString leftText;
  2880     keepNodeAsText->GetData(rightText);
  2881     joinNodeAsText->GetData(leftText);
  2882     leftText += rightText;
  2883     keepNodeAsText->SetData(leftText);
  2884   } else {
  2885     // otherwise it's an interior node, so shuffle around the children
  2886     nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes();
  2887     MOZ_ASSERT(childNodes);
  2889     // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
  2890     // GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's ok.
  2891     nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild();
  2893     // have to go through the list backwards to keep deletes from interfering with iteration
  2894     for (uint32_t i = childNodes->Length(); i > 0; --i) {
  2895       nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1);
  2896       if (childNode) {
  2897         // prepend children of aNodeToJoin
  2898         ErrorResult err;
  2899         aNodeToKeep->InsertBefore(*childNode, firstNode, err);
  2900         NS_ENSURE_SUCCESS(err.ErrorCode(), err.ErrorCode());
  2901         firstNode = childNode.forget();
  2906   // delete the extra node
  2907   ErrorResult err;
  2908   aParent->RemoveChild(*aNodeToJoin, err);
  2910   if (GetShouldTxnSetSelection()) {
  2911     // editor wants us to set selection at join point
  2912     selection->Collapse(aNodeToKeep, SafeCast<int32_t>(firstNodeLength));
  2913   } else if (selStartNode) {
  2914     // and adjust the selection if needed
  2915     // HACK: this is overly simplified - multi-range selections need more work than this
  2916     bool bNeedToAdjust = false;
  2918     // check to see if we joined nodes where selection starts
  2919     if (selStartNode == aNodeToJoin) {
  2920       bNeedToAdjust = true;
  2921       selStartNode = aNodeToKeep;
  2922     } else if (selStartNode == aNodeToKeep) {
  2923       bNeedToAdjust = true;
  2924       selStartOffset += firstNodeLength;
  2927     // check to see if we joined nodes where selection ends
  2928     if (selEndNode == aNodeToJoin) {
  2929       bNeedToAdjust = true;
  2930       selEndNode = aNodeToKeep;
  2931     } else if (selEndNode == aNodeToKeep) {
  2932       bNeedToAdjust = true;
  2933       selEndOffset += firstNodeLength;
  2936     // adjust selection if needed
  2937     if (bNeedToAdjust) {
  2938       selection->Collapse(selStartNode, selStartOffset);
  2939       selection->Extend(selEndNode, selEndOffset);
  2943   return err.ErrorCode();
  2947 int32_t
  2948 nsEditor::GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent)
  2950   MOZ_ASSERT(aChild && aParent);
  2952   nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
  2953   nsCOMPtr<nsINode> child = do_QueryInterface(aChild);
  2954   MOZ_ASSERT(parent && child);
  2956   int32_t idx = parent->IndexOf(child);
  2957   MOZ_ASSERT(idx != -1);
  2958   return idx;
  2961 // static
  2962 already_AddRefed<nsIDOMNode>
  2963 nsEditor::GetNodeLocation(nsIDOMNode* aChild, int32_t* outOffset)
  2965   MOZ_ASSERT(aChild && outOffset);
  2966   NS_ENSURE_TRUE(aChild && outOffset, nullptr);
  2967   *outOffset = -1;
  2969   nsCOMPtr<nsIDOMNode> parent;
  2971   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
  2972     aChild->GetParentNode(getter_AddRefs(parent))));
  2973   if (parent) {
  2974     *outOffset = GetChildOffset(aChild, parent);
  2977   return parent.forget();
  2980 nsINode*
  2981 nsEditor::GetNodeLocation(nsINode* aChild, int32_t* aOffset)
  2983   MOZ_ASSERT(aChild);
  2984   MOZ_ASSERT(aOffset);
  2986   nsINode* parent = aChild->GetParentNode();
  2987   if (parent) {
  2988     *aOffset = parent->IndexOf(aChild);
  2989     MOZ_ASSERT(*aOffset != -1);
  2990   } else {
  2991     *aOffset = -1;
  2993   return parent;
  2996 // returns the number of things inside aNode.  
  2997 // If aNode is text, returns number of characters. If not, returns number of children nodes.
  2998 nsresult
  2999 nsEditor::GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount) 
  3001   aCount = 0;
  3002   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  3003   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
  3004   aCount = node->Length();
  3005   return NS_OK;
  3009 nsresult 
  3010 nsEditor::GetPriorNode(nsIDOMNode  *aParentNode, 
  3011                        int32_t      aOffset, 
  3012                        bool         aEditableNode, 
  3013                        nsCOMPtr<nsIDOMNode> *aResultNode,
  3014                        bool         bNoBlockCrossing)
  3016   NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
  3017   *aResultNode = nullptr;
  3019   nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
  3020   NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
  3022   *aResultNode = do_QueryInterface(GetPriorNode(parentNode, aOffset,
  3023                                                 aEditableNode,
  3024                                                 bNoBlockCrossing));
  3025   return NS_OK;
  3028 nsIContent*
  3029 nsEditor::GetPriorNode(nsINode* aParentNode,
  3030                        int32_t aOffset,
  3031                        bool aEditableNode,
  3032                        bool aNoBlockCrossing)
  3034   MOZ_ASSERT(aParentNode);
  3036   // If we are at the beginning of the node, or it is a text node, then just
  3037   // look before it.
  3038   if (!aOffset || aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) {
  3039     if (aNoBlockCrossing && IsBlockNode(aParentNode)) {
  3040       // If we aren't allowed to cross blocks, don't look before this block.
  3041       return nullptr;
  3043     return GetPriorNode(aParentNode, aEditableNode, aNoBlockCrossing);
  3046   // else look before the child at 'aOffset'
  3047   if (nsIContent* child = aParentNode->GetChildAt(aOffset)) {
  3048     return GetPriorNode(child, aEditableNode, aNoBlockCrossing);
  3051   // unless there isn't one, in which case we are at the end of the node
  3052   // and want the deep-right child.
  3053   nsIContent* resultNode = GetRightmostChild(aParentNode, aNoBlockCrossing);
  3054   if (!resultNode || !aEditableNode || IsEditable(resultNode)) {
  3055     return resultNode;
  3058   // restart the search from the non-editable node we just found
  3059   return GetPriorNode(resultNode, aEditableNode, aNoBlockCrossing);
  3063 nsresult 
  3064 nsEditor::GetNextNode(nsIDOMNode   *aParentNode, 
  3065                       int32_t      aOffset, 
  3066                       bool         aEditableNode, 
  3067                       nsCOMPtr<nsIDOMNode> *aResultNode,
  3068                       bool         bNoBlockCrossing)
  3070   NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
  3071   *aResultNode = nullptr;
  3073   nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
  3074   NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
  3076   *aResultNode = do_QueryInterface(GetNextNode(parentNode, aOffset,
  3077                                                aEditableNode,
  3078                                                bNoBlockCrossing));
  3079   return NS_OK;
  3082 nsIContent*
  3083 nsEditor::GetNextNode(nsINode* aParentNode,
  3084                       int32_t aOffset,
  3085                       bool aEditableNode,
  3086                       bool aNoBlockCrossing)
  3088   MOZ_ASSERT(aParentNode);
  3090   // if aParentNode is a text node, use its location instead
  3091   if (aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) {
  3092     nsINode* parent = aParentNode->GetParentNode();
  3093     NS_ENSURE_TRUE(parent, nullptr);
  3094     aOffset = parent->IndexOf(aParentNode) + 1; // _after_ the text node
  3095     aParentNode = parent;
  3098   // look at the child at 'aOffset'
  3099   nsIContent* child = aParentNode->GetChildAt(aOffset);
  3100   if (child) {
  3101     if (aNoBlockCrossing && IsBlockNode(child)) {
  3102       return child;
  3105     nsIContent* resultNode = GetLeftmostChild(child, aNoBlockCrossing);
  3106     if (!resultNode) {
  3107       return child;
  3110     if (!IsDescendantOfEditorRoot(resultNode)) {
  3111       return nullptr;
  3114     if (!aEditableNode || IsEditable(resultNode)) {
  3115       return resultNode;
  3118     // restart the search from the non-editable node we just found
  3119     return GetNextNode(resultNode, aEditableNode, aNoBlockCrossing);
  3122   // unless there isn't one, in which case we are at the end of the node
  3123   // and want the next one.
  3124   if (aNoBlockCrossing && IsBlockNode(aParentNode)) {
  3125     // don't cross out of parent block
  3126     return nullptr;
  3129   return GetNextNode(aParentNode, aEditableNode, aNoBlockCrossing);
  3133 nsresult 
  3134 nsEditor::GetPriorNode(nsIDOMNode  *aCurrentNode, 
  3135                        bool         aEditableNode, 
  3136                        nsCOMPtr<nsIDOMNode> *aResultNode,
  3137                        bool         bNoBlockCrossing)
  3139   NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
  3141   nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
  3142   NS_ENSURE_TRUE(currentNode, NS_ERROR_NULL_POINTER);
  3144   *aResultNode = do_QueryInterface(GetPriorNode(currentNode, aEditableNode,
  3145                                                 bNoBlockCrossing));
  3146   return NS_OK;
  3149 nsIContent*
  3150 nsEditor::GetPriorNode(nsINode* aCurrentNode, bool aEditableNode,
  3151                        bool aNoBlockCrossing /* = false */)
  3153   MOZ_ASSERT(aCurrentNode);
  3155   if (!IsDescendantOfEditorRoot(aCurrentNode)) {
  3156     return nullptr;
  3159   return FindNode(aCurrentNode, false, aEditableNode, aNoBlockCrossing);
  3162 nsIContent*
  3163 nsEditor::FindNextLeafNode(nsINode  *aCurrentNode, 
  3164                            bool      aGoForward,
  3165                            bool      bNoBlockCrossing)
  3167   // called only by GetPriorNode so we don't need to check params.
  3168   NS_PRECONDITION(IsDescendantOfEditorRoot(aCurrentNode) &&
  3169                   !IsEditorRoot(aCurrentNode),
  3170                   "Bogus arguments");
  3172   nsINode* cur = aCurrentNode;
  3173   for (;;) {
  3174     // if aCurrentNode has a sibling in the right direction, return
  3175     // that sibling's closest child (or itself if it has no children)
  3176     nsIContent* sibling =
  3177       aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling();
  3178     if (sibling) {
  3179       if (bNoBlockCrossing && IsBlockNode(sibling)) {
  3180         // don't look inside prevsib, since it is a block
  3181         return sibling;
  3183       nsIContent *leaf =
  3184         aGoForward ? GetLeftmostChild(sibling, bNoBlockCrossing) :
  3185                      GetRightmostChild(sibling, bNoBlockCrossing);
  3186       if (!leaf) { 
  3187         return sibling;
  3190       return leaf;
  3193     nsINode *parent = cur->GetParentNode();
  3194     if (!parent) {
  3195       return nullptr;
  3198     NS_ASSERTION(IsDescendantOfEditorRoot(parent),
  3199                  "We started with a proper descendant of root, and should stop "
  3200                  "if we ever hit the root, so we better have a descendant of "
  3201                  "root now!");
  3202     if (IsEditorRoot(parent) ||
  3203         (bNoBlockCrossing && IsBlockNode(parent))) {
  3204       return nullptr;
  3207     cur = parent;
  3210   NS_NOTREACHED("What part of for(;;) do you not understand?");
  3211   return nullptr;
  3214 nsresult
  3215 nsEditor::GetNextNode(nsIDOMNode* aCurrentNode,
  3216                       bool aEditableNode,
  3217                       nsCOMPtr<nsIDOMNode> *aResultNode,
  3218                       bool bNoBlockCrossing)
  3220   nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
  3221   if (!currentNode || !aResultNode) {
  3222     return NS_ERROR_NULL_POINTER;
  3225   *aResultNode = do_QueryInterface(GetNextNode(currentNode, aEditableNode,
  3226                                                bNoBlockCrossing));
  3227   return NS_OK;
  3230 nsIContent*
  3231 nsEditor::GetNextNode(nsINode* aCurrentNode,
  3232                       bool aEditableNode,
  3233                       bool bNoBlockCrossing)
  3235   MOZ_ASSERT(aCurrentNode);
  3237   if (!IsDescendantOfEditorRoot(aCurrentNode)) {
  3238     return nullptr;
  3241   return FindNode(aCurrentNode, true, aEditableNode, bNoBlockCrossing);
  3244 nsIContent*
  3245 nsEditor::FindNode(nsINode *aCurrentNode,
  3246                    bool     aGoForward,
  3247                    bool     aEditableNode,
  3248                    bool     bNoBlockCrossing)
  3250   if (IsEditorRoot(aCurrentNode)) {
  3251     // Don't allow traversal above the root node! This helps
  3252     // prevent us from accidentally editing browser content
  3253     // when the editor is in a text widget.
  3255     return nullptr;
  3258   nsCOMPtr<nsIContent> candidate =
  3259     FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
  3261   if (!candidate) {
  3262     return nullptr;
  3265   if (!aEditableNode || IsEditable(candidate)) {
  3266     return candidate;
  3269   return FindNode(candidate, aGoForward, aEditableNode, bNoBlockCrossing);
  3272 nsIDOMNode*
  3273 nsEditor::GetRightmostChild(nsIDOMNode* aCurrentNode,
  3274                             bool bNoBlockCrossing)
  3276   nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
  3277   nsIContent* result = GetRightmostChild(currentNode, bNoBlockCrossing);
  3278   return result ? result->AsDOMNode() : nullptr;
  3281 nsIContent*
  3282 nsEditor::GetRightmostChild(nsINode *aCurrentNode,
  3283                             bool     bNoBlockCrossing)
  3285   NS_ENSURE_TRUE(aCurrentNode, nullptr);
  3286   nsIContent *cur = aCurrentNode->GetLastChild();
  3287   if (!cur) {
  3288     return nullptr;
  3290   for (;;) {
  3291     if (bNoBlockCrossing && IsBlockNode(cur)) {
  3292       return cur;
  3294     nsIContent* next = cur->GetLastChild();
  3295     if (!next) {
  3296       return cur;
  3298     cur = next;
  3301   NS_NOTREACHED("What part of for(;;) do you not understand?");
  3302   return nullptr;
  3305 nsIContent*
  3306 nsEditor::GetLeftmostChild(nsINode *aCurrentNode,
  3307                            bool     bNoBlockCrossing)
  3309   NS_ENSURE_TRUE(aCurrentNode, nullptr);
  3310   nsIContent *cur = aCurrentNode->GetFirstChild();
  3311   if (!cur) {
  3312     return nullptr;
  3314   for (;;) {
  3315     if (bNoBlockCrossing && IsBlockNode(cur)) {
  3316       return cur;
  3318     nsIContent *next = cur->GetFirstChild();
  3319     if (!next) {
  3320       return cur;
  3322     cur = next;
  3325   NS_NOTREACHED("What part of for(;;) do you not understand?");
  3326   return nullptr;
  3329 nsIDOMNode*
  3330 nsEditor::GetLeftmostChild(nsIDOMNode* aCurrentNode,
  3331                            bool bNoBlockCrossing)
  3333   nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
  3334   nsIContent* result = GetLeftmostChild(currentNode, bNoBlockCrossing);
  3335   return result ? result->AsDOMNode() : nullptr;
  3338 bool
  3339 nsEditor::IsBlockNode(nsIDOMNode* aNode)
  3341   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  3342   return IsBlockNode(node);
  3345 bool
  3346 nsEditor::IsBlockNode(nsINode* aNode)
  3348   // stub to be overridden in nsHTMLEditor.
  3349   // screwing around with the class hierarchy here in order
  3350   // to not duplicate the code in GetNextNode/GetPrevNode
  3351   // across both nsEditor/nsHTMLEditor.
  3352   return false;
  3355 bool
  3356 nsEditor::CanContain(nsIDOMNode* aParent, nsIDOMNode* aChild)
  3358   nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
  3359   NS_ENSURE_TRUE(parent, false);
  3361   switch (parent->NodeType()) {
  3362   case nsIDOMNode::ELEMENT_NODE:
  3363   case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
  3364     return TagCanContain(parent->Tag(), aChild);
  3366   return false;
  3369 bool
  3370 nsEditor::CanContainTag(nsIDOMNode* aParent, nsIAtom* aChildTag)
  3372   nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
  3373   NS_ENSURE_TRUE(parent, false);
  3375   switch (parent->NodeType()) {
  3376   case nsIDOMNode::ELEMENT_NODE:
  3377   case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
  3378     return TagCanContainTag(parent->Tag(), aChildTag);
  3380   return false;
  3383 bool 
  3384 nsEditor::TagCanContain(nsIAtom* aParentTag, nsIDOMNode* aChild)
  3386   nsCOMPtr<nsIContent> child = do_QueryInterface(aChild);
  3387   NS_ENSURE_TRUE(child, false);
  3389   switch (child->NodeType()) {
  3390   case nsIDOMNode::TEXT_NODE:
  3391   case nsIDOMNode::ELEMENT_NODE:
  3392   case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
  3393     return TagCanContainTag(aParentTag, child->Tag());
  3395   return false;
  3398 bool 
  3399 nsEditor::TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag)
  3401   return true;
  3404 bool
  3405 nsEditor::IsRoot(nsIDOMNode* inNode)
  3407   NS_ENSURE_TRUE(inNode, false);
  3409   nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
  3411   return inNode == rootNode;
  3414 bool 
  3415 nsEditor::IsRoot(nsINode* inNode)
  3417   NS_ENSURE_TRUE(inNode, false);
  3419   nsCOMPtr<nsINode> rootNode = GetRoot();
  3421   return inNode == rootNode;
  3424 bool
  3425 nsEditor::IsEditorRoot(nsINode* aNode)
  3427   NS_ENSURE_TRUE(aNode, false);
  3428   nsCOMPtr<nsINode> rootNode = GetEditorRoot();
  3429   return aNode == rootNode;
  3432 bool 
  3433 nsEditor::IsDescendantOfRoot(nsIDOMNode* inNode)
  3435   nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
  3436   return IsDescendantOfRoot(node);
  3439 bool
  3440 nsEditor::IsDescendantOfRoot(nsINode* inNode)
  3442   NS_ENSURE_TRUE(inNode, false);
  3443   nsCOMPtr<nsIContent> root = GetRoot();
  3444   NS_ENSURE_TRUE(root, false);
  3446   return nsContentUtils::ContentIsDescendantOf(inNode, root);
  3449 bool
  3450 nsEditor::IsDescendantOfEditorRoot(nsIDOMNode* aNode)
  3452   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  3453   return IsDescendantOfEditorRoot(node);
  3456 bool
  3457 nsEditor::IsDescendantOfEditorRoot(nsINode* aNode)
  3459   NS_ENSURE_TRUE(aNode, false);
  3460   nsCOMPtr<nsIContent> root = GetEditorRoot();
  3461   NS_ENSURE_TRUE(root, false);
  3463   return nsContentUtils::ContentIsDescendantOf(aNode, root);
  3466 bool 
  3467 nsEditor::IsContainer(nsIDOMNode *aNode)
  3469   return aNode ? true : false;
  3472 static inline bool
  3473 IsElementVisible(dom::Element* aElement)
  3475   if (aElement->GetPrimaryFrame()) {
  3476     // It's visible, for our purposes
  3477     return true;
  3480   nsIContent *cur = aElement;
  3481   for (; ;) {
  3482     // Walk up the tree looking for the nearest ancestor with a frame.
  3483     // The state of the child right below it will determine whether
  3484     // we might possibly have a frame or not.
  3485     bool haveLazyBitOnChild = cur->HasFlag(NODE_NEEDS_FRAME);
  3486     cur = cur->GetFlattenedTreeParent();
  3487     if (!cur) {
  3488       if (!haveLazyBitOnChild) {
  3489         // None of our ancestors have lazy bits set, so we shouldn't
  3490         // have a frame
  3491         return false;
  3494       // The root has a lazy frame construction bit.  We need to check
  3495       // our style.
  3496       break;
  3499     if (cur->GetPrimaryFrame()) {
  3500       if (!haveLazyBitOnChild) {
  3501         // Our ancestor directly under |cur| doesn't have lazy bits;
  3502         // that means we won't get a frame
  3503         return false;
  3506       if (cur->GetPrimaryFrame()->IsLeaf()) {
  3507         // Nothing under here will ever get frames
  3508         return false;
  3511       // Otherwise, we might end up with a frame when that lazy bit is
  3512       // processed.  Figure out our actual style.
  3513       break;
  3517   // Now it might be that we have no frame because we're in a
  3518   // display:none subtree, or it might be that we're just dealing with
  3519   // lazy frame construction and it hasn't happened yet.  Check which
  3520   // one it is.
  3521   nsRefPtr<nsStyleContext> styleContext =
  3522     nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
  3523                                                          nullptr, nullptr);
  3524   if (styleContext) {
  3525     return styleContext->StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_NONE;
  3527   return false;
  3530 bool 
  3531 nsEditor::IsEditable(nsIDOMNode *aNode)
  3533   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  3534   return IsEditable(content);
  3537 bool
  3538 nsEditor::IsEditable(nsIContent *aNode)
  3540   NS_ENSURE_TRUE(aNode, false);
  3542   if (IsMozEditorBogusNode(aNode) || !IsModifiableNode(aNode)) return false;
  3544   // see if it has a frame.  If so, we'll edit it.
  3545   // special case for textnodes: frame must have width.
  3546   if (aNode->IsElement() && !IsElementVisible(aNode->AsElement())) {
  3547     // If the element has no frame, it's not editable.  Note that we
  3548     // need to check IsElement() here, because some of our tests
  3549     // rely on frameless textnodes being visible.
  3550     return false;
  3552   switch (aNode->NodeType()) {
  3553     case nsIDOMNode::ELEMENT_NODE:
  3554     case nsIDOMNode::TEXT_NODE:
  3555       return true; // element or text node; not invisible
  3556     default:
  3557       return false;
  3561 bool
  3562 nsEditor::IsMozEditorBogusNode(nsIContent *element)
  3564   return element &&
  3565          element->AttrValueIs(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
  3566                               kMOZEditorBogusNodeValue, eCaseMatters);
  3569 uint32_t
  3570 nsEditor::CountEditableChildren(nsINode* aNode)
  3572   MOZ_ASSERT(aNode);
  3573   uint32_t count = 0;
  3574   for (nsIContent* child = aNode->GetFirstChild();
  3575        child;
  3576        child = child->GetNextSibling()) {
  3577     if (IsEditable(child)) {
  3578       ++count;
  3581   return count;
  3584 //END nsEditor static utility methods
  3587 NS_IMETHODIMP nsEditor::IncrementModificationCount(int32_t inNumMods)
  3589   uint32_t oldModCount = mModCount;
  3591   mModCount += inNumMods;
  3593   if ((oldModCount == 0 && mModCount != 0)
  3594    || (oldModCount != 0 && mModCount == 0))
  3595     NotifyDocumentListeners(eDocumentStateChanged);
  3596   return NS_OK;
  3600 NS_IMETHODIMP nsEditor::GetModificationCount(int32_t *outModCount)
  3602   NS_ENSURE_ARG_POINTER(outModCount);
  3603   *outModCount = mModCount;
  3604   return NS_OK;
  3608 NS_IMETHODIMP nsEditor::ResetModificationCount()
  3610   bool doNotify = (mModCount != 0);
  3612   mModCount = 0;
  3614   if (doNotify)
  3615     NotifyDocumentListeners(eDocumentStateChanged);
  3616   return NS_OK;
  3619 //END nsEditor Private methods
  3623 ///////////////////////////////////////////////////////////////////////////
  3624 // GetTag: digs out the atom for the tag of this node
  3625 //
  3626 nsIAtom *
  3627 nsEditor::GetTag(nsIDOMNode *aNode)
  3629   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  3631   if (!content) 
  3633     NS_ASSERTION(aNode, "null node passed to nsEditor::Tag()");
  3635     return nullptr;
  3638   return content->Tag();
  3642 ///////////////////////////////////////////////////////////////////////////
  3643 // GetTagString: digs out string for the tag of this node
  3644 //                    
  3645 nsresult 
  3646 nsEditor::GetTagString(nsIDOMNode *aNode, nsAString& outString)
  3648   if (!aNode) 
  3650     NS_NOTREACHED("null node passed to nsEditor::GetTag()");
  3651     return NS_ERROR_NULL_POINTER;
  3654   nsIAtom *atom = GetTag(aNode);
  3655   if (!atom)
  3657     return NS_ERROR_FAILURE;
  3660   atom->ToString(outString);
  3661   return NS_OK;
  3665 ///////////////////////////////////////////////////////////////////////////
  3666 // NodesSameType: do these nodes have the same tag?
  3667 //                    
  3668 bool 
  3669 nsEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
  3671   if (!aNode1 || !aNode2) {
  3672     NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
  3673     return false;
  3676   nsCOMPtr<nsIContent> content1 = do_QueryInterface(aNode1);
  3677   NS_ENSURE_TRUE(content1, false);
  3679   nsCOMPtr<nsIContent> content2 = do_QueryInterface(aNode2);
  3680   NS_ENSURE_TRUE(content2, false);
  3682   return AreNodesSameType(content1, content2);
  3685 /* virtual */
  3686 bool
  3687 nsEditor::AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2)
  3689   MOZ_ASSERT(aNode1);
  3690   MOZ_ASSERT(aNode2);
  3691   return aNode1->Tag() == aNode2->Tag();
  3695 ///////////////////////////////////////////////////////////////////////////
  3696 // IsTextNode: true if node of dom type text
  3697 //               
  3698 bool
  3699 nsEditor::IsTextNode(nsIDOMNode *aNode)
  3701   if (!aNode)
  3703     NS_NOTREACHED("null node passed to IsTextNode()");
  3704     return false;
  3707   uint16_t nodeType;
  3708   aNode->GetNodeType(&nodeType);
  3709   return (nodeType == nsIDOMNode::TEXT_NODE);
  3712 bool
  3713 nsEditor::IsTextNode(nsINode *aNode)
  3715   return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
  3718 ///////////////////////////////////////////////////////////////////////////
  3719 // GetChildAt: returns the node at this position index in the parent
  3720 //
  3721 nsCOMPtr<nsIDOMNode> 
  3722 nsEditor::GetChildAt(nsIDOMNode *aParent, int32_t aOffset)
  3724   nsCOMPtr<nsIDOMNode> resultNode;
  3726   nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
  3728   NS_ENSURE_TRUE(parent, resultNode);
  3730   resultNode = do_QueryInterface(parent->GetChildAt(aOffset));
  3732   return resultNode;
  3735 ///////////////////////////////////////////////////////////////////////////
  3736 // GetNodeAtRangeOffsetPoint: returns the node at this position in a range,
  3737 // assuming that aParentOrNode is the node itself if it's a text node, or
  3738 // the node's parent otherwise.
  3739 //
  3740 nsCOMPtr<nsIDOMNode>
  3741 nsEditor::GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset)
  3743   if (IsTextNode(aParentOrNode)) {
  3744     return aParentOrNode;
  3746   return GetChildAt(aParentOrNode, aOffset);
  3750 ///////////////////////////////////////////////////////////////////////////
  3751 // GetStartNodeAndOffset: returns whatever the start parent & offset is of 
  3752 //                        the first range in the selection.
  3753 nsresult 
  3754 nsEditor::GetStartNodeAndOffset(nsISelection *aSelection,
  3755                                        nsIDOMNode **outStartNode,
  3756                                        int32_t *outStartOffset)
  3758   NS_ENSURE_TRUE(outStartNode && outStartOffset && aSelection, NS_ERROR_NULL_POINTER);
  3760   nsCOMPtr<nsINode> startNode;
  3761   nsresult rv = GetStartNodeAndOffset(static_cast<Selection*>(aSelection),
  3762                                       getter_AddRefs(startNode),
  3763                                       outStartOffset);
  3764   NS_ENSURE_SUCCESS(rv, rv);
  3766   if (startNode) {
  3767     NS_ADDREF(*outStartNode = startNode->AsDOMNode());
  3768   } else {
  3769     *outStartNode = nullptr;
  3771   return NS_OK;
  3774 nsresult
  3775 nsEditor::GetStartNodeAndOffset(Selection* aSelection, nsINode** aStartNode,
  3776                                 int32_t* aStartOffset)
  3778   MOZ_ASSERT(aSelection);
  3779   MOZ_ASSERT(aStartNode);
  3780   MOZ_ASSERT(aStartOffset);
  3782   *aStartNode = nullptr;
  3783   *aStartOffset = 0;
  3785   NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE);
  3787   const nsRange* range = aSelection->GetRangeAt(0);
  3788   NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
  3790   NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE);
  3792   NS_IF_ADDREF(*aStartNode = range->GetStartParent());
  3793   *aStartOffset = range->StartOffset();
  3794   return NS_OK;
  3798 ///////////////////////////////////////////////////////////////////////////
  3799 // GetEndNodeAndOffset: returns whatever the end parent & offset is of 
  3800 //                        the first range in the selection.
  3801 nsresult 
  3802 nsEditor::GetEndNodeAndOffset(nsISelection *aSelection,
  3803                                        nsIDOMNode **outEndNode,
  3804                                        int32_t *outEndOffset)
  3806   NS_ENSURE_TRUE(outEndNode && outEndOffset && aSelection, NS_ERROR_NULL_POINTER);
  3808   nsCOMPtr<nsINode> endNode;
  3809   nsresult rv = GetEndNodeAndOffset(static_cast<Selection*>(aSelection),
  3810                                     getter_AddRefs(endNode),
  3811                                     outEndOffset);
  3812   NS_ENSURE_SUCCESS(rv, rv);
  3814   if (endNode) {
  3815     NS_ADDREF(*outEndNode = endNode->AsDOMNode());
  3816   } else {
  3817     *outEndNode = nullptr;
  3819   return NS_OK;
  3822 nsresult
  3823 nsEditor::GetEndNodeAndOffset(Selection* aSelection, nsINode** aEndNode,
  3824                               int32_t* aEndOffset)
  3826   MOZ_ASSERT(aSelection);
  3827   MOZ_ASSERT(aEndNode);
  3828   MOZ_ASSERT(aEndOffset);
  3830   *aEndNode = nullptr;
  3831   *aEndOffset = 0;
  3833   NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE);
  3835   const nsRange* range = aSelection->GetRangeAt(0);
  3836   NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
  3838   NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE);
  3840   NS_IF_ADDREF(*aEndNode = range->GetEndParent());
  3841   *aEndOffset = range->EndOffset();
  3842   return NS_OK;
  3846 ///////////////////////////////////////////////////////////////////////////
  3847 // IsPreformatted: checks the style info for the node for the preformatted
  3848 //                 text style.
  3849 nsresult 
  3850 nsEditor::IsPreformatted(nsIDOMNode *aNode, bool *aResult)
  3852   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  3854   NS_ENSURE_TRUE(aResult && content, NS_ERROR_NULL_POINTER);
  3856   nsCOMPtr<nsIPresShell> ps = GetPresShell();
  3857   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
  3859   // Look at the node (and its parent if it's not an element), and grab its style context
  3860   nsRefPtr<nsStyleContext> elementStyle;
  3861   if (!content->IsElement()) {
  3862     content = content->GetParent();
  3864   if (content && content->IsElement()) {
  3865     elementStyle = nsComputedDOMStyle::GetStyleContextForElementNoFlush(content->AsElement(),
  3866                                                                         nullptr,
  3867                                                                         ps);
  3870   if (!elementStyle)
  3872     // Consider nodes without a style context to be NOT preformatted:
  3873     // For instance, this is true of JS tags inside the body (which show
  3874     // up as #text nodes but have no style context).
  3875     *aResult = false;
  3876     return NS_OK;
  3879   const nsStyleText* styleText = elementStyle->StyleText();
  3881   *aResult = styleText->WhiteSpaceIsSignificant();
  3882   return NS_OK;
  3886 ///////////////////////////////////////////////////////////////////////////
  3887 // SplitNodeDeep: this splits a node "deeply", splitting children as 
  3888 //                appropriate.  The place to split is represented by
  3889 //                a dom point at {splitPointParent, splitPointOffset}.
  3890 //                That dom point must be inside aNode, which is the node to 
  3891 //                split.  outOffset is set to the offset in the parent of aNode where
  3892 //                the split terminates - where you would want to insert 
  3893 //                a new element, for instance, if that's why you were splitting 
  3894 //                the node.
  3895 //
  3896 nsresult
  3897 nsEditor::SplitNodeDeep(nsIDOMNode *aNode, 
  3898                         nsIDOMNode *aSplitPointParent, 
  3899                         int32_t aSplitPointOffset,
  3900                         int32_t *outOffset,
  3901                         bool    aNoEmptyContainers,
  3902                         nsCOMPtr<nsIDOMNode> *outLeftNode,
  3903                         nsCOMPtr<nsIDOMNode> *outRightNode)
  3905   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  3906   NS_ENSURE_TRUE(node && aSplitPointParent && outOffset, NS_ERROR_NULL_POINTER);
  3907   int32_t offset = aSplitPointOffset;
  3909   if (outLeftNode)  *outLeftNode  = nullptr;
  3910   if (outRightNode) *outRightNode = nullptr;
  3912   nsCOMPtr<nsINode> nodeToSplit = do_QueryInterface(aSplitPointParent);
  3913   while (nodeToSplit) {
  3914     // need to insert rules code call here to do things like
  3915     // not split a list if you are after the last <li> or before the first, etc.
  3916     // for now we just have some smarts about unneccessarily splitting
  3917     // textnodes, which should be universal enough to put straight in
  3918     // this nsEditor routine.
  3920     nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
  3921     uint32_t len = nodeToSplit->Length();
  3922     bool bDoSplit = false;
  3924     if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (int32_t)len)))
  3926       bDoSplit = true;
  3927       nsCOMPtr<nsIDOMNode> tempNode;
  3928       nsresult rv = SplitNode(nodeToSplit->AsDOMNode(), offset,
  3929                               getter_AddRefs(tempNode));
  3930       NS_ENSURE_SUCCESS(rv, rv);
  3932       if (outRightNode) {
  3933         *outRightNode = nodeToSplit->AsDOMNode();
  3935       if (outLeftNode) {
  3936         *outLeftNode = tempNode;
  3940     nsINode* parentNode = nodeToSplit->GetParentNode();
  3941     NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
  3943     if (!bDoSplit && offset) {
  3944       // must be "end of text node" case, we didn't split it, just move past it
  3945       offset = parentNode->IndexOf(nodeToSplit) + 1;
  3946       if (outLeftNode) {
  3947         *outLeftNode = nodeToSplit->AsDOMNode();
  3949     } else {
  3950       offset = parentNode->IndexOf(nodeToSplit);
  3951       if (outRightNode) {
  3952         *outRightNode = nodeToSplit->AsDOMNode();
  3956     if (nodeToSplit == node) {
  3957       // we split all the way up to (and including) aNode; we're done
  3958       break;
  3961     nodeToSplit = parentNode;
  3964   if (!nodeToSplit) {
  3965     NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()");
  3966     return NS_ERROR_FAILURE;
  3969   *outOffset = offset;
  3970   return NS_OK;
  3974 ///////////////////////////////////////////////////////////////////////////
  3975 // JoinNodeDeep:  this joins two like nodes "deeply", joining children as 
  3976 //                appropriate.  
  3977 nsresult
  3978 nsEditor::JoinNodeDeep(nsIDOMNode *aLeftNode, 
  3979                        nsIDOMNode *aRightNode,
  3980                        nsCOMPtr<nsIDOMNode> *aOutJoinNode, 
  3981                        int32_t *outOffset)
  3983   NS_ENSURE_TRUE(aLeftNode && aRightNode && aOutJoinNode && outOffset, NS_ERROR_NULL_POINTER);
  3985   // while the rightmost children and their descendants of the left node 
  3986   // match the leftmost children and their descendants of the right node
  3987   // join them up.  Can you say that three times fast?
  3989   nsCOMPtr<nsIDOMNode> leftNodeToJoin = do_QueryInterface(aLeftNode);
  3990   nsCOMPtr<nsIDOMNode> rightNodeToJoin = do_QueryInterface(aRightNode);
  3991   nsCOMPtr<nsIDOMNode> parentNode,tmp;
  3992   nsresult res = NS_OK;
  3994   rightNodeToJoin->GetParentNode(getter_AddRefs(parentNode));
  3996   while (leftNodeToJoin && rightNodeToJoin && parentNode &&
  3997           NodesSameType(leftNodeToJoin, rightNodeToJoin))
  3999     // adjust out params
  4000     uint32_t length;
  4001     res = GetLengthOfDOMNode(leftNodeToJoin, length);
  4002     NS_ENSURE_SUCCESS(res, res);
  4004     *aOutJoinNode = rightNodeToJoin;
  4005     *outOffset = length;
  4007     // do the join
  4008     res = JoinNodes(leftNodeToJoin, rightNodeToJoin, parentNode);
  4009     NS_ENSURE_SUCCESS(res, res);
  4011     if (IsTextNode(parentNode)) // we've joined all the way down to text nodes, we're done!
  4012       return NS_OK;
  4014     else
  4016       // get new left and right nodes, and begin anew
  4017       parentNode = rightNodeToJoin;
  4018       leftNodeToJoin = GetChildAt(parentNode, length-1);
  4019       rightNodeToJoin = GetChildAt(parentNode, length);
  4021       // skip over non-editable nodes
  4022       while (leftNodeToJoin && !IsEditable(leftNodeToJoin))
  4024         leftNodeToJoin->GetPreviousSibling(getter_AddRefs(tmp));
  4025         leftNodeToJoin = tmp;
  4027       if (!leftNodeToJoin) break;
  4029       while (rightNodeToJoin && !IsEditable(rightNodeToJoin))
  4031         rightNodeToJoin->GetNextSibling(getter_AddRefs(tmp));
  4032         rightNodeToJoin = tmp;
  4034       if (!rightNodeToJoin) break;
  4038   return res;
  4041 void
  4042 nsEditor::BeginUpdateViewBatch()
  4044   NS_PRECONDITION(mUpdateCount >= 0, "bad state");
  4046   if (0 == mUpdateCount)
  4048     // Turn off selection updates and notifications.
  4050     nsCOMPtr<nsISelection> selection;
  4051     GetSelection(getter_AddRefs(selection));
  4053     if (selection) 
  4055       nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
  4056       selPrivate->StartBatchChanges();
  4060   mUpdateCount++;
  4064 nsresult nsEditor::EndUpdateViewBatch()
  4066   NS_PRECONDITION(mUpdateCount > 0, "bad state");
  4068   if (mUpdateCount <= 0)
  4070     mUpdateCount = 0;
  4071     return NS_ERROR_FAILURE;
  4074   mUpdateCount--;
  4076   if (0 == mUpdateCount)
  4078     // Turn selection updating and notifications back on.
  4080     nsCOMPtr<nsISelection>selection;
  4081     GetSelection(getter_AddRefs(selection));
  4083     if (selection) {
  4084       nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
  4085       selPrivate->EndBatchChanges();
  4089   return NS_OK;
  4092 bool 
  4093 nsEditor::GetShouldTxnSetSelection()
  4095   return mShouldTxnSetSelection;
  4099 NS_IMETHODIMP 
  4100 nsEditor::DeleteSelectionImpl(EDirection aAction,
  4101                               EStripWrappers aStripWrappers)
  4103   MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
  4105   nsCOMPtr<nsISelection>selection;
  4106   nsresult res = GetSelection(getter_AddRefs(selection));
  4107   NS_ENSURE_SUCCESS(res, res);
  4108   nsRefPtr<EditAggregateTxn> txn;
  4109   nsCOMPtr<nsINode> deleteNode;
  4110   int32_t deleteCharOffset = 0, deleteCharLength = 0;
  4111   res = CreateTxnForDeleteSelection(aAction, getter_AddRefs(txn),
  4112                                     getter_AddRefs(deleteNode),
  4113                                     &deleteCharOffset, &deleteCharLength);
  4114   nsCOMPtr<nsIDOMCharacterData> deleteCharData(do_QueryInterface(deleteNode));
  4116   if (NS_SUCCEEDED(res))  
  4118     nsAutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
  4119     int32_t i;
  4120     // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
  4121     if (!deleteNode)
  4122       for (i = 0; i < mActionListeners.Count(); i++)
  4123         mActionListeners[i]->WillDeleteSelection(selection);
  4124     else if (deleteCharData)
  4125       for (i = 0; i < mActionListeners.Count(); i++)
  4126         mActionListeners[i]->WillDeleteText(deleteCharData, deleteCharOffset, 1);
  4127     else
  4128       for (i = 0; i < mActionListeners.Count(); i++)
  4129         mActionListeners[i]->WillDeleteNode(deleteNode->AsDOMNode());
  4131     // Delete the specified amount
  4132     res = DoTransaction(txn);  
  4134     // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
  4135     if (!deleteNode)
  4136       for (i = 0; i < mActionListeners.Count(); i++)
  4137         mActionListeners[i]->DidDeleteSelection(selection);
  4138     else if (deleteCharData)
  4139       for (i = 0; i < mActionListeners.Count(); i++)
  4140         mActionListeners[i]->DidDeleteText(deleteCharData, deleteCharOffset, 1, res);
  4141     else
  4142       for (i = 0; i < mActionListeners.Count(); i++)
  4143         mActionListeners[i]->DidDeleteNode(deleteNode->AsDOMNode(), res);
  4146   return res;
  4149 // XXX: error handling in this routine needs to be cleaned up!
  4150 NS_IMETHODIMP
  4151 nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
  4152                                            nsIDOMNode ** aNewNode)
  4154   nsresult result = DeleteSelectionAndPrepareToCreateNode();
  4155   NS_ENSURE_SUCCESS(result, result);
  4157   nsRefPtr<Selection> selection = GetSelection();
  4158   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  4160   nsCOMPtr<nsINode> node = selection->GetAnchorNode();
  4161   uint32_t offset = selection->AnchorOffset();
  4163   nsCOMPtr<nsIDOMNode> newNode;
  4164   result = CreateNode(aTag, node->AsDOMNode(), offset,
  4165                       getter_AddRefs(newNode));
  4166   // XXX: ERROR_HANDLING  check result, and make sure aNewNode is set correctly
  4167   // in success/failure cases
  4168   *aNewNode = newNode;
  4169   NS_IF_ADDREF(*aNewNode);
  4171   // we want the selection to be just after the new node
  4172   return selection->Collapse(node, offset + 1);
  4176 /* Non-interface, protected methods */
  4178 TextComposition*
  4179 nsEditor::GetComposition() const
  4181   return mComposition;
  4184 bool
  4185 nsEditor::IsIMEComposing() const
  4187   return mComposition && mComposition->IsComposing();
  4190 nsresult
  4191 nsEditor::DeleteSelectionAndPrepareToCreateNode()
  4193   nsresult res;
  4194   nsRefPtr<Selection> selection = GetSelection();
  4195   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  4196   MOZ_ASSERT(selection->GetAnchorFocusRange());
  4198   if (!selection->GetAnchorFocusRange()->Collapsed()) {
  4199     res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
  4200     NS_ENSURE_SUCCESS(res, res);
  4202     MOZ_ASSERT(selection->GetAnchorFocusRange() &&
  4203                selection->GetAnchorFocusRange()->Collapsed(),
  4204                "Selection not collapsed after delete");
  4207   // If the selection is a chardata node, split it if necessary and compute
  4208   // where to put the new node
  4209   nsCOMPtr<nsINode> node = selection->GetAnchorNode();
  4210   MOZ_ASSERT(node, "Selection has no ranges in it");
  4212   if (node && node->IsNodeOfType(nsINode::eDATA_NODE)) {
  4213     NS_ASSERTION(node->GetParentNode(),
  4214                  "It's impossible to insert into chardata with no parent -- "
  4215                  "fix the caller");
  4216     NS_ENSURE_STATE(node->GetParentNode());
  4218     uint32_t offset = selection->AnchorOffset();
  4220     if (offset == 0) {
  4221       res = selection->Collapse(node->GetParentNode(),
  4222                                 node->GetParentNode()->IndexOf(node));
  4223       MOZ_ASSERT(NS_SUCCEEDED(res));
  4224       NS_ENSURE_SUCCESS(res, res);
  4225     } else if (offset == node->Length()) {
  4226       res = selection->Collapse(node->GetParentNode(),
  4227                                 node->GetParentNode()->IndexOf(node) + 1);
  4228       MOZ_ASSERT(NS_SUCCEEDED(res));
  4229       NS_ENSURE_SUCCESS(res, res);
  4230     } else {
  4231       nsCOMPtr<nsIDOMNode> tmp;
  4232       res = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp));
  4233       NS_ENSURE_SUCCESS(res, res);
  4234       res = selection->Collapse(node->GetParentNode(),
  4235                                 node->GetParentNode()->IndexOf(node));
  4236       MOZ_ASSERT(NS_SUCCEEDED(res));
  4237       NS_ENSURE_SUCCESS(res, res);
  4240   return NS_OK;
  4245 void
  4246 nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
  4248   bool isTransientTransaction;
  4249   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
  4250     aTxn->GetIsTransient(&isTransientTransaction)));
  4252   if (!isTransientTransaction)
  4254     // we need to deal here with the case where the user saved after some
  4255     // edits, then undid one or more times. Then, the undo count is -ve,
  4256     // but we can't let a do take it back to zero. So we flip it up to
  4257     // a +ve number.
  4258     int32_t modCount;
  4259     GetModificationCount(&modCount);
  4260     if (modCount < 0)
  4261       modCount = -modCount;
  4263     // don't count transient transactions
  4264     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
  4265       IncrementModificationCount(1)));
  4270 void
  4271 nsEditor::DoAfterUndoTransaction()
  4273   // all undoable transactions are non-transient
  4274   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
  4275     IncrementModificationCount(-1)));
  4278 void
  4279 nsEditor::DoAfterRedoTransaction()
  4281   // all redoable transactions are non-transient
  4282   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
  4283     IncrementModificationCount(1)));
  4286 NS_IMETHODIMP 
  4287 nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, 
  4288                                    const nsAString& aAttribute, 
  4289                                    const nsAString& aValue,
  4290                                    ChangeAttributeTxn ** aTxn)
  4292   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
  4294   nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn();
  4296   nsresult rv = txn->Init(this, aElement, aAttribute, aValue, false);
  4297   if (NS_SUCCEEDED(rv))
  4299     txn.forget(aTxn);
  4302   return rv;
  4306 NS_IMETHODIMP 
  4307 nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, 
  4308                                       const nsAString& aAttribute,
  4309                                       ChangeAttributeTxn ** aTxn)
  4311   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
  4313   nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn();
  4315   nsresult rv = txn->Init(this, aElement, aAttribute, EmptyString(), true);
  4316   if (NS_SUCCEEDED(rv))
  4318     txn.forget(aTxn);
  4321   return rv;
  4325 NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsAString& aTag,
  4326                                                   nsIDOMNode     *aParent,
  4327                                                   int32_t         aPosition,
  4328                                                   CreateElementTxn ** aTxn)
  4330   NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER);
  4332   nsRefPtr<CreateElementTxn> txn = new CreateElementTxn();
  4334   nsresult rv = txn->Init(this, aTag, aParent, aPosition);
  4335   if (NS_SUCCEEDED(rv))
  4337     txn.forget(aTxn);
  4340   return rv;
  4344 NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode,
  4345                                                   nsIDOMNode * aParent,
  4346                                                   int32_t      aPosition,
  4347                                                   InsertElementTxn ** aTxn)
  4349   NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER);
  4351   nsRefPtr<InsertElementTxn> txn = new InsertElementTxn();
  4353   nsresult rv = txn->Init(aNode, aParent, aPosition, this);
  4354   if (NS_SUCCEEDED(rv))
  4356     txn.forget(aTxn);
  4359   return rv;
  4362 nsresult
  4363 nsEditor::CreateTxnForDeleteNode(nsINode* aNode, DeleteNodeTxn** aTxn)
  4365   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  4367   nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn();
  4369   nsresult res = txn->Init(this, aNode, &mRangeUpdater);
  4370   NS_ENSURE_SUCCESS(res, res);
  4372   txn.forget(aTxn);
  4373   return NS_OK;
  4376 NS_IMETHODIMP 
  4377 nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
  4378                               IMETextTxn ** aTxn)
  4380   NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
  4382   nsRefPtr<IMETextTxn> txn = new IMETextTxn();
  4384   // During handling IME composition, mComposition must have been initialized.
  4385   // TODO: We can simplify IMETextTxn::Init() with TextComposition class.
  4386   nsresult rv = txn->Init(mIMETextNode, mIMETextOffset,
  4387                           mComposition->String().Length(),
  4388                           mComposition->GetRanges(), aStringToInsert, this);
  4389   if (NS_SUCCEEDED(rv))
  4391     txn.forget(aTxn);
  4394   return rv;
  4398 NS_IMETHODIMP 
  4399 nsEditor::CreateTxnForAddStyleSheet(nsCSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn)
  4401   nsRefPtr<AddStyleSheetTxn> txn = new AddStyleSheetTxn();
  4403   nsresult rv = txn->Init(this, aSheet);
  4404   if (NS_SUCCEEDED(rv))
  4406     txn.forget(aTxn);
  4409   return rv;
  4414 NS_IMETHODIMP 
  4415 nsEditor::CreateTxnForRemoveStyleSheet(nsCSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn)
  4417   nsRefPtr<RemoveStyleSheetTxn> txn = new RemoveStyleSheetTxn();
  4419   nsresult rv = txn->Init(this, aSheet);
  4420   if (NS_SUCCEEDED(rv))
  4422     txn.forget(aTxn);
  4425   return rv;
  4429 nsresult
  4430 nsEditor::CreateTxnForDeleteSelection(EDirection aAction,
  4431                                       EditAggregateTxn** aTxn,
  4432                                       nsINode** aNode,
  4433                                       int32_t* aOffset,
  4434                                       int32_t* aLength)
  4436   MOZ_ASSERT(aTxn);
  4437   *aTxn = nullptr;
  4439   nsRefPtr<Selection> selection = GetSelection();
  4440   NS_ENSURE_STATE(selection);
  4442   // Check whether the selection is collapsed and we should do nothing:
  4443   if (selection->Collapsed() && aAction == eNone) {
  4444     return NS_OK;
  4447   // allocate the out-param transaction
  4448   nsRefPtr<EditAggregateTxn> aggTxn = new EditAggregateTxn();
  4450   for (int32_t rangeIdx = 0; rangeIdx < selection->GetRangeCount(); ++rangeIdx) {
  4451     nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
  4452     NS_ENSURE_STATE(range);
  4454     // Same with range as with selection; if it is collapsed and action
  4455     // is eNone, do nothing.
  4456     if (!range->Collapsed()) {
  4457       nsRefPtr<DeleteRangeTxn> txn = new DeleteRangeTxn();
  4458       txn->Init(this, range, &mRangeUpdater);
  4459       aggTxn->AppendChild(txn);
  4460     } else if (aAction != eNone) {
  4461       // we have an insertion point.  delete the thing in front of it or
  4462       // behind it, depending on aAction
  4463       nsresult res = CreateTxnForDeleteInsertionPoint(range, aAction, aggTxn,
  4464                                                       aNode, aOffset, aLength);
  4465       NS_ENSURE_SUCCESS(res, res);
  4469   aggTxn.forget(aTxn);
  4471   return NS_OK;
  4474 nsresult
  4475 nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData,
  4476                                       uint32_t             aOffset,
  4477                                       EDirection           aDirection,
  4478                                       DeleteTextTxn**      aTxn)
  4480   NS_ASSERTION(aDirection == eNext || aDirection == ePrevious,
  4481                "invalid direction");
  4482   nsAutoString data;
  4483   aData->GetData(data);
  4484   NS_ASSERTION(data.Length(), "Trying to delete from a zero-length node");
  4485   NS_ENSURE_STATE(data.Length());
  4487   uint32_t segOffset = aOffset, segLength = 1;
  4488   if (aDirection == eNext) {
  4489     if (segOffset + 1 < data.Length() &&
  4490         NS_IS_HIGH_SURROGATE(data[segOffset]) &&
  4491         NS_IS_LOW_SURROGATE(data[segOffset+1])) {
  4492       // delete both halves of the surrogate pair
  4493       ++segLength;
  4495   } else if (aOffset > 0) {
  4496     --segOffset;
  4497     if (segOffset > 0 &&
  4498       NS_IS_LOW_SURROGATE(data[segOffset]) &&
  4499       NS_IS_HIGH_SURROGATE(data[segOffset-1])) {
  4500       ++segLength;
  4501       --segOffset;
  4503   } else {
  4504     return NS_ERROR_FAILURE;
  4506   return CreateTxnForDeleteText(aData, segOffset, segLength, aTxn);
  4509 //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
  4510 //are not implemented
  4511 nsresult
  4512 nsEditor::CreateTxnForDeleteInsertionPoint(nsRange*          aRange,
  4513                                            EDirection        aAction,
  4514                                            EditAggregateTxn* aTxn,
  4515                                            nsINode**         aNode,
  4516                                            int32_t*          aOffset,
  4517                                            int32_t*          aLength)
  4519   MOZ_ASSERT(aAction != eNone);
  4521   nsresult res;
  4523   // get the node and offset of the insertion point
  4524   nsCOMPtr<nsINode> node = aRange->GetStartParent();
  4525   NS_ENSURE_STATE(node);
  4527   int32_t offset = aRange->StartOffset();
  4529   // determine if the insertion point is at the beginning, middle, or end of
  4530   // the node
  4531   nsCOMPtr<nsIDOMCharacterData> nodeAsCharData = do_QueryInterface(node);
  4533   uint32_t count = node->Length();
  4535   bool isFirst = (0 == offset);
  4536   bool isLast  = (count == (uint32_t)offset);
  4538   // XXX: if isFirst && isLast, then we'll need to delete the node
  4539   //      as well as the 1 child
  4541   // build a transaction for deleting the appropriate data
  4542   // XXX: this has to come from rule section
  4543   if (aAction == ePrevious && isFirst) {
  4544     // we're backspacing from the beginning of the node.  Delete the first
  4545     // thing to our left
  4546     nsCOMPtr<nsIContent> priorNode = GetPriorNode(node, true);
  4547     NS_ENSURE_STATE(priorNode);
  4549     // there is a priorNode, so delete its last child (if chardata, delete the
  4550     // last char). if it has no children, delete it
  4551     nsCOMPtr<nsIDOMCharacterData> priorNodeAsCharData =
  4552       do_QueryInterface(priorNode);
  4553     if (priorNodeAsCharData) {
  4554       uint32_t length = priorNode->Length();
  4555       // Bail out for empty chardata XXX: Do we want to do something else?
  4556       NS_ENSURE_STATE(length);
  4557       nsRefPtr<DeleteTextTxn> txn;
  4558       res = CreateTxnForDeleteCharacter(priorNodeAsCharData, length,
  4559                                         ePrevious, getter_AddRefs(txn));
  4560       NS_ENSURE_SUCCESS(res, res);
  4562       *aOffset = txn->GetOffset();
  4563       *aLength = txn->GetNumCharsToDelete();
  4564       aTxn->AppendChild(txn);
  4565     } else {
  4566       // priorNode is not chardata, so tell its parent to delete it
  4567       nsRefPtr<DeleteNodeTxn> txn;
  4568       res = CreateTxnForDeleteNode(priorNode, getter_AddRefs(txn));
  4569       NS_ENSURE_SUCCESS(res, res);
  4571       aTxn->AppendChild(txn);
  4574     NS_ADDREF(*aNode = priorNode);
  4576     return NS_OK;
  4579   if (aAction == eNext && isLast) {
  4580     // we're deleting from the end of the node.  Delete the first thing to our
  4581     // right
  4582     nsCOMPtr<nsIContent> nextNode = GetNextNode(node, true);
  4583     NS_ENSURE_STATE(nextNode);
  4585     // there is a nextNode, so delete its first child (if chardata, delete the
  4586     // first char). if it has no children, delete it
  4587     nsCOMPtr<nsIDOMCharacterData> nextNodeAsCharData =
  4588       do_QueryInterface(nextNode);
  4589     if (nextNodeAsCharData) {
  4590       uint32_t length = nextNode->Length();
  4591       // Bail out for empty chardata XXX: Do we want to do something else?
  4592       NS_ENSURE_STATE(length);
  4593       nsRefPtr<DeleteTextTxn> txn;
  4594       res = CreateTxnForDeleteCharacter(nextNodeAsCharData, 0, eNext,
  4595                                         getter_AddRefs(txn));
  4596       NS_ENSURE_SUCCESS(res, res);
  4598       *aOffset = txn->GetOffset();
  4599       *aLength = txn->GetNumCharsToDelete();
  4600       aTxn->AppendChild(txn);
  4601     } else {
  4602       // nextNode is not chardata, so tell its parent to delete it
  4603       nsRefPtr<DeleteNodeTxn> txn;
  4604       res = CreateTxnForDeleteNode(nextNode, getter_AddRefs(txn));
  4605       NS_ENSURE_SUCCESS(res, res);
  4606       aTxn->AppendChild(txn);
  4609     NS_ADDREF(*aNode = nextNode);
  4611     return NS_OK;
  4614   if (nodeAsCharData) {
  4615     // we have chardata, so delete a char at the proper offset
  4616     nsRefPtr<DeleteTextTxn> txn;
  4617     res = CreateTxnForDeleteCharacter(nodeAsCharData, offset, aAction,
  4618                                       getter_AddRefs(txn));
  4619     NS_ENSURE_SUCCESS(res, res);
  4621     aTxn->AppendChild(txn);
  4622     NS_ADDREF(*aNode = node);
  4623     *aOffset = txn->GetOffset();
  4624     *aLength = txn->GetNumCharsToDelete();
  4625   } else {
  4626     // we're either deleting a node or chardata, need to dig into the next/prev
  4627     // node to find out
  4628     nsCOMPtr<nsINode> selectedNode;
  4629     if (aAction == ePrevious) {
  4630       selectedNode = GetPriorNode(node, offset, true);
  4631     } else if (aAction == eNext) {
  4632       selectedNode = GetNextNode(node, offset, true);
  4635     while (selectedNode &&
  4636            selectedNode->IsNodeOfType(nsINode::eDATA_NODE) &&
  4637            !selectedNode->Length()) {
  4638       // Can't delete an empty chardata node (bug 762183)
  4639       if (aAction == ePrevious) {
  4640         selectedNode = GetPriorNode(selectedNode, true);
  4641       } else if (aAction == eNext) {
  4642         selectedNode = GetNextNode(selectedNode, true);
  4645     NS_ENSURE_STATE(selectedNode);
  4647     nsCOMPtr<nsIDOMCharacterData> selectedNodeAsCharData =
  4648       do_QueryInterface(selectedNode);
  4649     if (selectedNodeAsCharData) {
  4650       // we are deleting from a chardata node, so do a character deletion
  4651       uint32_t position = 0;
  4652       if (aAction == ePrevious) {
  4653         position = selectedNode->Length();
  4655       nsRefPtr<DeleteTextTxn> delTextTxn;
  4656       res = CreateTxnForDeleteCharacter(selectedNodeAsCharData, position,
  4657                                         aAction, getter_AddRefs(delTextTxn));
  4658       NS_ENSURE_SUCCESS(res, res);
  4659       NS_ENSURE_TRUE(delTextTxn, NS_ERROR_NULL_POINTER);
  4661       aTxn->AppendChild(delTextTxn);
  4662       *aOffset = delTextTxn->GetOffset();
  4663       *aLength = delTextTxn->GetNumCharsToDelete();
  4664     } else {
  4665       nsRefPtr<DeleteNodeTxn> delElementTxn;
  4666       res = CreateTxnForDeleteNode(selectedNode, getter_AddRefs(delElementTxn));
  4667       NS_ENSURE_SUCCESS(res, res);
  4668       NS_ENSURE_TRUE(delElementTxn, NS_ERROR_NULL_POINTER);
  4670       aTxn->AppendChild(delElementTxn);
  4673     NS_ADDREF(*aNode = selectedNode);
  4676   return NS_OK;
  4679 nsresult 
  4680 nsEditor::CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset,
  4681                       nsIDOMNode *aEndParent, int32_t aEndOffset,
  4682                       nsIDOMRange **aRange)
  4684   return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
  4685                               aEndOffset, aRange);
  4688 nsresult 
  4689 nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode)
  4691   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  4692   nsCOMPtr<nsISelection> selection;
  4693   nsresult res = GetSelection(getter_AddRefs(selection));
  4694   NS_ENSURE_SUCCESS(res, res);
  4695   if(!selection) return NS_ERROR_FAILURE;
  4697   nsCOMPtr<nsIDOMNode> parentNode;
  4698   res = aNode->GetParentNode(getter_AddRefs(parentNode));
  4699   NS_ENSURE_SUCCESS(res, res);
  4700   NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
  4702   int32_t offset = GetChildOffset(aNode, parentNode);
  4704   nsCOMPtr<nsIDOMRange> range;
  4705   res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range));
  4706   NS_ENSURE_SUCCESS(res, res);
  4707   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
  4709   return selection->AddRange(range);
  4712 nsresult nsEditor::ClearSelection()
  4714   nsCOMPtr<nsISelection> selection;
  4715   nsresult res = nsEditor::GetSelection(getter_AddRefs(selection));
  4716   NS_ENSURE_SUCCESS(res, res);
  4717   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  4718   return selection->RemoveAllRanges();  
  4721 nsresult
  4722 nsEditor::CreateHTMLContent(const nsAString& aTag, dom::Element** aContent)
  4724   nsCOMPtr<nsIDocument> doc = GetDocument();
  4725   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  4727   // XXX Wallpaper over editor bug (editor tries to create elements with an
  4728   //     empty nodename).
  4729   if (aTag.IsEmpty()) {
  4730     NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, "
  4731              "check caller.");
  4732     return NS_ERROR_FAILURE;
  4735   return doc->CreateElem(aTag, nullptr, kNameSpaceID_XHTML,
  4736                          reinterpret_cast<nsIContent**>(aContent));
  4739 nsresult
  4740 nsEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
  4741                                    const nsAString & aAttribute,
  4742                                    const nsAString & aValue,
  4743                                    bool aSuppressTransaction)
  4745   return SetAttribute(aElement, aAttribute, aValue);
  4748 nsresult
  4749 nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
  4750                                       const nsAString & aAttribute,
  4751                                       bool aSuppressTransaction)
  4753   return RemoveAttribute(aElement, aAttribute);
  4756 nsresult
  4757 nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
  4759   // NOTE: When you change this method, you should also change:
  4760   //   * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
  4761   //   * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
  4762   //
  4763   // And also when you add new key handling, you need to change the subclass's
  4764   // HandleKeyPressEvent()'s switch statement.
  4766   WidgetKeyboardEvent* nativeKeyEvent =
  4767     aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
  4768   NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
  4769   NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
  4770                "HandleKeyPressEvent gets non-keypress event");
  4772   // if we are readonly or disabled, then do nothing.
  4773   if (IsReadonly() || IsDisabled()) {
  4774     // consume backspace for disabled and readonly textfields, to prevent
  4775     // back in history, which could be confusing to users
  4776     if (nativeKeyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
  4777       aKeyEvent->PreventDefault();
  4779     return NS_OK;
  4782   switch (nativeKeyEvent->keyCode) {
  4783     case nsIDOMKeyEvent::DOM_VK_META:
  4784     case nsIDOMKeyEvent::DOM_VK_WIN:
  4785     case nsIDOMKeyEvent::DOM_VK_SHIFT:
  4786     case nsIDOMKeyEvent::DOM_VK_CONTROL:
  4787     case nsIDOMKeyEvent::DOM_VK_ALT:
  4788       aKeyEvent->PreventDefault(); // consumed
  4789       return NS_OK;
  4790     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
  4791       if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
  4792           nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
  4793         return NS_OK;
  4795       DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
  4796       aKeyEvent->PreventDefault(); // consumed
  4797       return NS_OK;
  4798     case nsIDOMKeyEvent::DOM_VK_DELETE:
  4799       // on certain platforms (such as windows) the shift key
  4800       // modifies what delete does (cmd_cut in this case).
  4801       // bailing here to allow the keybindings to do the cut.
  4802       if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() ||
  4803           nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
  4804           nativeKeyEvent->IsOS()) {
  4805         return NS_OK;
  4807       DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
  4808       aKeyEvent->PreventDefault(); // consumed
  4809       return NS_OK; 
  4811   return NS_OK;
  4814 nsresult
  4815 nsEditor::HandleInlineSpellCheck(EditAction action,
  4816                                    nsISelection *aSelection,
  4817                                    nsIDOMNode *previousSelectedNode,
  4818                                    int32_t previousSelectedOffset,
  4819                                    nsIDOMNode *aStartNode,
  4820                                    int32_t aStartOffset,
  4821                                    nsIDOMNode *aEndNode,
  4822                                    int32_t aEndOffset)
  4824   // Have to cast action here because this method is from an IDL
  4825   return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(
  4826                                  (int32_t)action, aSelection,
  4827                                  previousSelectedNode, previousSelectedOffset,
  4828                                  aStartNode, aStartOffset, aEndNode,
  4829                                  aEndOffset)
  4830                              : NS_OK;
  4833 already_AddRefed<nsIContent>
  4834 nsEditor::FindSelectionRoot(nsINode *aNode)
  4836   nsCOMPtr<nsIContent> rootContent = GetRoot();
  4837   return rootContent.forget();
  4840 nsresult
  4841 nsEditor::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget)
  4843   nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget);
  4844   NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG);
  4845   nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode);
  4846   if (!selectionRootContent) {
  4847     return NS_OK;
  4850   bool isTargetDoc =
  4851     targetNode->NodeType() == nsIDOMNode::DOCUMENT_NODE &&
  4852     targetNode->HasFlag(NODE_IS_EDITABLE);
  4854   nsCOMPtr<nsISelection> selection;
  4855   nsresult rv = GetSelection(getter_AddRefs(selection));
  4856   NS_ENSURE_SUCCESS(rv, rv);
  4858   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
  4859   NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
  4861   nsCOMPtr<nsISelectionController> selCon;
  4862   rv = GetSelectionController(getter_AddRefs(selCon));
  4863   NS_ENSURE_SUCCESS(rv, rv);
  4865   nsCOMPtr<nsISelectionPrivate> selectionPrivate =
  4866     do_QueryInterface(selection);
  4867   NS_ENSURE_TRUE(selectionPrivate, NS_ERROR_UNEXPECTED);
  4869   // Init the caret
  4870   nsRefPtr<nsCaret> caret = presShell->GetCaret();
  4871   NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED);
  4872   caret->SetIgnoreUserModify(false);
  4873   caret->SetCaretDOMSelection(selection);
  4874   selCon->SetCaretReadOnly(IsReadonly());
  4875   selCon->SetCaretEnabled(true);
  4877   // Init selection
  4878   selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
  4879   selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
  4880   selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
  4881   // If the computed selection root isn't root content, we should set it
  4882   // as selection ancestor limit.  However, if that is root element, it means
  4883   // there is not limitation of the selection, then, we must set nullptr.
  4884   // NOTE: If we set a root element to the ancestor limit, some selection
  4885   // methods don't work fine.
  4886   if (selectionRootContent->GetParent()) {
  4887     selectionPrivate->SetAncestorLimiter(selectionRootContent);
  4888   } else {
  4889     selectionPrivate->SetAncestorLimiter(nullptr);
  4892   // XXX What case needs this?
  4893   if (isTargetDoc) {
  4894     int32_t rangeCount;
  4895     selection->GetRangeCount(&rangeCount);
  4896     if (rangeCount == 0) {
  4897       BeginningOfDocument();
  4901   return NS_OK;
  4904 void
  4905 nsEditor::FinalizeSelection()
  4907   nsCOMPtr<nsISelectionController> selCon;
  4908   nsresult rv = GetSelectionController(getter_AddRefs(selCon));
  4909   NS_ENSURE_SUCCESS_VOID(rv);
  4911   nsCOMPtr<nsISelection> selection;
  4912   rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
  4913                             getter_AddRefs(selection));
  4914   NS_ENSURE_SUCCESS_VOID(rv);
  4916   nsCOMPtr<nsISelectionPrivate> selectionPrivate = do_QueryInterface(selection);
  4917   NS_ENSURE_TRUE_VOID(selectionPrivate);
  4919   selectionPrivate->SetAncestorLimiter(nullptr);
  4921   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
  4922   NS_ENSURE_TRUE_VOID(presShell);
  4924   selCon->SetCaretEnabled(false);
  4926   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  4927   NS_ENSURE_TRUE_VOID(fm);
  4928   fm->UpdateCaretForCaretBrowsingMode();
  4930   if (!HasIndependentSelection()) {
  4931     // If this editor doesn't have an independent selection, i.e., it must
  4932     // mean that it is an HTML editor, the selection controller is shared with
  4933     // presShell.  So, even this editor loses focus, other part of the document
  4934     // may still have focus.
  4935     nsCOMPtr<nsIDocument> doc = GetDocument();
  4936     ErrorResult ret;
  4937     if (!doc || !doc->HasFocus(ret)) {
  4938       // If the document already lost focus, mark the selection as disabled.
  4939       selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
  4940     } else {
  4941       // Otherwise, mark selection as normal because outside of a
  4942       // contenteditable element should be selected with normal selection
  4943       // color after here.
  4944       selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
  4946   } else if (IsFormWidget() || IsPasswordEditor() ||
  4947              IsReadonly() || IsDisabled() || IsInputFiltered()) {
  4948     // In <input> or <textarea>, the independent selection should be hidden
  4949     // while this editor doesn't have focus.
  4950     selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
  4951   } else {
  4952     // Otherwise, although we're not sure how this case happens, the
  4953     // independent selection should be marked as disabled.
  4954     selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
  4957   selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
  4960 dom::Element *
  4961 nsEditor::GetRoot()
  4963   if (!mRootElement)
  4965     nsCOMPtr<nsIDOMElement> root;
  4967     // Let GetRootElement() do the work
  4968     GetRootElement(getter_AddRefs(root));
  4971   return mRootElement;
  4974 dom::Element*
  4975 nsEditor::GetEditorRoot()
  4977   return GetRoot();
  4980 Element*
  4981 nsEditor::GetExposedRoot()
  4983   Element* rootElement = GetRoot();
  4985   // For plaintext editors, we need to ask the input/textarea element directly.
  4986   if (rootElement && rootElement->IsRootOfNativeAnonymousSubtree()) {
  4987     rootElement = rootElement->GetParent()->AsElement();
  4990   return rootElement;
  4993 nsresult
  4994 nsEditor::DetermineCurrentDirection()
  4996   // Get the current root direction from its frame
  4997   nsIContent* rootElement = GetExposedRoot();
  4999   // If we don't have an explicit direction, determine our direction
  5000   // from the content's direction
  5001   if (!(mFlags & (nsIPlaintextEditor::eEditorLeftToRight |
  5002                   nsIPlaintextEditor::eEditorRightToLeft))) {
  5004     nsIFrame* frame = rootElement->GetPrimaryFrame();
  5005     NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
  5007     // Set the flag here, to enable us to use the same code path below.
  5008     // It will be flipped before returning from the function.
  5009     if (frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
  5010       mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
  5011     } else {
  5012       mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
  5016   return NS_OK;
  5019 NS_IMETHODIMP
  5020 nsEditor::SwitchTextDirection()
  5022   // Get the current root direction from its frame
  5023   nsIContent* rootElement = GetExposedRoot();
  5025   nsresult rv = DetermineCurrentDirection();
  5026   NS_ENSURE_SUCCESS(rv, rv);
  5028   // Apply the opposite direction
  5029   if (mFlags & nsIPlaintextEditor::eEditorRightToLeft) {
  5030     NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight),
  5031                  "Unexpected mutually exclusive flag");
  5032     mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
  5033     mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
  5034     rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true);
  5035   } else if (mFlags & nsIPlaintextEditor::eEditorLeftToRight) {
  5036     NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft),
  5037                  "Unexpected mutually exclusive flag");
  5038     mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
  5039     mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
  5040     rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true);
  5043   if (NS_SUCCEEDED(rv)) {
  5044     FireInputEvent();
  5047   return rv;
  5050 void
  5051 nsEditor::SwitchTextDirectionTo(uint32_t aDirection)
  5053   // Get the current root direction from its frame
  5054   nsIContent* rootElement = GetExposedRoot();
  5056   nsresult rv = DetermineCurrentDirection();
  5057   NS_ENSURE_SUCCESS_VOID(rv);
  5059   // Apply the requested direction
  5060   if (aDirection == nsIPlaintextEditor::eEditorLeftToRight &&
  5061       (mFlags & nsIPlaintextEditor::eEditorRightToLeft)) {
  5062     NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight),
  5063                  "Unexpected mutually exclusive flag");
  5064     mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
  5065     mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
  5066     rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true);
  5067   } else if (aDirection == nsIPlaintextEditor::eEditorRightToLeft &&
  5068              (mFlags & nsIPlaintextEditor::eEditorLeftToRight)) {
  5069     NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft),
  5070                  "Unexpected mutually exclusive flag");
  5071     mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
  5072     mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
  5073     rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true);
  5076   if (NS_SUCCEEDED(rv)) {
  5077     FireInputEvent();
  5081 #if DEBUG_JOE
  5082 void
  5083 nsEditor::DumpNode(nsIDOMNode *aNode, int32_t indent)
  5085   int32_t i;
  5086   for (i=0; i<indent; i++)
  5087     printf("  ");
  5089   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  5090   nsCOMPtr<nsIDOMDocumentFragment> docfrag = do_QueryInterface(aNode);
  5092   if (element || docfrag)
  5094     if (element)
  5096       nsAutoString tag;
  5097       element->GetTagName(tag);
  5098       printf("<%s>\n", NS_LossyConvertUTF16toASCII(tag).get());
  5100     else
  5102       printf("<document fragment>\n");
  5104     nsCOMPtr<nsIDOMNodeList> childList;
  5105     aNode->GetChildNodes(getter_AddRefs(childList));
  5106     NS_ENSURE_TRUE(childList, NS_ERROR_NULL_POINTER);
  5107     uint32_t numChildren;
  5108     childList->GetLength(&numChildren);
  5109     nsCOMPtr<nsIDOMNode> child, tmp;
  5110     aNode->GetFirstChild(getter_AddRefs(child));
  5111     for (i=0; i<numChildren; i++)
  5113       DumpNode(child, indent+1);
  5114       child->GetNextSibling(getter_AddRefs(tmp));
  5115       child = tmp;
  5118   else if (IsTextNode(aNode))
  5120     nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aNode);
  5121     nsAutoString str;
  5122     textNode->GetData(str);
  5123     nsAutoCString cstr;
  5124     LossyCopyUTF16toASCII(str, cstr);
  5125     cstr.ReplaceChar('\n', ' ');
  5126     printf("<textnode> %s\n", cstr.get());
  5129 #endif
  5131 bool
  5132 nsEditor::IsModifiableNode(nsIDOMNode *aNode)
  5134   return true;
  5137 bool
  5138 nsEditor::IsModifiableNode(nsINode *aNode)
  5140   return true;
  5143 already_AddRefed<nsIContent>
  5144 nsEditor::GetFocusedContent()
  5146   nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
  5147   if (!piTarget) {
  5148     return nullptr;
  5151   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  5152   NS_ENSURE_TRUE(fm, nullptr);
  5154   nsCOMPtr<nsIContent> content = fm->GetFocusedContent();
  5155   return SameCOMIdentity(content, piTarget) ? content.forget() : nullptr;
  5158 already_AddRefed<nsIContent>
  5159 nsEditor::GetFocusedContentForIME()
  5161   return GetFocusedContent();
  5164 bool
  5165 nsEditor::IsActiveInDOMWindow()
  5167   nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
  5168   if (!piTarget) {
  5169     return false;
  5172   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  5173   NS_ENSURE_TRUE(fm, false);
  5175   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
  5176   nsPIDOMWindow* ourWindow = doc->GetWindow();
  5177   nsCOMPtr<nsPIDOMWindow> win;
  5178   nsIContent* content =
  5179     nsFocusManager::GetFocusedDescendant(ourWindow, false,
  5180                                          getter_AddRefs(win));
  5181   return SameCOMIdentity(content, piTarget);
  5184 bool
  5185 nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
  5187   // If the event is trusted, the event should always cause input.
  5188   NS_ENSURE_TRUE(aEvent, false);
  5190   // If this is mouse event but this editor doesn't have focus, we shouldn't
  5191   // handle it.
  5192   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
  5193   if (mouseEvent) {
  5194     nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
  5195     if (!focusedContent) {
  5196       return false;
  5198   } else {
  5199     nsAutoString eventType;
  5200     aEvent->GetType(eventType);
  5201     // If composition event or text event isn't dispatched via widget,
  5202     // we need to ignore them since they cannot be managed by TextComposition.
  5203     // E.g., the event was created by chrome JS.
  5204     // Note that if we allow to handle such events, editor may be confused by
  5205     // strange event order.
  5206     if (eventType.EqualsLiteral("text") ||
  5207         eventType.EqualsLiteral("compositionstart") ||
  5208         eventType.EqualsLiteral("compositionend")) {
  5209       WidgetGUIEvent* widgetGUIEvent =
  5210         aEvent->GetInternalNSEvent()->AsGUIEvent();
  5211       if (!widgetGUIEvent || !widgetGUIEvent->widget) {
  5212         return false;
  5217   bool isTrusted;
  5218   nsresult rv = aEvent->GetIsTrusted(&isTrusted);
  5219   NS_ENSURE_SUCCESS(rv, false);
  5220   if (isTrusted) {
  5221     return true;
  5224   // Ignore untrusted mouse event.
  5225   // XXX Why are we handling other untrusted input events?
  5226   if (mouseEvent) {
  5227     return false;
  5230   // Otherwise, we shouldn't handle any input events when we're not an active
  5231   // element of the DOM window.
  5232   return IsActiveInDOMWindow();
  5235 void
  5236 nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget)
  5238   InitializeSelection(aFocusEventTarget);
  5239   if (mInlineSpellChecker) {
  5240     mInlineSpellChecker->UpdateCurrentDictionary();
  5244 NS_IMETHODIMP
  5245 nsEditor::GetSuppressDispatchingInputEvent(bool *aSuppressed)
  5247   NS_ENSURE_ARG_POINTER(aSuppressed);
  5248   *aSuppressed = !mDispatchInputEvent;
  5249   return NS_OK;
  5252 NS_IMETHODIMP
  5253 nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress)
  5255   mDispatchInputEvent = !aSuppress;
  5256   return NS_OK;

mercurial