LCOV - code coverage report
Current view: top level - editor/libeditor - EditorBase.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 102 2049 5.0 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #include "mozilla/EditorBase.h"
       7             : 
       8             : #include "mozilla/DebugOnly.h"          // for DebugOnly
       9             : #include "mozilla/Encoding.h"           // for Encoding
      10             : 
      11             : #include <stdio.h>                      // for nullptr, stdout
      12             : #include <string.h>                     // for strcmp
      13             : 
      14             : #include "ChangeAttributeTransaction.h" // for ChangeAttributeTransaction
      15             : #include "CompositionTransaction.h"     // for CompositionTransaction
      16             : #include "CreateElementTransaction.h"   // for CreateElementTransaction
      17             : #include "DeleteNodeTransaction.h"      // for DeleteNodeTransaction
      18             : #include "DeleteRangeTransaction.h"     // for DeleteRangeTransaction
      19             : #include "DeleteTextTransaction.h"      // for DeleteTextTransaction
      20             : #include "EditAggregateTransaction.h"   // for EditAggregateTransaction
      21             : #include "EditorEventListener.h"        // for EditorEventListener
      22             : #include "HTMLEditRules.h"              // for HTMLEditRules
      23             : #include "InsertNodeTransaction.h"      // for InsertNodeTransaction
      24             : #include "InsertTextTransaction.h"      // for InsertTextTransaction
      25             : #include "JoinNodeTransaction.h"        // for JoinNodeTransaction
      26             : #include "PlaceholderTransaction.h"     // for PlaceholderTransaction
      27             : #include "SplitNodeTransaction.h"       // for SplitNodeTransaction
      28             : #include "StyleSheetTransactions.h"     // for AddStyleSheetTransaction, etc.
      29             : #include "TextEditUtils.h"              // for TextEditUtils
      30             : #include "mozilla/CheckedInt.h"         // for CheckedInt
      31             : #include "mozilla/ComputedStyle.h"      // for ComputedStyle
      32             : #include "mozilla/EditAction.h"         // for EditSubAction
      33             : #include "mozilla/EditorDOMPoint.h"     // for EditorDOMPoint
      34             : #include "mozilla/EditorSpellCheck.h"   // for EditorSpellCheck
      35             : #include "mozilla/EditorUtils.h"        // for various helper classes.
      36             : #include "mozilla/EditTransactionBase.h" // for EditTransactionBase
      37             : #include "mozilla/FlushType.h"          // for FlushType::Frames
      38             : #include "mozilla/IMEContentObserver.h" // for IMEContentObserver
      39             : #include "mozilla/IMEStateManager.h"    // for IMEStateManager
      40             : #include "mozilla/mozalloc.h"           // for operator new, etc.
      41             : #include "mozilla/mozInlineSpellChecker.h" // for mozInlineSpellChecker
      42             : #include "mozilla/mozSpellChecker.h"    // for mozSpellChecker
      43             : #include "mozilla/Preferences.h"        // for Preferences
      44             : #include "mozilla/RangeBoundary.h"      // for RawRangeBoundary, RangeBoundary
      45             : #include "mozilla/dom/Selection.h"      // for Selection, etc.
      46             : #include "mozilla/Services.h"           // for GetObserverService
      47             : #include "mozilla/TextComposition.h"    // for TextComposition
      48             : #include "mozilla/TextInputListener.h"  // for TextInputListener
      49             : #include "mozilla/TextServicesDocument.h" // for TextServicesDocument
      50             : #include "mozilla/TextEvents.h"
      51             : #include "mozilla/TransactionManager.h" // for TransactionManager
      52             : #include "mozilla/dom/CharacterData.h"  // for CharacterData
      53             : #include "mozilla/dom/Element.h"        // for Element, nsINode::AsElement
      54             : #include "mozilla/dom/EventTarget.h"    // for EventTarget
      55             : #include "mozilla/dom/HTMLBodyElement.h"
      56             : #include "mozilla/dom/Text.h"
      57             : #include "mozilla/dom/Event.h"
      58             : #include "nsAString.h"                  // for nsAString::Length, etc.
      59             : #include "nsCCUncollectableMarker.h"    // for nsCCUncollectableMarker
      60             : #include "nsCaret.h"                    // for nsCaret
      61             : #include "nsCaseTreatment.h"
      62             : #include "nsCharTraits.h"               // for NS_IS_HIGH_SURROGATE, etc.
      63             : #include "nsComponentManagerUtils.h"    // for do_CreateInstance
      64             : #include "nsComputedDOMStyle.h"         // for nsComputedDOMStyle
      65             : #include "nsContentUtils.h"             // for nsContentUtils
      66             : #include "nsDOMString.h"                // for DOMStringIsNull
      67             : #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc.
      68             : #include "nsError.h"                    // for NS_OK, etc.
      69             : #include "nsFocusManager.h"             // for nsFocusManager
      70             : #include "nsFrameSelection.h"           // for nsFrameSelection
      71             : #include "nsGenericHTMLElement.h"       // for nsGenericHTMLElement
      72             : #include "nsGkAtoms.h"                  // for nsGkAtoms, nsGkAtoms::dir
      73             : #include "nsIAbsorbingTransaction.h"    // for nsIAbsorbingTransaction
      74             : #include "nsAtom.h"                     // for nsAtom
      75             : #include "nsIContent.h"                 // for nsIContent
      76             : #include "nsIDocument.h"                // for nsIDocument
      77             : #include "nsIDOMEventListener.h"        // for nsIDOMEventListener
      78             : #include "nsIDocumentStateListener.h"   // for nsIDocumentStateListener
      79             : #include "nsIEditActionListener.h"      // for nsIEditActionListener
      80             : #include "nsIEditorObserver.h"          // for nsIEditorObserver
      81             : #include "nsIEditorSpellCheck.h"        // for nsIEditorSpellCheck
      82             : #include "nsIFrame.h"                   // for nsIFrame
      83             : #include "nsIHTMLDocument.h"            // for nsIHTMLDocument
      84             : #include "nsIInlineSpellChecker.h"      // for nsIInlineSpellChecker, etc.
      85             : #include "nsNameSpaceManager.h"        // for kNameSpaceID_None, etc.
      86             : #include "nsINode.h"                    // for nsINode, etc.
      87             : #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc.
      88             : #include "nsIPresShell.h"               // for nsIPresShell
      89             : #include "nsISelectionController.h"     // for nsISelectionController, etc.
      90             : #include "nsISelectionDisplay.h"        // for nsISelectionDisplay, etc.
      91             : #include "nsISupportsBase.h"            // for nsISupports
      92             : #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_IF_ADDREF
      93             : #include "nsITransaction.h"             // for nsITransaction
      94             : #include "nsITransactionManager.h"
      95             : #include "nsIWeakReference.h"           // for nsISupportsWeakReference
      96             : #include "nsIWidget.h"                  // for nsIWidget, IMEState, etc.
      97             : #include "nsPIDOMWindow.h"              // for nsPIDOMWindow
      98             : #include "nsPresContext.h"              // for nsPresContext
      99             : #include "nsRange.h"                    // for nsRange
     100             : #include "nsReadableUtils.h"            // for EmptyString, ToNewCString
     101             : #include "nsString.h"                   // for nsAutoString, nsString, etc.
     102             : #include "nsStringFwd.h"                // for nsString
     103             : #include "nsStyleConsts.h"              // for NS_STYLE_DIRECTION_RTL, etc.
     104             : #include "nsStyleStruct.h"              // for nsStyleDisplay, nsStyleText, etc.
     105             : #include "nsStyleStructFwd.h"           // for nsIFrame::StyleUIReset, etc.
     106             : #include "nsTextNode.h"                 // for nsTextNode
     107             : #include "nsThreadUtils.h"              // for nsRunnable
     108             : #include "prtime.h"                     // for PR_Now
     109             : 
     110             : class nsIOutputStream;
     111             : class nsITransferable;
     112             : 
     113             : namespace mozilla {
     114             : 
     115             : using namespace dom;
     116             : using namespace widget;
     117             : 
     118             : /*****************************************************************************
     119             :  * mozilla::EditorBase
     120             :  *****************************************************************************/
     121             : 
     122             : template already_AddRefed<Element>
     123             : EditorBase::CreateNodeWithTransaction(nsAtom& aTag,
     124             :                                       const EditorDOMPoint& aPointToInsert);
     125             : template already_AddRefed<Element>
     126             : EditorBase::CreateNodeWithTransaction(nsAtom& aTag,
     127             :                                       const EditorRawDOMPoint& aPointToInsert);
     128             : template nsresult
     129             : EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert,
     130             :                                       const EditorDOMPoint& aPointToInsert);
     131             : template nsresult
     132             : EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert,
     133             :                                       const EditorRawDOMPoint& aPointToInsert);
     134             : template already_AddRefed<nsIContent>
     135             : EditorBase::SplitNodeWithTransaction(const EditorDOMPoint& aStartOfRightNode,
     136             :                                      ErrorResult& aError);
     137             : template already_AddRefed<nsIContent>
     138             : EditorBase::SplitNodeWithTransaction(const EditorRawDOMPoint& aStartOfRightNode,
     139             :                                      ErrorResult& aError);
     140             : template SplitNodeResult
     141             : EditorBase::SplitNodeDeepWithTransaction(
     142             :               nsIContent& aMostAncestorToSplit,
     143             :               const EditorDOMPoint& aStartOfDeepestRightNode,
     144             :               SplitAtEdges aSplitAtEdges);
     145             : template SplitNodeResult
     146             : EditorBase::SplitNodeDeepWithTransaction(
     147             :               nsIContent& aMostAncestorToSplit,
     148             :               const EditorRawDOMPoint& aStartOfDeepestRightNode,
     149             :               SplitAtEdges aSplitAtEdges);
     150             : template nsresult
     151             : EditorBase::MoveNodeWithTransaction(nsIContent& aContent,
     152             :                                     const EditorDOMPoint& aPointToInsert);
     153             : template nsresult
     154             : EditorBase::MoveNodeWithTransaction(nsIContent& aContent,
     155             :                                     const EditorRawDOMPoint& aPointToInsert);
     156             : 
     157           0 : EditorBase::EditorBase()
     158             :   : mPlaceholderName(nullptr)
     159             :   , mModCount(0)
     160             :   , mFlags(0)
     161             :   , mUpdateCount(0)
     162             :   , mPlaceholderBatch(0)
     163             :   , mTopLevelEditSubAction(EditSubAction::eNone)
     164             :   , mDirection(eNone)
     165             :   , mDocDirtyState(-1)
     166             :   , mSpellcheckCheckboxState(eTriUnset)
     167             :   , mShouldTxnSetSelection(true)
     168             :   , mDidPreDestroy(false)
     169             :   , mDidPostCreate(false)
     170             :   , mDispatchInputEvent(true)
     171             :   , mIsInEditSubAction(false)
     172             :   , mHidingCaret(false)
     173             :   , mSpellCheckerDictionaryUpdated(true)
     174           0 :   , mIsHTMLEditorClass(false)
     175             : {
     176           0 : }
     177             : 
     178           0 : EditorBase::~EditorBase()
     179             : {
     180           0 :   MOZ_ASSERT(!IsInitialized() || mDidPreDestroy,
     181             :              "Why PreDestroy hasn't been called?");
     182             : 
     183           0 :   if (mComposition) {
     184           0 :     mComposition->OnEditorDestroyed();
     185           0 :     mComposition = nullptr;
     186             :   }
     187             :   // If this editor is still hiding the caret, we need to restore it.
     188           0 :   HideCaret(false);
     189           0 :   mTransactionManager = nullptr;
     190           0 : }
     191             : 
     192             : NS_IMPL_CYCLE_COLLECTION_CLASS(EditorBase)
     193             : 
     194           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase)
     195           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
     196           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionController)
     197           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
     198           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMEContentObserver)
     199           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
     200           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextServicesDocument)
     201           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextInputListener)
     202           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransactionManager)
     203           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
     204           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers)
     205           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
     206           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
     207             : 
     208           0 :  if (tmp->mEventListener) {
     209           0 :    tmp->mEventListener->Disconnect();
     210           0 :    tmp->mEventListener = nullptr;
     211             :  }
     212             : 
     213           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderTransaction)
     214           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSavedSel);
     215           0 :  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRangeUpdater);
     216           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     217             : 
     218           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase)
     219             :  nsIDocument* currentDoc =
     220           0 :    tmp->mRootElement ? tmp->mRootElement->GetUncomposedDoc() : nullptr;
     221           0 :  if (currentDoc &&
     222           0 :      nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
     223             :    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
     224             :  }
     225           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
     226           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionController)
     227           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
     228           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
     229           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
     230           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextServicesDocument)
     231           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextInputListener)
     232           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransactionManager)
     233           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
     234           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers)
     235           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
     236           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
     237           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
     238           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderTransaction)
     239           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSavedSel);
     240           0 :  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRangeUpdater);
     241           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     242             : 
     243           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditorBase)
     244           0 :  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
     245           0 :  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     246           0 :  NS_INTERFACE_MAP_ENTRY(nsIEditor)
     247           0 :  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
     248           0 : NS_INTERFACE_MAP_END
     249             : 
     250           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(EditorBase)
     251           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase)
     252             : 
     253             : 
     254             : nsresult
     255           0 : EditorBase::Init(nsIDocument& aDocument,
     256             :                  Element* aRoot,
     257             :                  nsISelectionController* aSelectionController,
     258             :                  uint32_t aFlags,
     259             :                  const nsAString& aValue)
     260             : {
     261           0 :   MOZ_ASSERT(mTopLevelEditSubAction == EditSubAction::eNone,
     262             :              "Initializing during an edit action is an error");
     263             : 
     264             :   // First only set flags, but other stuff shouldn't be initialized now.
     265             :   // Don't move this call after initializing mDocument.
     266             :   // SetFlags() can check whether it's called during initialization or not by
     267             :   // them.  Note that SetFlags() will be called by PostCreate().
     268             : #ifdef DEBUG
     269             :   nsresult rv =
     270             : #endif
     271           0 :   SetFlags(aFlags);
     272           0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
     273             : 
     274           0 :   mDocument = &aDocument;
     275             :   // HTML editors currently don't have their own selection controller,
     276             :   // so they'll pass null as aSelCon, and we'll get the selection controller
     277             :   // off of the presshell.
     278           0 :   nsCOMPtr<nsISelectionController> selectionController;
     279           0 :   if (aSelectionController) {
     280           0 :     mSelectionController = aSelectionController;
     281           0 :     selectionController = aSelectionController;
     282             :   } else {
     283           0 :     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     284           0 :     selectionController = do_QueryInterface(presShell);
     285             :   }
     286           0 :   MOZ_ASSERT(selectionController,
     287             :              "Selection controller should be available at this point");
     288             : 
     289             :   //set up root element if we are passed one.
     290           0 :   if (aRoot) {
     291           0 :     mRootElement = aRoot;
     292             :   }
     293             : 
     294           0 :   mUpdateCount=0;
     295             : 
     296             :   // If this is an editor for <input> or <textarea>, the text node which
     297             :   // has composition string is always recreated with same content. Therefore,
     298             :   // we need to nodify mComposition of text node destruction and replacing
     299             :   // composing string when this receives eCompositionChange event next time.
     300           0 :   if (mComposition &&
     301           0 :       mComposition->GetContainerTextNode() &&
     302           0 :       !mComposition->GetContainerTextNode()->IsInComposedDoc()) {
     303           0 :     mComposition->OnTextNodeRemoved();
     304             :   }
     305             : 
     306             :   // Show the caret.
     307           0 :   selectionController->SetCaretReadOnly(false);
     308           0 :   selectionController->SetDisplaySelection(
     309           0 :                          nsISelectionController::SELECTION_ON);
     310             :   // Show all the selection reflected to user.
     311           0 :   selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
     312             : 
     313           0 :   MOZ_ASSERT(IsInitialized());
     314             : 
     315           0 :   Selection* selection = GetSelection();
     316           0 :   if (selection) {
     317           0 :     selection->AddSelectionListener(this);
     318             :   }
     319             : 
     320             :   // Make sure that the editor will be destroyed properly
     321           0 :   mDidPreDestroy = false;
     322             :   // Make sure that the ediotr will be created properly
     323           0 :   mDidPostCreate = false;
     324             : 
     325           0 :   return NS_OK;
     326             : }
     327             : 
     328             : nsresult
     329           0 : EditorBase::PostCreate()
     330             : {
     331             :   // Synchronize some stuff for the flags.  SetFlags() will initialize
     332             :   // something by the flag difference.  This is first time of that, so, all
     333             :   // initializations must be run.  For such reason, we need to invert mFlags
     334             :   // value first.
     335           0 :   mFlags = ~mFlags;
     336           0 :   nsresult rv = SetFlags(~mFlags);
     337           0 :   NS_ENSURE_SUCCESS(rv, rv);
     338             : 
     339             :   // These operations only need to happen on the first PostCreate call
     340           0 :   if (!mDidPostCreate) {
     341           0 :     mDidPostCreate = true;
     342             : 
     343             :     // Set up listeners
     344           0 :     CreateEventListeners();
     345           0 :     rv = InstallEventListeners();
     346           0 :     NS_ENSURE_SUCCESS(rv, rv);
     347             : 
     348             :     // nuke the modification count, so the doc appears unmodified
     349             :     // do this before we notify listeners
     350           0 :     ResetModificationCount();
     351             : 
     352             :     // update the UI with our state
     353           0 :     NotifyDocumentListeners(eDocumentCreated);
     354           0 :     NotifyDocumentListeners(eDocumentStateChanged);
     355             :   }
     356             : 
     357             :   // update nsTextStateManager and caret if we have focus
     358           0 :   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
     359           0 :   if (focusedContent) {
     360           0 :     InitializeSelection(focusedContent);
     361             : 
     362             :     // If the text control gets reframed during focus, Focus() would not be
     363             :     // called, so take a chance here to see if we need to spell check the text
     364             :     // control.
     365           0 :     mEventListener->SpellCheckIfNeeded();
     366             : 
     367           0 :     IMEState newState;
     368           0 :     rv = GetPreferredIMEState(&newState);
     369           0 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     370             :     // May be null in design mode
     371           0 :     nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
     372           0 :     IMEStateManager::UpdateIMEState(newState, content, this);
     373             :   }
     374             : 
     375             :   // FYI: This call might cause destroying this editor.
     376           0 :   IMEStateManager::OnEditorInitialized(*this);
     377             : 
     378           0 :   return NS_OK;
     379             : }
     380             : 
     381             : void
     382           0 : EditorBase::SetTextInputListener(TextInputListener* aTextInputListener)
     383             : {
     384           0 :   MOZ_ASSERT(!mTextInputListener || !aTextInputListener ||
     385             :              mTextInputListener == aTextInputListener);
     386           0 :   mTextInputListener = aTextInputListener;
     387           0 : }
     388             : 
     389             : void
     390           0 : EditorBase::SetIMEContentObserver(IMEContentObserver* aIMEContentObserver)
     391             : {
     392           0 :   MOZ_ASSERT(!mIMEContentObserver || !aIMEContentObserver ||
     393             :              mIMEContentObserver == aIMEContentObserver);
     394           0 :   mIMEContentObserver = aIMEContentObserver;
     395           0 : }
     396             : 
     397             : void
     398           0 : EditorBase::CreateEventListeners()
     399             : {
     400             :   // Don't create the handler twice
     401           0 :   if (!mEventListener) {
     402           0 :     mEventListener = new EditorEventListener();
     403             :   }
     404           0 : }
     405             : 
     406             : nsresult
     407           0 : EditorBase::InstallEventListeners()
     408             : {
     409           0 :   if (NS_WARN_IF(!IsInitialized()) || NS_WARN_IF(!mEventListener)) {
     410             :     return NS_ERROR_NOT_INITIALIZED;
     411             :   }
     412             : 
     413             :   // Initialize the event target.
     414           0 :   nsCOMPtr<nsIContent> rootContent = GetRoot();
     415           0 :   NS_ENSURE_TRUE(rootContent, NS_ERROR_NOT_AVAILABLE);
     416           0 :   mEventTarget = do_QueryInterface(rootContent->GetParent());
     417           0 :   NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_AVAILABLE);
     418             : 
     419           0 :   nsresult rv = mEventListener->Connect(this);
     420           0 :   if (mComposition) {
     421             :     // Restart to handle composition with new editor contents.
     422           0 :     mComposition->StartHandlingComposition(this);
     423             :   }
     424             :   return rv;
     425             : }
     426             : 
     427             : void
     428           0 : EditorBase::RemoveEventListeners()
     429             : {
     430           0 :   if (!IsInitialized() || !mEventListener) {
     431             :     return;
     432             :   }
     433           0 :   mEventListener->Disconnect();
     434           0 :   if (mComposition) {
     435             :     // Even if this is called, don't release mComposition because this is
     436             :     // may be reused after reframing.
     437           0 :     mComposition->EndHandlingComposition(this);
     438             :   }
     439           0 :   mEventTarget = nullptr;
     440             : }
     441             : 
     442             : bool
     443           0 : EditorBase::GetDesiredSpellCheckState()
     444             : {
     445             :   // Check user override on this element
     446           0 :   if (mSpellcheckCheckboxState != eTriUnset) {
     447           0 :     return (mSpellcheckCheckboxState == eTriTrue);
     448             :   }
     449             : 
     450             :   // Check user preferences
     451           0 :   int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
     452             : 
     453           0 :   if (!spellcheckLevel) {
     454             :     return false;                    // Spellchecking forced off globally
     455             :   }
     456             : 
     457           0 :   if (!CanEnableSpellCheck()) {
     458             :     return false;
     459             :   }
     460             : 
     461           0 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     462           0 :   if (presShell) {
     463           0 :     nsPresContext* context = presShell->GetPresContext();
     464           0 :     if (context && !context->IsDynamic()) {
     465             :       return false;
     466             :     }
     467             :   }
     468             : 
     469             :   // Check DOM state
     470           0 :   nsCOMPtr<nsIContent> content = GetExposedRoot();
     471           0 :   if (!content) {
     472             :     return false;
     473             :   }
     474             : 
     475           0 :   auto element = nsGenericHTMLElement::FromNode(content);
     476           0 :   if (!element) {
     477             :     return false;
     478             :   }
     479             : 
     480           0 :   if (!IsPlaintextEditor()) {
     481             :     // Some of the page content might be editable and some not, if spellcheck=
     482             :     // is explicitly set anywhere, so if there's anything editable on the page,
     483             :     // return true and let the spellchecker figure it out.
     484           0 :     nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetComposedDoc());
     485           0 :     return doc && doc->IsEditingOn();
     486             :   }
     487             : 
     488           0 :   return element->Spellcheck();
     489             : }
     490             : 
     491             : void
     492           0 : EditorBase::PreDestroy(bool aDestroyingFrames)
     493             : {
     494           0 :   if (mDidPreDestroy) {
     495             :     return;
     496             :   }
     497             : 
     498           0 :   Selection* selection = GetSelection();
     499           0 :   if (selection) {
     500           0 :     selection->RemoveSelectionListener(this);
     501             :   }
     502             : 
     503           0 :   IMEStateManager::OnEditorDestroying(*this);
     504             : 
     505             :   // Let spellchecker clean up its observers etc. It is important not to
     506             :   // actually free the spellchecker here, since the spellchecker could have
     507             :   // caused flush notifications, which could have gotten here if a textbox
     508             :   // is being removed. Setting the spellchecker to nullptr could free the
     509             :   // object that is still in use! It will be freed when the editor is
     510             :   // destroyed.
     511           0 :   if (mInlineSpellChecker)
     512           0 :     mInlineSpellChecker->Cleanup(aDestroyingFrames);
     513             : 
     514             :   // tell our listeners that the doc is going away
     515           0 :   NotifyDocumentListeners(eDocumentToBeDestroyed);
     516             : 
     517             :   // Unregister event listeners
     518           0 :   RemoveEventListeners();
     519             :   // If this editor is still hiding the caret, we need to restore it.
     520           0 :   HideCaret(false);
     521           0 :   mActionListeners.Clear();
     522           0 :   mEditorObservers.Clear();
     523           0 :   mDocStateListeners.Clear();
     524           0 :   mInlineSpellChecker = nullptr;
     525           0 :   mTextServicesDocument = nullptr;
     526           0 :   mTextInputListener = nullptr;
     527           0 :   mSpellcheckCheckboxState = eTriUnset;
     528           0 :   mRootElement = nullptr;
     529             : 
     530             :   // Transaction may grab this instance.  Therefore, they should be released
     531             :   // here for stopping the circular reference with this instance.
     532           0 :   if (mTransactionManager) {
     533           0 :     DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
     534           0 :     NS_WARNING_ASSERTION(disabledUndoRedo,
     535             :       "Failed to disable undo/redo transactions");
     536           0 :     mTransactionManager = nullptr;
     537             :   }
     538             : 
     539           0 :   mDidPreDestroy = true;
     540             : }
     541             : 
     542             : NS_IMETHODIMP
     543           0 : EditorBase::GetFlags(uint32_t* aFlags)
     544             : {
     545             :   // NOTE: If you need to override this method, you need to make Flags()
     546             :   //       virtual.
     547           0 :   *aFlags = Flags();
     548           0 :   return NS_OK;
     549             : }
     550             : 
     551             : NS_IMETHODIMP
     552           0 : EditorBase::SetFlags(uint32_t aFlags)
     553             : {
     554           0 :   if (mFlags == aFlags) {
     555             :     return NS_OK;
     556             :   }
     557             : 
     558           0 :   bool spellcheckerWasEnabled = CanEnableSpellCheck();
     559           0 :   mFlags = aFlags;
     560             : 
     561           0 :   if (!IsInitialized()) {
     562             :     // If we're initializing, we shouldn't do anything now.
     563             :     // SetFlags() will be called by PostCreate(),
     564             :     // we should synchronize some stuff for the flags at that time.
     565             :     return NS_OK;
     566             :   }
     567             : 
     568             :   // The flag change may cause the spellchecker state change
     569           0 :   if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
     570           0 :     SyncRealTimeSpell();
     571             :   }
     572             : 
     573             :   // If this is called from PostCreate(), it will update the IME state if it's
     574             :   // necessary.
     575           0 :   if (!mDidPostCreate) {
     576             :     return NS_OK;
     577             :   }
     578             : 
     579             :   // Might be changing editable state, so, we need to reset current IME state
     580             :   // if we're focused and the flag change causes IME state change.
     581           0 :   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
     582           0 :   if (focusedContent) {
     583           0 :     IMEState newState;
     584           0 :     nsresult rv = GetPreferredIMEState(&newState);
     585           0 :     if (NS_SUCCEEDED(rv)) {
     586             :       // NOTE: When the enabled state isn't going to be modified, this method
     587             :       // is going to do nothing.
     588           0 :       nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
     589           0 :       IMEStateManager::UpdateIMEState(newState, content, this);
     590             :     }
     591             :   }
     592             : 
     593             :   return NS_OK;
     594             : }
     595             : 
     596             : NS_IMETHODIMP
     597           0 : EditorBase::GetIsSelectionEditable(bool* aIsSelectionEditable)
     598             : {
     599           0 :   NS_ENSURE_ARG_POINTER(aIsSelectionEditable);
     600           0 :   *aIsSelectionEditable = IsSelectionEditable();
     601           0 :   return NS_OK;
     602             : }
     603             : 
     604             : bool
     605           0 : EditorBase::IsSelectionEditable()
     606             : {
     607             :   // get current selection
     608           0 :   RefPtr<Selection> selection = GetSelection();
     609           0 :   if (NS_WARN_IF(!selection)) {
     610             :     return false;
     611             :   }
     612             : 
     613           0 :   if (!mIsHTMLEditorClass) {
     614             :     // XXX we just check that the anchor node is editable at the moment
     615             :     //     we should check that all nodes in the selection are editable
     616           0 :     nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
     617           0 :     return anchorNode && IsEditable(anchorNode);
     618             :   }
     619             : 
     620           0 :   nsINode* anchorNode = selection->GetAnchorNode();
     621           0 :   nsINode* focusNode = selection->GetFocusNode();
     622           0 :   if (!anchorNode || !focusNode) {
     623             :     return false;
     624             :   }
     625             : 
     626             :   // Per the editing spec as of June 2012: we have to have a selection whose
     627             :   // start and end nodes are editable, and which share an ancestor editing
     628             :   // host.  (Bug 766387.)
     629           0 :   bool isSelectionEditable = selection->RangeCount() &&
     630           0 :                              anchorNode->IsEditable() &&
     631           0 :                              focusNode->IsEditable();
     632           0 :   if (!isSelectionEditable) {
     633             :     return false;
     634             :   }
     635             : 
     636             :   nsINode* commonAncestor =
     637           0 :     selection->GetAnchorFocusRange()->GetCommonAncestor();
     638           0 :   while (commonAncestor && !commonAncestor->IsEditable()) {
     639           0 :     commonAncestor = commonAncestor->GetParentNode();
     640             :   }
     641             :   // If there is no editable common ancestor, return false.
     642           0 :   return !!commonAncestor;
     643             : }
     644             : 
     645             : NS_IMETHODIMP
     646           0 : EditorBase::GetIsDocumentEditable(bool* aIsDocumentEditable)
     647             : {
     648           0 :   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
     649           0 :   nsCOMPtr<nsIDocument> doc = GetDocument();
     650           0 :   *aIsDocumentEditable = doc && IsModifiable();
     651             : 
     652             :   return NS_OK;
     653             : }
     654             : 
     655             : NS_IMETHODIMP
     656           0 : EditorBase::GetDocument(nsIDocument** aDoc)
     657             : {
     658           0 :   NS_IF_ADDREF(*aDoc = mDocument);
     659           0 :   return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED;
     660             : }
     661             : 
     662             : already_AddRefed<nsIWidget>
     663           0 : EditorBase::GetWidget()
     664             : {
     665           0 :   nsCOMPtr<nsIPresShell> ps = GetPresShell();
     666           0 :   NS_ENSURE_TRUE(ps, nullptr);
     667           0 :   nsPresContext* pc = ps->GetPresContext();
     668           0 :   NS_ENSURE_TRUE(pc, nullptr);
     669           0 :   nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
     670           0 :   NS_ENSURE_TRUE(widget.get(), nullptr);
     671           0 :   return widget.forget();
     672             : }
     673             : 
     674             : NS_IMETHODIMP
     675           0 : EditorBase::GetContentsMIMEType(char** aContentsMIMEType)
     676             : {
     677           0 :   NS_ENSURE_ARG_POINTER(aContentsMIMEType);
     678           0 :   *aContentsMIMEType = ToNewCString(mContentMIMEType);
     679           0 :   return NS_OK;
     680             : }
     681             : 
     682             : NS_IMETHODIMP
     683           0 : EditorBase::SetContentsMIMEType(const char* aContentsMIMEType)
     684             : {
     685           0 :   mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : "");
     686           0 :   return NS_OK;
     687             : }
     688             : 
     689             : NS_IMETHODIMP
     690           0 : EditorBase::GetSelectionController(nsISelectionController** aSel)
     691             : {
     692           0 :   NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
     693           0 :   *aSel = nullptr; // init out param
     694           0 :   nsCOMPtr<nsISelectionController> selCon = GetSelectionController();
     695           0 :   if (NS_WARN_IF(!selCon)) {
     696             :     return NS_ERROR_NOT_INITIALIZED;
     697             :   }
     698           0 :   selCon.forget(aSel);
     699           0 :   return NS_OK;
     700             : }
     701             : 
     702             : NS_IMETHODIMP
     703           0 : EditorBase::DeleteSelection(EDirection aAction,
     704             :                             EStripWrappers aStripWrappers)
     705             : {
     706           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     707             : }
     708             : 
     709             : NS_IMETHODIMP
     710           0 : EditorBase::GetSelection(Selection** aSelection)
     711             : {
     712           0 :   return GetSelection(SelectionType::eNormal, aSelection);
     713             : }
     714             : 
     715             : nsresult
     716           0 : EditorBase::GetSelection(SelectionType aSelectionType,
     717             :                          Selection** aSelection)
     718             : {
     719           0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
     720           0 :   *aSelection = nullptr;
     721           0 :   nsISelectionController* selcon = GetSelectionController();
     722           0 :   if (!selcon) {
     723             :     return NS_ERROR_NOT_INITIALIZED;
     724             :   }
     725             :   RefPtr<Selection> selection =
     726           0 :     selcon->GetSelection(ToRawSelectionType(aSelectionType));
     727           0 :   if (!selection) {
     728             :     return NS_ERROR_INVALID_ARG;
     729             :   }
     730           0 :   selection.forget(aSelection);
     731           0 :   return NS_OK;
     732             : }
     733             : 
     734             : NS_IMETHODIMP
     735           0 : EditorBase::DoTransaction(nsITransaction* aTxn)
     736             : {
     737           0 :   return DoTransaction(nullptr, aTxn);
     738             : }
     739             : 
     740             : nsresult
     741           0 : EditorBase::DoTransaction(Selection* aSelection, nsITransaction* aTxn)
     742             : {
     743           0 :   if (mPlaceholderBatch && !mPlaceholderTransaction) {
     744             :     mPlaceholderTransaction =
     745           0 :       PlaceholderTransaction::Create(*this, mPlaceholderName, std::move(mSelState));
     746           0 :     MOZ_ASSERT(mSelState.isNothing());
     747             : 
     748             :     // We will recurse, but will not hit this case in the nested call
     749           0 :     DoTransaction(mPlaceholderTransaction);
     750             : 
     751           0 :     if (mTransactionManager) {
     752             :       nsCOMPtr<nsITransaction> topTransaction =
     753           0 :         mTransactionManager->PeekUndoStack();
     754             :       nsCOMPtr<nsIAbsorbingTransaction> topAbsorbingTransaction =
     755           0 :         do_QueryInterface(topTransaction);
     756           0 :       if (topAbsorbingTransaction) {
     757             :         RefPtr<PlaceholderTransaction> topPlaceholderTransaction =
     758           0 :           topAbsorbingTransaction->AsPlaceholderTransaction();
     759           0 :         if (topPlaceholderTransaction) {
     760             :           // there is a placeholder transaction on top of the undo stack.  It
     761             :           // is either the one we just created, or an earlier one that we are
     762             :           // now merging into.  From here on out remember this placeholder
     763             :           // instead of the one we just created.
     764           0 :           mPlaceholderTransaction = topPlaceholderTransaction;
     765             :         }
     766             :       }
     767             :     }
     768             :   }
     769             : 
     770           0 :   if (aTxn) {
     771             :     // XXX: Why are we doing selection specific batching stuff here?
     772             :     // XXX: Most entry points into the editor have auto variables that
     773             :     // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
     774             :     // XXX: these selection batch calls no-ops.
     775             :     // XXX:
     776             :     // XXX: I suspect that this was placed here to avoid multiple
     777             :     // XXX: selection changed notifications from happening until after
     778             :     // XXX: the transaction was done. I suppose that can still happen
     779             :     // XXX: if an embedding application called DoTransaction() directly
     780             :     // XXX: to pump its own transactions through the system, but in that
     781             :     // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
     782             :     // XXX: its auto equivalent AutoUpdateViewBatch to ensure that
     783             :     // XXX: selection listeners have access to accurate frame data?
     784             :     // XXX:
     785             :     // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
     786             :     // XXX: we will need to make sure that they are disabled during
     787             :     // XXX: the init of the editor for text widgets to avoid layout
     788             :     // XXX: re-entry during initial reflow. - kin
     789             : 
     790             :     // get the selection and start a batch change
     791           0 :     RefPtr<Selection> selection = aSelection ? aSelection : GetSelection();
     792           0 :     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
     793             : 
     794           0 :     SelectionBatcher selectionBatcher(selection);
     795             : 
     796             :     nsresult rv;
     797           0 :     if (mTransactionManager) {
     798           0 :       RefPtr<TransactionManager> transactionManager(mTransactionManager);
     799           0 :       rv = transactionManager->DoTransaction(aTxn);
     800             :     } else {
     801           0 :       rv = aTxn->DoTransaction();
     802             :     }
     803           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     804           0 :       return rv;
     805             :     }
     806             : 
     807           0 :     DoAfterDoTransaction(aTxn);
     808             :   }
     809             : 
     810             :   return NS_OK;
     811             : }
     812             : 
     813             : NS_IMETHODIMP
     814           0 : EditorBase::EnableUndo(bool aEnable)
     815             : {
     816             :   // XXX Should we return NS_ERROR_FAILURE if EdnableUndoRedo() or
     817             :   //     DisableUndoRedo() returns false?
     818           0 :   if (aEnable) {
     819           0 :     DebugOnly<bool> enabledUndoRedo = EnableUndoRedo();
     820           0 :     NS_WARNING_ASSERTION(enabledUndoRedo,
     821             :       "Failed to enable undo/redo transactions");
     822             :     return NS_OK;
     823             :   }
     824           0 :   DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
     825           0 :   NS_WARNING_ASSERTION(disabledUndoRedo,
     826             :     "Failed to disable undo/redo transactions");
     827             :   return NS_OK;
     828             : }
     829             : 
     830             : NS_IMETHODIMP
     831           0 : EditorBase::GetTransactionManager(nsITransactionManager** aTransactionManager)
     832             : {
     833           0 :   if (NS_WARN_IF(!aTransactionManager)) {
     834             :     return NS_ERROR_INVALID_ARG;
     835             :   }
     836           0 :   if (NS_WARN_IF(!mTransactionManager)) {
     837             :     return NS_ERROR_FAILURE;
     838             :   }
     839           0 :   NS_IF_ADDREF(*aTransactionManager = mTransactionManager);
     840           0 :   return NS_OK;
     841             : }
     842             : 
     843             : NS_IMETHODIMP
     844           0 : EditorBase::Undo(uint32_t aCount)
     845             : {
     846           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     847             : }
     848             : 
     849             : NS_IMETHODIMP
     850           0 : EditorBase::CanUndo(bool* aIsEnabled,
     851             :                     bool* aCanUndo)
     852             : {
     853           0 :   if (NS_WARN_IF(!aIsEnabled) || NS_WARN_IF(!aCanUndo)) {
     854             :     return NS_ERROR_INVALID_ARG;
     855             :   }
     856           0 :   *aCanUndo = CanUndo();
     857           0 :   *aIsEnabled = IsUndoRedoEnabled();
     858           0 :   return NS_OK;
     859             : }
     860             : 
     861             : NS_IMETHODIMP
     862           0 : EditorBase::Redo(uint32_t aCount)
     863             : {
     864           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     865             : }
     866             : 
     867             : NS_IMETHODIMP
     868           0 : EditorBase::CanRedo(bool* aIsEnabled, bool* aCanRedo)
     869             : {
     870           0 :   if (NS_WARN_IF(!aIsEnabled) || NS_WARN_IF(!aCanRedo)) {
     871             :     return NS_ERROR_INVALID_ARG;
     872             :   }
     873           0 :   *aCanRedo = CanRedo();
     874           0 :   *aIsEnabled = IsUndoRedoEnabled();
     875           0 :   return NS_OK;
     876             : }
     877             : 
     878             : NS_IMETHODIMP
     879           0 : EditorBase::BeginTransaction()
     880             : {
     881           0 :   BeginUpdateViewBatch();
     882             : 
     883           0 :   if (mTransactionManager) {
     884           0 :     RefPtr<TransactionManager> transactionManager(mTransactionManager);
     885           0 :     transactionManager->BeginBatch(nullptr);
     886             :   }
     887             : 
     888           0 :   return NS_OK;
     889             : }
     890             : 
     891             : NS_IMETHODIMP
     892           0 : EditorBase::EndTransaction()
     893             : {
     894           0 :   if (mTransactionManager) {
     895           0 :     RefPtr<TransactionManager> transactionManager(mTransactionManager);
     896           0 :     transactionManager->EndBatch(false);
     897             :   }
     898             : 
     899           0 :   EndUpdateViewBatch();
     900             : 
     901           0 :   return NS_OK;
     902             : }
     903             : 
     904             : void
     905           0 : EditorBase::BeginPlaceholderTransaction(nsAtom* aTransactionName)
     906             : {
     907           0 :   MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!");
     908           0 :   if (!mPlaceholderBatch) {
     909           0 :     NotifyEditorObservers(eNotifyEditorObserversOfBefore);
     910             :     // time to turn on the batch
     911           0 :     BeginUpdateViewBatch();
     912           0 :     mPlaceholderTransaction = nullptr;
     913           0 :     mPlaceholderName = aTransactionName;
     914           0 :     RefPtr<Selection> selection = GetSelection();
     915           0 :     if (selection) {
     916           0 :       mSelState.emplace();
     917           0 :       mSelState->SaveSelection(selection);
     918             :       // Composition transaction can modify multiple nodes and it merges text
     919             :       // node for ime into single text node.
     920             :       // So if current selection is into IME text node, it might be failed
     921             :       // to restore selection by UndoTransaction.
     922             :       // So we need update selection by range updater.
     923           0 :       if (mPlaceholderName == nsGkAtoms::IMETxnName) {
     924           0 :         mRangeUpdater.RegisterSelectionState(*mSelState);
     925             :       }
     926             :     }
     927             :   }
     928           0 :   mPlaceholderBatch++;
     929           0 : }
     930             : 
     931             : void
     932           0 : EditorBase::EndPlaceholderTransaction()
     933             : {
     934           0 :   MOZ_ASSERT(mPlaceholderBatch > 0,
     935             :              "zero or negative placeholder batch count when ending batch!");
     936           0 :   if (mPlaceholderBatch == 1) {
     937           0 :     RefPtr<Selection> selection = GetSelection();
     938             : 
     939             :     // By making the assumption that no reflow happens during the calls
     940             :     // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
     941             :     // allow the selection to cache a frame offset which is used by the
     942             :     // caret drawing code. We only enable this cache here; at other times,
     943             :     // we have no way to know whether reflow invalidates it
     944             :     // See bugs 35296 and 199412.
     945           0 :     if (selection) {
     946           0 :       selection->SetCanCacheFrameOffset(true);
     947             :     }
     948             : 
     949             :     // time to turn off the batch
     950           0 :     EndUpdateViewBatch();
     951             :     // make sure selection is in view
     952             : 
     953             :     // After ScrollSelectionIntoView(), the pending notifications might be
     954             :     // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
     955           0 :     ScrollSelectionIntoView(false);
     956             : 
     957             :     // cached for frame offset are Not available now
     958           0 :     if (selection) {
     959           0 :       selection->SetCanCacheFrameOffset(false);
     960             :     }
     961             : 
     962           0 :     if (mSelState) {
     963             :       // we saved the selection state, but never got to hand it to placeholder
     964             :       // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
     965           0 :       if (mPlaceholderName == nsGkAtoms::IMETxnName) {
     966           0 :         mRangeUpdater.DropSelectionState(*mSelState);
     967             :       }
     968           0 :       mSelState.reset();
     969             :     }
     970             :     // We might have never made a placeholder if no action took place.
     971           0 :     if (mPlaceholderTransaction) {
     972           0 :       mPlaceholderTransaction->EndPlaceHolderBatch();
     973             :       // notify editor observers of action but if composing, it's done by
     974             :       // compositionchange event handler.
     975           0 :       if (!mComposition) {
     976           0 :         NotifyEditorObservers(eNotifyEditorObserversOfEnd);
     977             :       }
     978           0 :       mPlaceholderTransaction = nullptr;
     979             :     } else {
     980           0 :       NotifyEditorObservers(eNotifyEditorObserversOfCancel);
     981             :     }
     982             :   }
     983           0 :   mPlaceholderBatch--;
     984           0 : }
     985             : 
     986             : NS_IMETHODIMP
     987           0 : EditorBase::ShouldTxnSetSelection(bool* aResult)
     988             : {
     989           0 :   NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
     990           0 :   *aResult = mShouldTxnSetSelection;
     991           0 :   return NS_OK;
     992             : }
     993             : 
     994             : NS_IMETHODIMP
     995           0 : EditorBase::SetShouldTxnSetSelection(bool aShould)
     996             : {
     997           0 :   mShouldTxnSetSelection = aShould;
     998           0 :   return NS_OK;
     999             : }
    1000             : 
    1001             : NS_IMETHODIMP
    1002           0 : EditorBase::GetDocumentIsEmpty(bool* aDocumentIsEmpty)
    1003             : {
    1004           0 :   *aDocumentIsEmpty = true;
    1005             : 
    1006           0 :   dom::Element* root = GetRoot();
    1007           0 :   NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
    1008             : 
    1009           0 :   *aDocumentIsEmpty = !root->HasChildren();
    1010           0 :   return NS_OK;
    1011             : }
    1012             : 
    1013             : // XXX: The rule system should tell us which node to select all on (ie, the
    1014             : //      root, or the body)
    1015             : NS_IMETHODIMP
    1016           0 : EditorBase::SelectAll()
    1017             : {
    1018             :   // XXX Why doesn't this check if the document is alive?
    1019           0 :   if (!IsInitialized()) {
    1020             :     return NS_ERROR_NOT_INITIALIZED;
    1021             :   }
    1022             : 
    1023           0 :   nsresult rv = SelectAllInternal();
    1024           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1025             :     return rv;
    1026             :   }
    1027           0 :   return NS_OK;
    1028             : }
    1029             : 
    1030             : nsresult
    1031           0 : EditorBase::SelectAllInternal()
    1032             : {
    1033           0 :   MOZ_ASSERT(IsInitialized());
    1034             : 
    1035           0 :   CommitComposition();
    1036           0 :   if (NS_WARN_IF(Destroyed())) {
    1037             :     return NS_ERROR_EDITOR_DESTROYED;
    1038             :   }
    1039             : 
    1040             :   // XXX Do we need to keep handling after committing composition causes moving
    1041             :   //     focus to different element?  Although TextEditor has independent
    1042             :   //     selection, so, we may not see any odd behavior even in such case.
    1043             : 
    1044           0 :   RefPtr<Selection> selection = GetSelection();
    1045           0 :   if (NS_WARN_IF(!selection)) {
    1046             :     return NS_ERROR_FAILURE;
    1047             :   }
    1048           0 :   nsresult rv = SelectEntireDocument(selection);
    1049           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1050             :     return rv;
    1051             :   }
    1052           0 :   return NS_OK;
    1053             : }
    1054             : 
    1055             : NS_IMETHODIMP
    1056           0 : EditorBase::BeginningOfDocument()
    1057             : {
    1058             :   // XXX Why doesn't this check if the document is alive?
    1059           0 :   if (!IsInitialized()) {
    1060             :     return NS_ERROR_NOT_INITIALIZED;
    1061             :   }
    1062             : 
    1063             :   // get the selection
    1064           0 :   RefPtr<Selection> selection = GetSelection();
    1065           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
    1066             : 
    1067             :   // get the root element
    1068           0 :   dom::Element* rootElement = GetRoot();
    1069           0 :   NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
    1070             : 
    1071             :   // find first editable thingy
    1072           0 :   nsCOMPtr<nsINode> firstNode = GetFirstEditableNode(rootElement);
    1073           0 :   if (!firstNode) {
    1074             :     // just the root node, set selection to inside the root
    1075           0 :     return selection->Collapse(rootElement, 0);
    1076             :   }
    1077             : 
    1078           0 :   if (firstNode->NodeType() == nsINode::TEXT_NODE) {
    1079             :     // If firstNode is text, set selection to beginning of the text node.
    1080           0 :     return selection->Collapse(firstNode, 0);
    1081             :   }
    1082             : 
    1083             :   // Otherwise, it's a leaf node and we set the selection just in front of it.
    1084           0 :   nsCOMPtr<nsIContent> parent = firstNode->GetParent();
    1085           0 :   if (!parent) {
    1086             :     return NS_ERROR_NULL_POINTER;
    1087             :   }
    1088             : 
    1089           0 :   MOZ_ASSERT(parent->ComputeIndexOf(firstNode) == 0,
    1090             :              "How come the first node isn't the left most child in its parent?");
    1091           0 :   return selection->Collapse(parent, 0);
    1092             : }
    1093             : 
    1094             : NS_IMETHODIMP
    1095           0 : EditorBase::EndOfDocument()
    1096             : {
    1097           0 :   RefPtr<Selection> selection = GetSelection();
    1098           0 :   return CollapseSelectionToEnd(selection);
    1099             : }
    1100             : 
    1101             : nsresult
    1102           0 : EditorBase::CollapseSelectionToEnd(Selection* aSelection)
    1103             : {
    1104             :   // XXX Why doesn't this check if the document is alive?
    1105           0 :   if (NS_WARN_IF(!IsInitialized())) {
    1106             :     return NS_ERROR_NOT_INITIALIZED;
    1107             :   }
    1108             : 
    1109           0 :   if (NS_WARN_IF(!aSelection)) {
    1110             :     return NS_ERROR_NULL_POINTER;
    1111             :   }
    1112             : 
    1113             :   // get the root element
    1114           0 :   nsINode* node = GetRoot();
    1115           0 :   if (NS_WARN_IF(!node)) {
    1116             :     return NS_ERROR_NULL_POINTER;
    1117             :   }
    1118             : 
    1119           0 :   nsINode* child = node->GetLastChild();
    1120           0 :   while (child && IsContainer(child)) {
    1121           0 :     node = child;
    1122           0 :     child = node->GetLastChild();
    1123             :   }
    1124             : 
    1125           0 :   uint32_t length = node->Length();
    1126           0 :   return aSelection->Collapse(node, static_cast<int32_t>(length));
    1127             : }
    1128             : 
    1129             : NS_IMETHODIMP
    1130           0 : EditorBase::GetDocumentModified(bool* outDocModified)
    1131             : {
    1132           0 :   NS_ENSURE_TRUE(outDocModified, NS_ERROR_NULL_POINTER);
    1133             : 
    1134           0 :   int32_t  modCount = 0;
    1135           0 :   GetModificationCount(&modCount);
    1136             : 
    1137           0 :   *outDocModified = (modCount != 0);
    1138           0 :   return NS_OK;
    1139             : }
    1140             : 
    1141             : NS_IMETHODIMP
    1142           0 : EditorBase::GetDocumentCharacterSet(nsACString& characterSet)
    1143             : {
    1144           0 :   nsCOMPtr<nsIDocument> document = GetDocument();
    1145           0 :   if (NS_WARN_IF(!document)) {
    1146             :     return NS_ERROR_UNEXPECTED;
    1147             :   }
    1148           0 :   document->GetDocumentCharacterSet()->Name(characterSet);
    1149           0 :   return NS_OK;
    1150             : }
    1151             : 
    1152             : NS_IMETHODIMP
    1153           0 : EditorBase::SetDocumentCharacterSet(const nsACString& characterSet)
    1154             : {
    1155           0 :   nsCOMPtr<nsIDocument> document = GetDocument();
    1156           0 :   if (NS_WARN_IF(!document)) {
    1157             :     return NS_ERROR_UNEXPECTED;
    1158             :   }
    1159             :   // This method is scriptable, so add-ons could pass in something other
    1160             :   // than a canonical name.
    1161           0 :   auto encoding = Encoding::ForLabelNoReplacement(characterSet);
    1162           0 :   if (!encoding) {
    1163             :     return NS_ERROR_INVALID_ARG;
    1164             :   }
    1165           0 :   document->SetDocumentCharacterSet(WrapNotNull(encoding));
    1166           0 :   return NS_OK;
    1167             : }
    1168             : 
    1169             : NS_IMETHODIMP
    1170           0 : EditorBase::Cut()
    1171             : {
    1172           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1173             : }
    1174             : 
    1175             : NS_IMETHODIMP
    1176           0 : EditorBase::CanCut(bool* aCanCut)
    1177             : {
    1178           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1179             : }
    1180             : 
    1181             : NS_IMETHODIMP
    1182           0 : EditorBase::Copy()
    1183             : {
    1184           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1185             : }
    1186             : 
    1187             : NS_IMETHODIMP
    1188           0 : EditorBase::CanCopy(bool* aCanCut)
    1189             : {
    1190           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1191             : }
    1192             : 
    1193             : NS_IMETHODIMP
    1194           0 : EditorBase::CanDelete(bool* aCanDelete)
    1195             : {
    1196           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1197             : }
    1198             : 
    1199             : NS_IMETHODIMP
    1200           0 : EditorBase::Paste(int32_t aSelectionType)
    1201             : {
    1202           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1203             : }
    1204             : 
    1205             : NS_IMETHODIMP
    1206           0 : EditorBase::PasteTransferable(nsITransferable* aTransferable)
    1207             : {
    1208           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1209             : }
    1210             : 
    1211             : NS_IMETHODIMP
    1212           0 : EditorBase::CanPaste(int32_t aSelectionType, bool* aCanPaste)
    1213             : {
    1214           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1215             : }
    1216             : 
    1217             : NS_IMETHODIMP
    1218           0 : EditorBase::SetAttribute(Element* aElement,
    1219             :                          const nsAString& aAttribute,
    1220             :                          const nsAString& aValue)
    1221             : {
    1222           0 :   if (NS_WARN_IF(aAttribute.IsEmpty())) {
    1223             :     return NS_ERROR_INVALID_ARG;
    1224             :   }
    1225           0 :   if (NS_WARN_IF(!aElement)) {
    1226             :     return NS_ERROR_INVALID_ARG;
    1227             :   }
    1228           0 :   RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
    1229           0 :   return SetAttributeWithTransaction(*aElement, *attribute, aValue);
    1230             : }
    1231             : 
    1232             : nsresult
    1233           0 : EditorBase::SetAttributeWithTransaction(Element& aElement,
    1234             :                                         nsAtom& aAttribute,
    1235             :                                         const nsAString& aValue)
    1236             : {
    1237             :   RefPtr<ChangeAttributeTransaction> transaction =
    1238           0 :     ChangeAttributeTransaction::Create(aElement, aAttribute, aValue);
    1239           0 :   return DoTransaction(transaction);
    1240             : }
    1241             : 
    1242             : NS_IMETHODIMP
    1243           0 : EditorBase::GetAttributeValue(Element* aElement,
    1244             :                               const nsAString& aAttribute,
    1245             :                               nsAString& aResultValue,
    1246             :                               bool* aResultIsSet)
    1247             : {
    1248           0 :   NS_ENSURE_TRUE(aResultIsSet, NS_ERROR_NULL_POINTER);
    1249           0 :   *aResultIsSet = false;
    1250           0 :   if (!aElement) {
    1251             :     return NS_OK;
    1252             :   }
    1253           0 :   nsAutoString value;
    1254           0 :   aElement->GetAttribute(aAttribute, value);
    1255           0 :   if (!DOMStringIsNull(value)) {
    1256           0 :     *aResultIsSet = true;
    1257             :     aResultValue = value;
    1258             :   }
    1259           0 :   return NS_OK;
    1260             : }
    1261             : 
    1262             : NS_IMETHODIMP
    1263           0 : EditorBase::RemoveAttribute(Element* aElement,
    1264             :                             const nsAString& aAttribute)
    1265             : {
    1266           0 :   if (NS_WARN_IF(aAttribute.IsEmpty())) {
    1267             :     return NS_ERROR_INVALID_ARG;
    1268             :   }
    1269           0 :   if (NS_WARN_IF(!aElement)) {
    1270             :     return NS_ERROR_INVALID_ARG;
    1271             :   }
    1272           0 :   RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
    1273           0 :   return RemoveAttributeWithTransaction(*aElement, *attribute);
    1274             : }
    1275             : 
    1276             : nsresult
    1277           0 : EditorBase::RemoveAttributeWithTransaction(Element& aElement,
    1278             :                                            nsAtom& aAttribute)
    1279             : {
    1280             :   // XXX If aElement doesn't have aAttribute, shouldn't we stop creating
    1281             :   //     the transaction?  Otherwise, there will be added a transaction
    1282             :   //     which does nothing at doing undo/redo.
    1283             :   RefPtr<ChangeAttributeTransaction> transaction =
    1284           0 :     ChangeAttributeTransaction::CreateToRemove(aElement, aAttribute);
    1285           0 :   return DoTransaction(transaction);
    1286             : }
    1287             : 
    1288             : NS_IMETHODIMP
    1289           1 : EditorBase::MarkNodeDirty(nsINode* aNode)
    1290             : {
    1291             :   // Mark the node dirty, but not for webpages (bug 599983)
    1292           1 :   if (!OutputsMozDirty()) {
    1293             :     return NS_OK;
    1294             :   }
    1295           3 :   if (RefPtr<Element> element = Element::FromNodeOrNull(aNode)) {
    1296           1 :     element->SetAttr(kNameSpaceID_None, nsGkAtoms::mozdirty, EmptyString(),
    1297           0 :                      false);
    1298             :   }
    1299           1 :   return NS_OK;
    1300             : }
    1301             : 
    1302             : NS_IMETHODIMP
    1303           0 : EditorBase::GetInlineSpellChecker(bool autoCreate,
    1304             :                                   nsIInlineSpellChecker** aInlineSpellChecker)
    1305             : {
    1306           0 :   NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
    1307             : 
    1308           1 :   if (mDidPreDestroy) {
    1309             :     // Don't allow people to get or create the spell checker once the editor
    1310             :     // is going away.
    1311           0 :     *aInlineSpellChecker = nullptr;
    1312           0 :     return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
    1313             :   }
    1314             : 
    1315             :   // We don't want to show the spell checking UI if there are no spell check dictionaries available.
    1316           1 :   bool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking();
    1317           0 :   if (!canSpell) {
    1318           0 :     *aInlineSpellChecker = nullptr;
    1319           0 :     return NS_ERROR_FAILURE;
    1320             :   }
    1321             : 
    1322             :   nsresult rv;
    1323           0 :   if (!mInlineSpellChecker && autoCreate) {
    1324           0 :     mInlineSpellChecker = new mozInlineSpellChecker();
    1325             :   }
    1326             : 
    1327           2 :   if (mInlineSpellChecker) {
    1328           0 :     rv = mInlineSpellChecker->Init(this);
    1329           0 :     if (NS_FAILED(rv)) {
    1330           0 :       mInlineSpellChecker = nullptr;
    1331             :     }
    1332           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1333             :   }
    1334             : 
    1335           2 :   NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
    1336             : 
    1337           0 :   return NS_OK;
    1338             : }
    1339             : 
    1340             : void
    1341           0 : EditorBase::SyncRealTimeSpell()
    1342             : {
    1343           1 :   bool enable = GetDesiredSpellCheckState();
    1344             : 
    1345             :   // Initializes mInlineSpellChecker
    1346           2 :   nsCOMPtr<nsIInlineSpellChecker> spellChecker;
    1347           0 :   GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
    1348             : 
    1349           2 :   if (mInlineSpellChecker) {
    1350           0 :     if (!mSpellCheckerDictionaryUpdated && enable) {
    1351           0 :       mInlineSpellChecker->UpdateCurrentDictionary();
    1352           0 :       mSpellCheckerDictionaryUpdated = true;
    1353             :     }
    1354             : 
    1355             :     // We might have a mInlineSpellChecker even if there are no dictionaries
    1356             :     // available since we don't destroy the mInlineSpellChecker when the last
    1357             :     // dictionariy is removed, but in that case spellChecker is null
    1358           0 :     mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
    1359             :   }
    1360           0 : }
    1361             : 
    1362             : NS_IMETHODIMP
    1363           0 : EditorBase::SetSpellcheckUserOverride(bool enable)
    1364             : {
    1365           0 :   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
    1366             : 
    1367           0 :   SyncRealTimeSpell();
    1368           0 :   return NS_OK;
    1369             : }
    1370             : 
    1371             : template<typename PT, typename CT>
    1372             : already_AddRefed<Element>
    1373           0 : EditorBase::CreateNodeWithTransaction(
    1374             :               nsAtom& aTagName,
    1375             :               const EditorDOMPointBase<PT, CT>& aPointToInsert)
    1376             : {
    1377           0 :   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
    1378             : 
    1379             :   // XXX We need offset at new node for mRangeUpdater.  Therefore, we need
    1380             :   //     to compute the offset now but this is expensive.  So, if it's possible,
    1381             :   //     we need to redesign mRangeUpdater as avoiding using indices.
    1382           0 :   Unused << aPointToInsert.Offset();
    1383             : 
    1384             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    1385             :                                       *this, EditSubAction::eCreateNode,
    1386           0 :                                       nsIEditor::eNext);
    1387             : 
    1388           0 :   RefPtr<Element> newElement;
    1389             : 
    1390             :   RefPtr<CreateElementTransaction> transaction =
    1391           0 :     CreateElementTransaction::Create(*this, aTagName, aPointToInsert);
    1392           0 :   nsresult rv = DoTransaction(transaction);
    1393           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1394             :     // XXX Why do we do this even when DoTransaction() returned error?
    1395           0 :     mRangeUpdater.SelAdjCreateNode(aPointToInsert);
    1396             :   } else {
    1397           0 :     newElement = transaction->GetNewNode();
    1398           0 :     MOZ_ASSERT(newElement);
    1399             : 
    1400             :     // If we succeeded to create and insert new element, we need to adjust
    1401             :     // ranges in mRangeUpdater.  It currently requires offset of the new node.
    1402             :     // So, let's call it with original offset.  Note that if aPointToInsert
    1403             :     // stores child node, it may not be at the offset since new element must
    1404             :     // be inserted before the old child.  Although, mutation observer can do
    1405             :     // anything, but currently, we don't check it.
    1406           0 :     mRangeUpdater.SelAdjCreateNode(
    1407           0 :                     EditorRawDOMPoint(aPointToInsert.GetContainer(),
    1408             :                                       aPointToInsert.Offset()));
    1409             :   }
    1410             : 
    1411           0 :   if (mRules && mRules->AsHTMLEditRules() && newElement) {
    1412           0 :     Selection* selection = GetSelection();
    1413           0 :     if (selection) {
    1414           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    1415           0 :       htmlEditRules->DidCreateNode(*selection, *newElement);
    1416             :     } else {
    1417           0 :       NS_WARNING("Selection has gone");
    1418             :     }
    1419             :   }
    1420             : 
    1421           0 :   if (!mActionListeners.IsEmpty()) {
    1422           0 :     AutoActionListenerArray listeners(mActionListeners);
    1423           0 :     for (auto& listener : listeners) {
    1424           0 :       listener->DidCreateNode(nsDependentAtomString(&aTagName),
    1425             :                               newElement, rv);
    1426             :     }
    1427             :   }
    1428             : 
    1429           0 :   return newElement.forget();
    1430             : }
    1431             : 
    1432             : NS_IMETHODIMP
    1433           0 : EditorBase::InsertNode(nsINode* aNodeToInsert,
    1434             :                        nsINode* aContainer,
    1435             :                        int32_t aOffset)
    1436             : {
    1437           0 :   nsCOMPtr<nsIContent> contentToInsert = do_QueryInterface(aNodeToInsert);
    1438           0 :   if (NS_WARN_IF(!contentToInsert)) {
    1439             :     return NS_ERROR_NULL_POINTER;
    1440             :   }
    1441           0 :   if (NS_WARN_IF(!aContainer)) {
    1442             :     return NS_ERROR_NULL_POINTER;
    1443             :   }
    1444             :   int32_t offset =
    1445           0 :     aOffset < 0 ? static_cast<int32_t>(aContainer->Length()) :
    1446           0 :                   std::min(aOffset, static_cast<int32_t>(aContainer->Length()));
    1447           0 :   return InsertNodeWithTransaction(*contentToInsert,
    1448           0 :                                    EditorRawDOMPoint(aContainer, offset));
    1449             : }
    1450             : 
    1451             : template<typename PT, typename CT>
    1452             : nsresult
    1453           1 : EditorBase::InsertNodeWithTransaction(
    1454             :               nsIContent& aContentToInsert,
    1455             :               const EditorDOMPointBase<PT, CT>& aPointToInsert)
    1456             : {
    1457           0 :   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
    1458             :     return NS_ERROR_INVALID_ARG;
    1459             :   }
    1460           1 :   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
    1461             : 
    1462             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    1463             :                                       *this, EditSubAction::eInsertNode,
    1464           2 :                                       nsIEditor::eNext);
    1465             : 
    1466             :   RefPtr<InsertNodeTransaction> transaction =
    1467           1 :     InsertNodeTransaction::Create(*this, aContentToInsert, aPointToInsert);
    1468           2 :   nsresult rv = DoTransaction(transaction);
    1469             : 
    1470           1 :   mRangeUpdater.SelAdjInsertNode(aPointToInsert);
    1471             : 
    1472           2 :   if (mRules && mRules->AsHTMLEditRules()) {
    1473           0 :     Selection* selection = GetSelection();
    1474           0 :     if (selection) {
    1475           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    1476           0 :       htmlEditRules->DidInsertNode(*selection, aContentToInsert);
    1477             :     } else {
    1478           0 :       NS_WARNING("Selection has gone");
    1479             :     }
    1480             :   }
    1481             : 
    1482           1 :   if (!mActionListeners.IsEmpty()) {
    1483           0 :     AutoActionListenerArray listeners(mActionListeners);
    1484           0 :     for (auto& listener : listeners) {
    1485           0 :       listener->DidInsertNode(&aContentToInsert, rv);
    1486             :     }
    1487             :   }
    1488             : 
    1489             :   return rv;
    1490             : }
    1491             : 
    1492             : NS_IMETHODIMP
    1493           0 : EditorBase::SplitNode(nsINode* aNode,
    1494             :                       int32_t aOffset,
    1495             :                       nsINode** aNewLeftNode)
    1496             : {
    1497           0 :   if (NS_WARN_IF(!aNode)) {
    1498             :     return NS_ERROR_INVALID_ARG;
    1499             :   }
    1500             : 
    1501             :   int32_t offset = std::min(std::max(aOffset, 0),
    1502           0 :                             static_cast<int32_t>(aNode->Length()));
    1503           0 :   ErrorResult error;
    1504             :   nsCOMPtr<nsIContent> newNode =
    1505           0 :     SplitNodeWithTransaction(EditorRawDOMPoint(aNode, offset), error);
    1506           0 :   newNode.forget(aNewLeftNode);
    1507           0 :   if (NS_WARN_IF(error.Failed())) {
    1508           0 :     return error.StealNSResult();
    1509             :   }
    1510             :   return NS_OK;
    1511             : }
    1512             : 
    1513             : template<typename PT, typename CT>
    1514             : already_AddRefed<nsIContent>
    1515           0 : EditorBase::SplitNodeWithTransaction(
    1516             :               const EditorDOMPointBase<PT, CT>& aStartOfRightNode,
    1517             :               ErrorResult& aError)
    1518             : {
    1519           0 :   if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
    1520           0 :       NS_WARN_IF(!aStartOfRightNode.GetContainerAsContent())) {
    1521           0 :     aError.Throw(NS_ERROR_INVALID_ARG);
    1522             :     return nullptr;
    1523             :   }
    1524           0 :   MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
    1525             : 
    1526             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    1527             :                                       *this, EditSubAction::eSplitNode,
    1528           0 :                                       nsIEditor::eNext);
    1529             : 
    1530             :   // XXX Unfortunately, storing offset of the split point in
    1531             :   //     SplitNodeTransaction is necessary for now.  We should fix this
    1532             :   //     in a follow up bug.
    1533           0 :   Unused << aStartOfRightNode.Offset();
    1534             : 
    1535             :   RefPtr<SplitNodeTransaction> transaction =
    1536           0 :     SplitNodeTransaction::Create(*this, aStartOfRightNode);
    1537           0 :   aError = DoTransaction(transaction);
    1538             : 
    1539           0 :   nsCOMPtr<nsIContent> newNode = transaction->GetNewNode();
    1540           0 :   NS_WARNING_ASSERTION(newNode, "Failed to create a new left node");
    1541             : 
    1542             :   // XXX Some other transactions manage range updater by themselves.
    1543             :   //     Why doesn't SplitNodeTransaction do it?
    1544           0 :   mRangeUpdater.SelAdjSplitNode(*aStartOfRightNode.GetContainerAsContent(),
    1545             :                                 newNode);
    1546             : 
    1547           0 :   if (mRules && mRules->AsHTMLEditRules() && newNode) {
    1548           0 :     Selection* selection = GetSelection();
    1549           0 :     if (selection) {
    1550           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    1551           0 :       htmlEditRules->DidSplitNode(*selection,
    1552           0 :                                   *aStartOfRightNode.GetContainer(), *newNode);
    1553             :     } else {
    1554           0 :       NS_WARNING("Selection has gone");
    1555             :     }
    1556             :   }
    1557             : 
    1558           0 :   if (mInlineSpellChecker) {
    1559           0 :     RefPtr<mozInlineSpellChecker> spellChecker = mInlineSpellChecker;
    1560           0 :     spellChecker->DidSplitNode(aStartOfRightNode.GetContainer(), newNode);
    1561             :   }
    1562             : 
    1563           0 :   if (!mActionListeners.IsEmpty()) {
    1564           0 :     AutoActionListenerArray listeners(mActionListeners);
    1565           0 :     for (auto& listener : listeners) {
    1566           0 :       listener->DidSplitNode(aStartOfRightNode.GetContainer(), newNode);
    1567             :     }
    1568             :   }
    1569             : 
    1570           0 :   if (NS_WARN_IF(aError.Failed())) {
    1571             :     return nullptr;
    1572             :   }
    1573             : 
    1574           0 :   return newNode.forget();
    1575             : }
    1576             : 
    1577             : NS_IMETHODIMP
    1578           0 : EditorBase::JoinNodes(nsINode* aLeftNode,
    1579             :                       nsINode* aRightNode,
    1580             :                       nsINode*)
    1581             : {
    1582           0 :   NS_ENSURE_STATE(aLeftNode && aRightNode && aLeftNode->GetParentNode());
    1583           0 :   return JoinNodesWithTransaction(*aLeftNode, *aRightNode);
    1584             : }
    1585             : 
    1586             : nsresult
    1587           0 : EditorBase::JoinNodesWithTransaction(nsINode& aLeftNode,
    1588             :                                      nsINode& aRightNode)
    1589             : {
    1590           0 :   nsCOMPtr<nsINode> parent = aLeftNode.GetParentNode();
    1591           0 :   MOZ_ASSERT(parent);
    1592             : 
    1593             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    1594             :                                       *this, EditSubAction::eJoinNodes,
    1595           0 :                                       nsIEditor::ePrevious);
    1596             : 
    1597             :   // Remember some values; later used for saved selection updating.
    1598             :   // Find the offset between the nodes to be joined.
    1599           0 :   int32_t offset = parent->ComputeIndexOf(&aRightNode);
    1600             :   // Find the number of children of the lefthand node
    1601           0 :   uint32_t oldLeftNodeLen = aLeftNode.Length();
    1602             : 
    1603           0 :   if (mRules && mRules->AsHTMLEditRules()) {
    1604           0 :     RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    1605           0 :     htmlEditRules->WillJoinNodes(aLeftNode, aRightNode);
    1606             :   }
    1607             : 
    1608           0 :   nsresult rv = NS_OK;
    1609             :   RefPtr<JoinNodeTransaction> transaction =
    1610           0 :     JoinNodeTransaction::MaybeCreate(*this, aLeftNode, aRightNode);
    1611           0 :   if (transaction)  {
    1612           0 :     rv = DoTransaction(transaction);
    1613             :   }
    1614             : 
    1615             :   // XXX Some other transactions manage range updater by themselves.
    1616             :   //     Why doesn't JoinNodeTransaction do it?
    1617           0 :   mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset,
    1618           0 :                                 (int32_t)oldLeftNodeLen);
    1619             : 
    1620           0 :   if (mRules && mRules->AsHTMLEditRules()) {
    1621           0 :     Selection* selection = GetSelection();
    1622           0 :     if (selection) {
    1623           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    1624           0 :       htmlEditRules->DidJoinNodes(*selection, aLeftNode, aRightNode);
    1625             :     } else {
    1626           0 :       NS_WARNING("Selection has gone");
    1627             :     }
    1628             :   }
    1629             : 
    1630           0 :   if (mInlineSpellChecker) {
    1631           0 :     RefPtr<mozInlineSpellChecker> spellChecker = mInlineSpellChecker;
    1632           0 :     spellChecker->DidJoinNodes(aLeftNode, aRightNode);
    1633             :   }
    1634             : 
    1635           0 :   if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
    1636           0 :     RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
    1637           0 :     textServicesDocument->DidJoinNodes(aLeftNode, aRightNode);
    1638             :   }
    1639             : 
    1640           0 :   if (!mActionListeners.IsEmpty()) {
    1641           0 :     AutoActionListenerArray listeners(mActionListeners);
    1642           0 :     for (auto& listener : listeners) {
    1643           0 :       listener->DidJoinNodes(&aLeftNode, &aRightNode, parent, rv);
    1644             :     }
    1645             :   }
    1646             : 
    1647           0 :   return rv;
    1648             : }
    1649             : 
    1650             : NS_IMETHODIMP
    1651           0 : EditorBase::DeleteNode(nsINode* aNode)
    1652             : {
    1653           0 :   if (NS_WARN_IF(!aNode)) {
    1654             :     return NS_ERROR_INVALID_ARG;
    1655             :   }
    1656           0 :   return DeleteNodeWithTransaction(*aNode);
    1657             : }
    1658             : 
    1659             : nsresult
    1660           0 : EditorBase::DeleteNodeWithTransaction(nsINode& aNode)
    1661             : {
    1662             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    1663             :                                       *this, EditSubAction::eCreateNode,
    1664           0 :                                       nsIEditor::ePrevious);
    1665             : 
    1666           0 :   if (mRules && mRules->AsHTMLEditRules()) {
    1667           0 :     Selection* selection = GetSelection();
    1668           0 :     if (selection) {
    1669           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    1670           0 :       htmlEditRules->WillDeleteNode(*selection, aNode);
    1671             :     } else {
    1672           0 :       NS_WARNING("Selection has gone");
    1673             :     }
    1674             :   }
    1675             : 
    1676             :   // FYI: DeleteNodeTransaction grabs aNode while it's alive.  So, it's safe
    1677             :   //      to refer aNode even after calling DoTransaction().
    1678             :   RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
    1679           0 :     DeleteNodeTransaction::MaybeCreate(*this, aNode);
    1680           0 :   nsresult rv = deleteNodeTransaction ? DoTransaction(deleteNodeTransaction) :
    1681           0 :                                         NS_ERROR_FAILURE;
    1682             : 
    1683           0 :   if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
    1684           0 :     RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
    1685           0 :     textServicesDocument->DidDeleteNode(&aNode);
    1686             :   }
    1687             : 
    1688           0 :   if (!mActionListeners.IsEmpty()) {
    1689           0 :     AutoActionListenerArray listeners(mActionListeners);
    1690           0 :     for (auto& listener : listeners) {
    1691           0 :       listener->DidDeleteNode(&aNode, rv);
    1692             :     }
    1693             :   }
    1694             : 
    1695           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1696             :   return NS_OK;
    1697             : }
    1698             : 
    1699             : already_AddRefed<Element>
    1700           0 : EditorBase::ReplaceContainerWithTransactionInternal(
    1701             :               Element& aOldContainer,
    1702             :               nsAtom& aTagName,
    1703             :               nsAtom& aAttribute,
    1704             :               const nsAString& aAttributeValue,
    1705             :               bool aCloneAllAttributes)
    1706             : {
    1707           0 :   EditorDOMPoint atOldContainer(&aOldContainer);
    1708           0 :   if (NS_WARN_IF(!atOldContainer.IsSet())) {
    1709             :     return nullptr;
    1710             :   }
    1711             : 
    1712           0 :   RefPtr<Element> newContainer = CreateHTMLContent(&aTagName);
    1713           0 :   if (NS_WARN_IF(!newContainer)) {
    1714             :     return nullptr;
    1715             :   }
    1716             : 
    1717             :   // Set or clone attribute if needed.
    1718           0 :   if (aCloneAllAttributes) {
    1719           0 :     MOZ_ASSERT(&aAttribute == nsGkAtoms::_empty);
    1720           0 :     CloneAttributesWithTransaction(*newContainer, aOldContainer);
    1721           0 :   } else if (&aAttribute != nsGkAtoms::_empty) {
    1722             :     nsresult rv =
    1723           0 :       newContainer->SetAttr(kNameSpaceID_None, &aAttribute, aAttributeValue,
    1724           0 :                             true);
    1725           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1726             :       return nullptr;
    1727             :     }
    1728             :   }
    1729             : 
    1730             :   // Notify our internal selection state listener.
    1731             :   // Note: An AutoSelectionRestorer object must be created before calling this
    1732             :   // to initialize mRangeUpdater.
    1733             :   AutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, &aOldContainer,
    1734           0 :                                                newContainer);
    1735             :   {
    1736           0 :     AutoTransactionsConserveSelection conserveSelection(this);
    1737             :     // Move all children from the old container to the new container.
    1738           0 :     while (aOldContainer.HasChildren()) {
    1739           0 :       nsCOMPtr<nsIContent> child = aOldContainer.GetFirstChild();
    1740           0 :       if (NS_WARN_IF(!child)) {
    1741           0 :         return nullptr;
    1742             :       }
    1743           0 :       nsresult rv = DeleteNodeWithTransaction(*child);
    1744           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1745             :         return nullptr;
    1746             :       }
    1747             : 
    1748           0 :       rv = InsertNodeWithTransaction(*child,
    1749           0 :                                      EditorRawDOMPoint(newContainer,
    1750           0 :                                                        newContainer->Length()));
    1751           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1752             :         return nullptr;
    1753             :       }
    1754             :     }
    1755             :   }
    1756             : 
    1757             :   // Insert new container into tree.
    1758           0 :   NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
    1759             :     "The old container might be moved by mutation observer");
    1760           0 :   nsresult rv = InsertNodeWithTransaction(*newContainer, atOldContainer);
    1761           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1762             :     return nullptr;
    1763             :   }
    1764             : 
    1765             :   // Delete old container.
    1766           0 :   rv = DeleteNodeWithTransaction(aOldContainer);
    1767           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1768             :     return nullptr;
    1769             :   }
    1770             : 
    1771             :   return newContainer.forget();
    1772             : }
    1773             : 
    1774             : nsresult
    1775           0 : EditorBase::RemoveContainerWithTransaction(Element& aElement)
    1776             : {
    1777           0 :   EditorDOMPoint pointToInsertChildren(&aElement);
    1778           0 :   if (NS_WARN_IF(!pointToInsertChildren.IsSet())) {
    1779             :     return NS_ERROR_FAILURE;
    1780             :   }
    1781             : 
    1782             :   // Notify our internal selection state listener.
    1783             :   AutoRemoveContainerSelNotify selNotify(mRangeUpdater, &aElement,
    1784             :                                          pointToInsertChildren.GetContainer(),
    1785           0 :                                          pointToInsertChildren.Offset(),
    1786           0 :                                          aElement.GetChildCount());
    1787             : 
    1788             :   // Move all children from aNode to its parent.
    1789           0 :   while (aElement.HasChildren()) {
    1790           0 :     nsCOMPtr<nsIContent> child = aElement.GetLastChild();
    1791           0 :     if (NS_WARN_IF(!child)) {
    1792           0 :       return NS_ERROR_FAILURE;
    1793             :     }
    1794           0 :     nsresult rv = DeleteNodeWithTransaction(*child);
    1795           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1796             :       return rv;
    1797             :     }
    1798             : 
    1799             :     // Insert the last child before the previous last child.  So, we need to
    1800             :     // use offset here because previous child might have been moved to
    1801             :     // container.
    1802           0 :     rv = InsertNodeWithTransaction(*child,
    1803           0 :                                    EditorRawDOMPoint(
    1804             :                                      pointToInsertChildren.GetContainer(),
    1805           0 :                                      pointToInsertChildren.Offset()));
    1806           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1807             :       return rv;
    1808             :     }
    1809             :   }
    1810             : 
    1811           0 :   nsresult rv = DeleteNodeWithTransaction(aElement);
    1812           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1813             :     return rv;
    1814             :   }
    1815           0 :   return NS_OK;
    1816             : }
    1817             : 
    1818             : already_AddRefed<Element>
    1819           0 : EditorBase::InsertContainerWithTransactionInternal(
    1820             :               nsIContent& aContent,
    1821             :               nsAtom& aTagName,
    1822             :               nsAtom& aAttribute,
    1823             :               const nsAString& aAttributeValue)
    1824             : {
    1825           0 :   EditorDOMPoint pointToInsertNewContainer(&aContent);
    1826           0 :   if (NS_WARN_IF(!pointToInsertNewContainer.IsSet())) {
    1827             :     return nullptr;
    1828             :   }
    1829             :   // aContent will be moved to the new container before inserting the new
    1830             :   // container.  So, when we insert the container, the insertion point
    1831             :   // is before the next sibling of aContent.
    1832             :   // XXX If pointerToInsertNewContainer stores offset here, the offset and
    1833             :   //     referring child node become mismatched.  Although, currently this
    1834             :   //     is not a problem since InsertNodeTransaction refers only child node.
    1835           0 :   DebugOnly<bool> advanced = pointToInsertNewContainer.AdvanceOffset();
    1836           0 :   NS_WARNING_ASSERTION(advanced,
    1837             :     "Failed to advance offset to after aContent");
    1838             : 
    1839             :   // Create new container.
    1840           0 :   RefPtr<Element> newContainer = CreateHTMLContent(&aTagName);
    1841           0 :   if (NS_WARN_IF(!newContainer)) {
    1842             :     return nullptr;
    1843             :   }
    1844             : 
    1845             :   // Set attribute if needed.
    1846           0 :   if (&aAttribute != nsGkAtoms::_empty) {
    1847             :     nsresult rv =
    1848           0 :       newContainer->SetAttr(kNameSpaceID_None, &aAttribute, aAttributeValue,
    1849           0 :                             true);
    1850           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1851             :       return nullptr;
    1852             :     }
    1853             :   }
    1854             : 
    1855             :   // Notify our internal selection state listener
    1856           0 :   AutoInsertContainerSelNotify selNotify(mRangeUpdater);
    1857             : 
    1858             :   // Put aNode in the new container, first.
    1859           0 :   nsresult rv = DeleteNodeWithTransaction(aContent);
    1860           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1861             :     return nullptr;
    1862             :   }
    1863             : 
    1864             :   {
    1865           0 :     AutoTransactionsConserveSelection conserveSelection(this);
    1866             :     rv = InsertNodeWithTransaction(aContent,
    1867           0 :                                    EditorRawDOMPoint(newContainer, 0));
    1868           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1869           0 :       return nullptr;
    1870             :     }
    1871             :   }
    1872             : 
    1873             :   // Put the new container where aNode was.
    1874           0 :   rv = InsertNodeWithTransaction(*newContainer, pointToInsertNewContainer);
    1875           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1876             :     return nullptr;
    1877             :   }
    1878             : 
    1879             :   return newContainer.forget();
    1880             : }
    1881             : 
    1882             : template<typename PT, typename CT>
    1883             : nsresult
    1884           0 : EditorBase::MoveNodeWithTransaction(
    1885             :               nsIContent& aContent,
    1886             :               const EditorDOMPointBase<PT, CT>& aPointToInsert)
    1887             : {
    1888           0 :   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
    1889             : 
    1890           0 :   EditorDOMPoint oldPoint(&aContent);
    1891           0 :   if (NS_WARN_IF(!oldPoint.IsSet())) {
    1892             :     return NS_ERROR_FAILURE;
    1893             :   }
    1894             : 
    1895             :   // Don't do anything if it's already in right place.
    1896           0 :   if (aPointToInsert == oldPoint) {
    1897             :     return NS_OK;
    1898             :   }
    1899             : 
    1900             :   // Notify our internal selection state listener
    1901           0 :   EditorDOMPoint newPoint(aPointToInsert);
    1902           0 :   AutoMoveNodeSelNotify selNotify(mRangeUpdater, oldPoint, newPoint);
    1903             : 
    1904             :   // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
    1905           0 :   nsresult rv = DeleteNodeWithTransaction(aContent);
    1906           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1907             :     return rv;
    1908             :   }
    1909             : 
    1910             :   // Mutation event listener could break insertion point. Let's check it.
    1911           0 :   EditorRawDOMPoint pointToInsert(selNotify.ComputeInsertionPoint());
    1912           0 :   if (NS_WARN_IF(!pointToInsert.IsSet())) {
    1913             :     return NS_ERROR_FAILURE;
    1914             :   }
    1915             :   // If some children have removed from the container, let's append to the
    1916             :   // container.
    1917             :   // XXX Perhaps, if mutation event listener inserts or removes some children
    1918             :   //     but the child node referring with aPointToInsert is still available,
    1919             :   //     we should insert aContent before it.  However, we should keep
    1920             :   //     traditional behavior for now.
    1921           0 :   if (NS_WARN_IF(!pointToInsert.IsSetAndValid())) {
    1922           0 :     pointToInsert.SetToEndOf(pointToInsert.GetContainer());
    1923             :   }
    1924           0 :   rv = InsertNodeWithTransaction(aContent, pointToInsert);
    1925           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1926             :     return rv;
    1927             :   }
    1928           0 :   return NS_OK;
    1929             : }
    1930             : 
    1931             : void
    1932           0 : EditorBase::MoveAllChildren(nsINode& aContainer,
    1933             :                             const EditorRawDOMPoint& aPointToInsert,
    1934             :                             ErrorResult& aError)
    1935             : {
    1936           0 :   if (!aContainer.HasChildren()) {
    1937             :     return;
    1938             :   }
    1939           0 :   nsIContent* firstChild = aContainer.GetFirstChild();
    1940           0 :   if (NS_WARN_IF(!firstChild)) {
    1941           0 :     aError.Throw(NS_ERROR_FAILURE);
    1942           0 :     return;
    1943             :   }
    1944           0 :   nsIContent* lastChild = aContainer.GetLastChild();
    1945           0 :   if (NS_WARN_IF(!lastChild)) {
    1946           0 :     aError.Throw(NS_ERROR_FAILURE);
    1947           0 :     return;
    1948             :   }
    1949           0 :   return MoveChildren(*firstChild, *lastChild, aPointToInsert, aError);
    1950             : }
    1951             : 
    1952             : void
    1953           0 : EditorBase::MovePreviousSiblings(nsIContent& aChild,
    1954             :                                  const EditorRawDOMPoint& aPointToInsert,
    1955             :                                  ErrorResult& aError)
    1956             : {
    1957           0 :   if (NS_WARN_IF(!aChild.GetParentNode())) {
    1958           0 :     aError.Throw(NS_ERROR_INVALID_ARG);
    1959           0 :     return;
    1960             :   }
    1961           0 :   nsIContent* firstChild = aChild.GetParentNode()->GetFirstChild();
    1962           0 :   if (NS_WARN_IF(!firstChild)) {
    1963           0 :     aError.Throw(NS_ERROR_FAILURE);
    1964           0 :     return;
    1965             :   }
    1966             :   nsIContent* lastChild =
    1967           0 :     &aChild == firstChild ? firstChild : aChild.GetPreviousSibling();
    1968           0 :   if (NS_WARN_IF(!lastChild)) {
    1969           0 :     aError.Throw(NS_ERROR_FAILURE);
    1970           0 :     return;
    1971             :   }
    1972           0 :   return MoveChildren(*firstChild, *lastChild, aPointToInsert, aError);
    1973             : }
    1974             : 
    1975             : void
    1976           0 : EditorBase::MoveChildren(nsIContent& aFirstChild,
    1977             :                          nsIContent& aLastChild,
    1978             :                          const EditorRawDOMPoint& aPointToInsert,
    1979             :                          ErrorResult& aError)
    1980             : {
    1981           0 :   nsCOMPtr<nsINode> oldContainer = aFirstChild.GetParentNode();
    1982           0 :   if (NS_WARN_IF(oldContainer != aLastChild.GetParentNode()) ||
    1983           0 :       NS_WARN_IF(!aPointToInsert.IsSet()) ||
    1984           0 :       NS_WARN_IF(!aPointToInsert.CanContainerHaveChildren())) {
    1985           0 :     aError.Throw(NS_ERROR_INVALID_ARG);
    1986           0 :     return;
    1987             :   }
    1988             : 
    1989             :   // First, store all children which should be moved to the new container.
    1990           0 :   AutoTArray<nsCOMPtr<nsIContent>, 10> children;
    1991           0 :   for (nsIContent* child = &aFirstChild;
    1992           0 :        child;
    1993           0 :        child = child->GetNextSibling()) {
    1994           0 :     children.AppendElement(child);
    1995           0 :     if (child == &aLastChild) {
    1996             :       break;
    1997             :     }
    1998             :   }
    1999             : 
    2000           0 :   if (NS_WARN_IF(children.LastElement() != &aLastChild)) {
    2001           0 :     aError.Throw(NS_ERROR_INVALID_ARG);
    2002           0 :     return;
    2003             :   }
    2004             : 
    2005           0 :   nsCOMPtr<nsINode> newContainer = aPointToInsert.GetContainer();
    2006           0 :   nsCOMPtr<nsIContent> nextNode = aPointToInsert.GetChild();
    2007           0 :   for (size_t i = children.Length(); i > 0; --i) {
    2008           0 :     nsCOMPtr<nsIContent>& child = children[i - 1];
    2009           0 :     if (child->GetParentNode() != oldContainer) {
    2010             :       // If the child has been moved to different container, we shouldn't
    2011             :       // touch it.
    2012             :       continue;
    2013             :     }
    2014           0 :     oldContainer->RemoveChild(*child, aError);
    2015           0 :     if (NS_WARN_IF(aError.Failed())) {
    2016           0 :       return;
    2017             :     }
    2018           0 :     if (nextNode) {
    2019             :       // If we're not appending the children to the new container, we should
    2020             :       // check if referring next node of insertion point is still in the new
    2021             :       // container.
    2022           0 :       EditorRawDOMPoint pointToInsert(nextNode);
    2023           0 :       if (NS_WARN_IF(!pointToInsert.IsSet()) ||
    2024           0 :           NS_WARN_IF(pointToInsert.GetContainer() != newContainer)) {
    2025             :         // The next node of insertion point has been moved by mutation observer.
    2026             :         // Let's stop moving the remaining nodes.
    2027             :         // XXX Or should we move remaining children after the last moved child?
    2028           0 :         aError.Throw(NS_ERROR_FAILURE);
    2029           0 :         return;
    2030             :       }
    2031             :     }
    2032           0 :     newContainer->InsertBefore(*child, nextNode, aError);
    2033           0 :     if (NS_WARN_IF(aError.Failed())) {
    2034             :       return;
    2035             :     }
    2036             :     // If the child was inserted or appended properly, the following children
    2037             :     // should be inserted before it.  Otherwise, keep using current position.
    2038           0 :     if (child->GetParentNode() == newContainer) {
    2039           0 :       nextNode = child;
    2040             :     }
    2041             :   }
    2042             : }
    2043             : 
    2044             : NS_IMETHODIMP
    2045           0 : EditorBase::AddEditorObserver(nsIEditorObserver* aObserver)
    2046             : {
    2047             :   // we don't keep ownership of the observers.  They must
    2048             :   // remove themselves as observers before they are destroyed.
    2049             : 
    2050           0 :   NS_ENSURE_TRUE(aObserver, NS_ERROR_NULL_POINTER);
    2051             : 
    2052             :   // Make sure the listener isn't already on the list
    2053           0 :   if (!mEditorObservers.Contains(aObserver)) {
    2054           0 :     mEditorObservers.AppendElement(*aObserver);
    2055           0 :     NS_WARNING_ASSERTION(mEditorObservers.Length() != 1,
    2056             :       "nsIEditorObserver installed, this editor becomes slower");
    2057             :   }
    2058             : 
    2059             :   return NS_OK;
    2060             : }
    2061             : 
    2062             : NS_IMETHODIMP
    2063           0 : EditorBase::RemoveEditorObserver(nsIEditorObserver* aObserver)
    2064             : {
    2065           0 :   NS_ENSURE_TRUE(aObserver, NS_ERROR_FAILURE);
    2066             : 
    2067           0 :   NS_WARNING_ASSERTION(mEditorObservers.Length() != 1,
    2068             :     "All nsIEditorObservers have been removed, this editor becomes faster");
    2069           0 :   mEditorObservers.RemoveElement(aObserver);
    2070             : 
    2071           0 :   return NS_OK;
    2072             : }
    2073             : 
    2074             : NS_IMETHODIMP
    2075           8 : EditorBase::NotifySelectionChanged(nsIDocument* aDocument,
    2076             :                                    Selection* aSelection,
    2077             :                                    int16_t aReason)
    2078             : {
    2079           0 :   if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
    2080             :     return NS_ERROR_INVALID_ARG;
    2081             :   }
    2082             : 
    2083           0 :   if (mTextInputListener) {
    2084          12 :     RefPtr<TextInputListener> textInputListener = mTextInputListener;
    2085           6 :     textInputListener->OnSelectionChange(*aSelection, aReason);
    2086             :   }
    2087             : 
    2088           0 :   if (mIMEContentObserver) {
    2089           0 :     RefPtr<IMEContentObserver> observer = mIMEContentObserver;
    2090           0 :     observer->OnSelectionChange(*aSelection);
    2091             :   }
    2092             : 
    2093             :   return NS_OK;
    2094             : }
    2095             : 
    2096           0 : class EditorInputEventDispatcher final : public Runnable
    2097             : {
    2098             : public:
    2099           0 :   EditorInputEventDispatcher(EditorBase* aEditorBase,
    2100             :                              nsIContent* aTarget,
    2101             :                              bool aIsComposing)
    2102           0 :     : Runnable("EditorInputEventDispatcher")
    2103             :     , mEditorBase(aEditorBase)
    2104             :     , mTarget(aTarget)
    2105           0 :     , mIsComposing(aIsComposing)
    2106             :   {
    2107           0 :   }
    2108             : 
    2109           0 :   NS_IMETHOD Run() override
    2110             :   {
    2111             :     // Note that we don't need to check mDispatchInputEvent here.  We need
    2112             :     // to check it only when the editor requests to dispatch the input event.
    2113             : 
    2114           0 :     if (!mTarget->IsInComposedDoc()) {
    2115             :       return NS_OK;
    2116             :     }
    2117             : 
    2118           0 :     nsCOMPtr<nsIPresShell> ps = mEditorBase->GetPresShell();
    2119           0 :     if (!ps) {
    2120             :       return NS_OK;
    2121             :     }
    2122             : 
    2123           0 :     nsCOMPtr<nsIWidget> widget = mEditorBase->GetWidget();
    2124           0 :     if (!widget) {
    2125             :       return NS_OK;
    2126             :     }
    2127             : 
    2128             :     // Even if the change is caused by untrusted event, we need to dispatch
    2129             :     // trusted input event since it's a fact.
    2130           0 :     InternalEditorInputEvent inputEvent(true, eEditorInput, widget);
    2131           0 :     inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
    2132           0 :     inputEvent.mIsComposing = mIsComposing;
    2133           0 :     nsEventStatus status = nsEventStatus_eIgnore;
    2134             :     nsresult rv =
    2135           0 :       ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status);
    2136           0 :     NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error
    2137             :     return NS_OK;
    2138             :   }
    2139             : 
    2140             : private:
    2141             :   RefPtr<EditorBase> mEditorBase;
    2142             :   nsCOMPtr<nsIContent> mTarget;
    2143             :   bool mIsComposing;
    2144             : };
    2145             : 
    2146             : void
    2147           0 : EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification)
    2148             : {
    2149           0 :   switch (aNotification) {
    2150             :     case eNotifyEditorObserversOfEnd:
    2151           0 :       mIsInEditSubAction = false;
    2152             : 
    2153           0 :       if (mTextInputListener) {
    2154           0 :         RefPtr<TextInputListener> listener = mTextInputListener;
    2155           0 :         listener->OnEditActionHandled();
    2156             :       }
    2157             : 
    2158           0 :       if (mIMEContentObserver) {
    2159           0 :         RefPtr<IMEContentObserver> observer = mIMEContentObserver;
    2160           0 :         observer->OnEditActionHandled();
    2161             :       }
    2162             : 
    2163           0 :       if (!mEditorObservers.IsEmpty()) {
    2164             :         // Copy the observers since EditAction()s can modify mEditorObservers.
    2165           0 :         AutoEditorObserverArray observers(mEditorObservers);
    2166           0 :         for (auto& observer : observers) {
    2167           0 :           observer->EditAction();
    2168             :         }
    2169             :       }
    2170             : 
    2171           0 :       if (!mDispatchInputEvent) {
    2172             :         return;
    2173             :       }
    2174             : 
    2175           0 :       FireInputEvent();
    2176           0 :       break;
    2177             :     case eNotifyEditorObserversOfBefore:
    2178           0 :       if (NS_WARN_IF(mIsInEditSubAction)) {
    2179             :         break;
    2180             :       }
    2181             : 
    2182           0 :       mIsInEditSubAction = true;
    2183             : 
    2184           0 :       if (mIMEContentObserver) {
    2185           0 :         RefPtr<IMEContentObserver> observer = mIMEContentObserver;
    2186           0 :         observer->BeforeEditAction();
    2187             :       }
    2188             :       break;
    2189             :     case eNotifyEditorObserversOfCancel:
    2190           0 :       mIsInEditSubAction = false;
    2191             : 
    2192           0 :       if (mIMEContentObserver) {
    2193           0 :         RefPtr<IMEContentObserver> observer = mIMEContentObserver;
    2194           0 :         observer->CancelEditAction();
    2195             :       }
    2196             :       break;
    2197             :     default:
    2198           0 :       MOZ_CRASH("Handle all notifications here");
    2199             :       break;
    2200             :   }
    2201             : }
    2202             : 
    2203             : void
    2204           0 : EditorBase::FireInputEvent()
    2205             : {
    2206             :   // We don't need to dispatch multiple input events if there is a pending
    2207             :   // input event.  However, it may have different event target.  If we resolved
    2208             :   // this issue, we need to manage the pending events in an array.  But it's
    2209             :   // overwork.  We don't need to do it for the very rare case.
    2210             : 
    2211           0 :   nsCOMPtr<nsIContent> target = GetInputEventTargetContent();
    2212           0 :   NS_ENSURE_TRUE_VOID(target);
    2213             : 
    2214             :   // NOTE: Don't refer IsIMEComposing() because it returns false even before
    2215             :   //       compositionend.  However, DOM Level 3 Events defines it should be
    2216             :   //       true after compositionstart and before compositionend.
    2217             :   nsContentUtils::AddScriptRunner(
    2218           0 :     new EditorInputEventDispatcher(this, target, !!GetComposition()));
    2219             : }
    2220             : 
    2221             : NS_IMETHODIMP
    2222           0 : EditorBase::AddEditActionListener(nsIEditActionListener* aListener)
    2223             : {
    2224           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
    2225             : 
    2226             :   // If given edit action listener is text services document for the inline
    2227             :   // spell checker, store it as reference of concrete class for performance
    2228             :   // reason.
    2229           0 :   if (mInlineSpellChecker) {
    2230             :     EditorSpellCheck* editorSpellCheck =
    2231           0 :       mInlineSpellChecker->GetEditorSpellCheck();
    2232           0 :     if (editorSpellCheck) {
    2233           0 :       mozSpellChecker* spellChecker = editorSpellCheck->GetSpellChecker();
    2234           0 :       if (spellChecker) {
    2235             :         TextServicesDocument* textServicesDocument =
    2236           0 :           spellChecker->GetTextServicesDocument();
    2237           0 :         if (static_cast<nsIEditActionListener*>(textServicesDocument) ==
    2238             :               aListener) {
    2239           0 :           mTextServicesDocument = textServicesDocument;
    2240           0 :           return NS_OK;
    2241             :         }
    2242             :       }
    2243             :     }
    2244             :   }
    2245             : 
    2246             :   // Make sure the listener isn't already on the list
    2247           0 :   if (!mActionListeners.Contains(aListener)) {
    2248           0 :     mActionListeners.AppendElement(*aListener);
    2249           0 :     NS_WARNING_ASSERTION(mActionListeners.Length() != 1,
    2250             :       "nsIEditActionListener installed, this editor becomes slower");
    2251             :   }
    2252             : 
    2253             :   return NS_OK;
    2254             : }
    2255             : 
    2256             : NS_IMETHODIMP
    2257           0 : EditorBase::RemoveEditActionListener(nsIEditActionListener* aListener)
    2258             : {
    2259           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
    2260             : 
    2261           0 :   if (static_cast<nsIEditActionListener*>(mTextServicesDocument) == aListener) {
    2262           0 :     mTextServicesDocument = nullptr;
    2263           0 :     return NS_OK;
    2264             :   }
    2265             : 
    2266           0 :   NS_WARNING_ASSERTION(mActionListeners.Length() != 1,
    2267             :     "All nsIEditActionListeners have been removed, this editor becomes faster");
    2268           0 :   mActionListeners.RemoveElement(aListener);
    2269             : 
    2270           0 :   return NS_OK;
    2271             : }
    2272             : 
    2273             : NS_IMETHODIMP
    2274           0 : EditorBase::AddDocumentStateListener(nsIDocumentStateListener* aListener)
    2275             : {
    2276           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
    2277             : 
    2278           0 :   if (!mDocStateListeners.Contains(aListener)) {
    2279           0 :     mDocStateListeners.AppendElement(*aListener);
    2280             :   }
    2281             : 
    2282             :   return NS_OK;
    2283             : }
    2284             : 
    2285             : NS_IMETHODIMP
    2286           0 : EditorBase::RemoveDocumentStateListener(nsIDocumentStateListener* aListener)
    2287             : {
    2288           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
    2289             : 
    2290           0 :   mDocStateListeners.RemoveElement(aListener);
    2291             : 
    2292           0 :   return NS_OK;
    2293             : }
    2294             : 
    2295             : NS_IMETHODIMP
    2296           0 : EditorBase::OutputToString(const nsAString& aFormatType,
    2297             :                            uint32_t aFlags,
    2298             :                            nsAString& aOutputString)
    2299             : {
    2300             :   // these should be implemented by derived classes.
    2301           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    2302             : }
    2303             : 
    2304             : NS_IMETHODIMP
    2305           0 : EditorBase::DumpContentTree()
    2306             : {
    2307             : #ifdef DEBUG
    2308           0 :   if (mRootElement) {
    2309           0 :     mRootElement->List(stdout);
    2310             :   }
    2311             : #endif
    2312           0 :   return NS_OK;
    2313             : }
    2314             : 
    2315             : NS_IMETHODIMP
    2316           0 : EditorBase::DebugDumpContent()
    2317             : {
    2318             : #ifdef DEBUG
    2319           0 :   nsCOMPtr<nsIDocument> document = GetDocument();
    2320           0 :   if (NS_WARN_IF(!document)) {
    2321             :     return NS_ERROR_NOT_INITIALIZED;
    2322             :   }
    2323           0 :   Element* body = document->GetBody();
    2324           0 :   if (body) {
    2325           0 :     body->List();
    2326             :   }
    2327             : #endif
    2328             :   return NS_OK;
    2329             : }
    2330             : 
    2331             : NS_IMETHODIMP
    2332           0 : EditorBase::DebugUnitTests(int32_t* outNumTests,
    2333             :                            int32_t* outNumTestsFailed)
    2334             : {
    2335             : #ifdef DEBUG
    2336           0 :   NS_NOTREACHED("This should never get called. Overridden by subclasses");
    2337             : #endif
    2338           0 :   return NS_OK;
    2339             : }
    2340             : 
    2341             : bool
    2342           0 : EditorBase::ArePreservingSelection()
    2343             : {
    2344           0 :   return !(mSavedSel.IsEmpty());
    2345             : }
    2346             : 
    2347             : void
    2348           0 : EditorBase::PreserveSelectionAcrossActions(Selection* aSel)
    2349             : {
    2350           0 :   mSavedSel.SaveSelection(aSel);
    2351           0 :   mRangeUpdater.RegisterSelectionState(mSavedSel);
    2352           0 : }
    2353             : 
    2354             : nsresult
    2355           0 : EditorBase::RestorePreservedSelection(Selection* aSel)
    2356             : {
    2357           0 :   if (mSavedSel.IsEmpty()) {
    2358             :     return NS_ERROR_FAILURE;
    2359             :   }
    2360           0 :   mSavedSel.RestoreSelection(aSel);
    2361           0 :   StopPreservingSelection();
    2362           0 :   return NS_OK;
    2363             : }
    2364             : 
    2365             : void
    2366           0 : EditorBase::StopPreservingSelection()
    2367             : {
    2368           0 :   mRangeUpdater.DropSelectionState(mSavedSel);
    2369           0 :   mSavedSel.MakeEmpty();
    2370           0 : }
    2371             : 
    2372             : NS_IMETHODIMP
    2373           0 : EditorBase::ForceCompositionEnd()
    2374             : {
    2375           0 :   return CommitComposition();
    2376             : }
    2377             : 
    2378             : nsresult
    2379           0 : EditorBase::CommitComposition()
    2380             : {
    2381           0 :   nsPresContext* pc = GetPresContext();
    2382           0 :   if (!pc) {
    2383             :     return NS_ERROR_NOT_AVAILABLE;
    2384             :   }
    2385             : 
    2386           0 :   return mComposition ?
    2387           0 :     IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc) : NS_OK;
    2388             : }
    2389             : 
    2390             : nsresult
    2391           2 : EditorBase::GetPreferredIMEState(IMEState* aState)
    2392             : {
    2393           0 :   NS_ENSURE_ARG_POINTER(aState);
    2394           2 :   aState->mEnabled = IMEState::ENABLED;
    2395           0 :   aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
    2396             : 
    2397           6 :   if (IsReadonly() || IsDisabled()) {
    2398           0 :     aState->mEnabled = IMEState::DISABLED;
    2399           0 :     return NS_OK;
    2400             :   }
    2401             : 
    2402           4 :   nsCOMPtr<nsIContent> content = GetRoot();
    2403           0 :   NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
    2404             : 
    2405           0 :   nsIFrame* frame = content->GetPrimaryFrame();
    2406           0 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    2407             : 
    2408           2 :   switch (frame->StyleUIReset()->mIMEMode) {
    2409             :     case NS_STYLE_IME_MODE_AUTO:
    2410           4 :       if (IsPasswordEditor())
    2411           0 :         aState->mEnabled = IMEState::PASSWORD;
    2412             :       break;
    2413             :     case NS_STYLE_IME_MODE_DISABLED:
    2414             :       // we should use password state for |ime-mode: disabled;|.
    2415           0 :       aState->mEnabled = IMEState::PASSWORD;
    2416           0 :       break;
    2417             :     case NS_STYLE_IME_MODE_ACTIVE:
    2418           0 :       aState->mOpen = IMEState::OPEN;
    2419           0 :       break;
    2420             :     case NS_STYLE_IME_MODE_INACTIVE:
    2421           0 :       aState->mOpen = IMEState::CLOSED;
    2422           0 :       break;
    2423             :   }
    2424             : 
    2425             :   return NS_OK;
    2426             : }
    2427             : 
    2428             : NS_IMETHODIMP
    2429           0 : EditorBase::GetComposing(bool* aResult)
    2430             : {
    2431           0 :   NS_ENSURE_ARG_POINTER(aResult);
    2432           0 :   *aResult = IsIMEComposing();
    2433           0 :   return NS_OK;
    2434             : }
    2435             : 
    2436             : NS_IMETHODIMP
    2437           0 : EditorBase::GetRootElement(Element** aRootElement)
    2438             : {
    2439           0 :   NS_ENSURE_ARG_POINTER(aRootElement);
    2440           0 :   NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE);
    2441           0 :   RefPtr<Element> rootElement = mRootElement;
    2442           0 :   rootElement.forget(aRootElement);
    2443             :   return NS_OK;
    2444             : }
    2445             : 
    2446             : void
    2447           1 : EditorBase::OnStartToHandleTopLevelEditSubAction(
    2448             :               EditSubAction aEditSubAction,
    2449             :               nsIEditor::EDirection aDirection)
    2450             : {
    2451           0 :   mTopLevelEditSubAction = aEditSubAction;
    2452           1 :   mDirection = aDirection;
    2453           1 : }
    2454             : 
    2455             : void
    2456           0 : EditorBase::OnEndHandlingTopLevelEditSubAction()
    2457             : {
    2458           1 :   mTopLevelEditSubAction = EditSubAction::eNone;
    2459           0 :   mDirection = eNone;
    2460           1 : }
    2461             : 
    2462             : NS_IMETHODIMP
    2463           0 : EditorBase::CloneAttribute(const nsAString& aAttribute,
    2464             :                            Element* aDestElement,
    2465             :                            Element* aSourceElement)
    2466             : {
    2467           0 :   NS_ENSURE_TRUE(aDestElement && aSourceElement, NS_ERROR_NULL_POINTER);
    2468           0 :   if (NS_WARN_IF(aAttribute.IsEmpty())) {
    2469             :     return NS_ERROR_FAILURE;
    2470             :   }
    2471             : 
    2472           0 :   RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
    2473           0 :   return CloneAttributeWithTransaction(*attribute, *aDestElement, *aSourceElement);
    2474             : }
    2475             : 
    2476             : nsresult
    2477           0 : EditorBase::CloneAttributeWithTransaction(nsAtom& aAttribute,
    2478             :                                           Element& aDestElement,
    2479             :                                           Element& aSourceElement)
    2480             : {
    2481           0 :   nsAutoString attrValue;
    2482           0 :   if (aSourceElement.GetAttr(kNameSpaceID_None, &aAttribute, attrValue)) {
    2483           0 :     return SetAttributeWithTransaction(aDestElement, aAttribute, attrValue);
    2484             :   }
    2485           0 :   return RemoveAttributeWithTransaction(aDestElement, aAttribute);
    2486             : }
    2487             : 
    2488             : /**
    2489             :  * @param aDest     Must be a DOM element.
    2490             :  * @param aSource   Must be a DOM element.
    2491             :  */
    2492             : NS_IMETHODIMP
    2493           0 : EditorBase::CloneAttributes(Element* aDestElement,
    2494             :                             Element* aSourceElement)
    2495             : {
    2496           0 :   if (NS_WARN_IF(!aDestElement) || NS_WARN_IF(!aSourceElement)) {
    2497             :     return NS_ERROR_INVALID_ARG;
    2498             :   }
    2499             : 
    2500           0 :   CloneAttributesWithTransaction(*aDestElement, *aSourceElement);
    2501             : 
    2502           0 :   return NS_OK;
    2503             : }
    2504             : 
    2505             : void
    2506           0 : EditorBase::CloneAttributesWithTransaction(Element& aDestElement,
    2507             :                                            Element& aSourceElement)
    2508             : {
    2509           0 :   AutoPlaceholderBatch beginBatching(this);
    2510             : 
    2511             :   // Use transaction system for undo only if destination is already in the
    2512             :   // document
    2513           0 :   Element* rootElement = GetRoot();
    2514           0 :   if (NS_WARN_IF(!rootElement)) {
    2515           0 :     return;
    2516             :   }
    2517             : 
    2518           0 :   OwningNonNull<Element> destElement(aDestElement);
    2519           0 :   OwningNonNull<Element> sourceElement(aSourceElement);
    2520           0 :   bool isDestElementInBody = rootElement->Contains(destElement);
    2521             : 
    2522             :   // Clear existing attributes
    2523           0 :   RefPtr<nsDOMAttributeMap> destAttributes = destElement->Attributes();
    2524           0 :   while (RefPtr<Attr> attr = destAttributes->Item(0)) {
    2525           0 :     if (isDestElementInBody) {
    2526             :       RemoveAttributeWithTransaction(destElement,
    2527           0 :                                      *attr->NodeInfo()->NameAtom());
    2528             :     } else {
    2529           0 :       destElement->UnsetAttr(kNameSpaceID_None, attr->NodeInfo()->NameAtom(),
    2530           0 :                              true);
    2531             :     }
    2532             :   }
    2533             : 
    2534             :   // Set just the attributes that the source element has
    2535           0 :   RefPtr<nsDOMAttributeMap> sourceAttributes = sourceElement->Attributes();
    2536           0 :   uint32_t sourceCount = sourceAttributes->Length();
    2537           0 :   for (uint32_t i = 0; i < sourceCount; i++) {
    2538           0 :     RefPtr<Attr> attr = sourceAttributes->Item(i);
    2539           0 :     nsAutoString value;
    2540           0 :     attr->GetValue(value);
    2541           0 :     if (isDestElementInBody) {
    2542           0 :       SetAttributeOrEquivalent(destElement, attr->NodeInfo()->NameAtom(), value,
    2543           0 :                                false);
    2544             :     } else {
    2545             :       // The element is not inserted in the document yet, we don't want to put
    2546             :       // a transaction on the UndoStack
    2547           0 :       SetAttributeOrEquivalent(destElement, attr->NodeInfo()->NameAtom(), value,
    2548           0 :                                true);
    2549             :     }
    2550             :   }
    2551             : }
    2552             : 
    2553             : nsresult
    2554           0 : EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor)
    2555             : {
    2556           0 :   nsISelectionController* selectionController = GetSelectionController();
    2557           0 :   if (!selectionController) {
    2558             :     return NS_OK;
    2559             :   }
    2560             : 
    2561           0 :   int16_t region = nsISelectionController::SELECTION_FOCUS_REGION;
    2562           0 :   if (aScrollToAnchor) {
    2563           0 :     region = nsISelectionController::SELECTION_ANCHOR_REGION;
    2564             :   }
    2565           0 :   selectionController->ScrollSelectionIntoView(
    2566             :                          nsISelectionController::SELECTION_NORMAL,
    2567             :                          region,
    2568           0 :                          nsISelectionController::SCROLL_OVERFLOW_HIDDEN);
    2569           0 :   return NS_OK;
    2570             : }
    2571             : 
    2572             : EditorRawDOMPoint
    2573           0 : EditorBase::FindBetterInsertionPoint(const EditorRawDOMPoint& aPoint)
    2574             : {
    2575           0 :   if (NS_WARN_IF(!aPoint.IsSet())) {
    2576           0 :     return aPoint;
    2577             :   }
    2578             : 
    2579           0 :   MOZ_ASSERT(aPoint.IsSetAndValid());
    2580             : 
    2581           0 :   if (aPoint.IsInTextNode()) {
    2582             :     // There is no "better" insertion point.
    2583           0 :     return aPoint;
    2584             :   }
    2585             : 
    2586           0 :   if (!IsPlaintextEditor()) {
    2587             :     // We cannot find "better" insertion point in HTML editor.
    2588             :     // WARNING: When you add some code to find better node in HTML editor,
    2589             :     //          you need to call this before calling InsertTextWithTransaction()
    2590             :     //          in HTMLEditRules.
    2591           0 :     return aPoint;
    2592             :   }
    2593             : 
    2594           0 :   nsCOMPtr<nsINode> root = GetRoot();
    2595           0 :   if (aPoint.GetContainer() == root) {
    2596             :     // In some cases, aNode is the anonymous DIV, and offset is 0.  To avoid
    2597             :     // injecting unneeded text nodes, we first look to see if we have one
    2598             :     // available.  In that case, we'll just adjust node and offset accordingly.
    2599           0 :     if (aPoint.IsStartOfContainer() &&
    2600           0 :         aPoint.GetContainer()->HasChildren() &&
    2601           0 :         aPoint.GetContainer()->GetFirstChild()->IsText()) {
    2602           0 :       return EditorRawDOMPoint(aPoint.GetContainer()->GetFirstChild(), 0);
    2603             :     }
    2604             : 
    2605             :     // In some other cases, aNode is the anonymous DIV, and offset points to the
    2606             :     // terminating mozBR.  In that case, we'll adjust aInOutNode and
    2607             :     // aInOutOffset to the preceding text node, if any.
    2608           0 :     if (!aPoint.IsStartOfContainer()) {
    2609           0 :       if (AsHTMLEditor()) {
    2610             :         // Fall back to a slow path that uses GetChildAt_Deprecated() for Thunderbird's
    2611             :         // plaintext editor.
    2612           0 :         nsIContent* child = aPoint.GetPreviousSiblingOfChild();
    2613           0 :         if (child && child->IsText()) {
    2614           0 :           if (NS_WARN_IF(child->Length() > INT32_MAX)) {
    2615           0 :             return aPoint;
    2616             :           }
    2617           0 :           return EditorRawDOMPoint(child, child->Length());
    2618             :         }
    2619             :       } else {
    2620             :         // If we're in a real plaintext editor, use a fast path that avoids
    2621             :         // calling GetChildAt_Deprecated() which may perform a linear search.
    2622           0 :         nsIContent* child = aPoint.GetContainer()->GetLastChild();
    2623           0 :         while (child) {
    2624           0 :           if (child->IsText()) {
    2625           0 :             if (NS_WARN_IF(child->Length() > INT32_MAX)) {
    2626           0 :               return aPoint;
    2627             :             }
    2628           0 :             return EditorRawDOMPoint(child, child->Length());
    2629             :           }
    2630           0 :           child = child->GetPreviousSibling();
    2631             :         }
    2632             :       }
    2633             :     }
    2634             :   }
    2635             : 
    2636             :   // Sometimes, aNode is the mozBR element itself.  In that case, we'll adjust
    2637             :   // the insertion point to the previous text node, if one exists, or to the
    2638             :   // parent anonymous DIV.
    2639           0 :   if (TextEditUtils::IsMozBR(aPoint.GetContainer()) &&
    2640           0 :       aPoint.IsStartOfContainer()) {
    2641           0 :     nsIContent* previousSibling = aPoint.GetContainer()->GetPreviousSibling();
    2642           0 :     if (previousSibling && previousSibling->IsText()) {
    2643           0 :       if (NS_WARN_IF(previousSibling->Length() > INT32_MAX)) {
    2644           0 :         return aPoint;
    2645             :       }
    2646           0 :       return EditorRawDOMPoint(previousSibling, previousSibling->Length());
    2647             :     }
    2648             : 
    2649           0 :     nsINode* parentOfContainer = aPoint.GetContainer()->GetParentNode();
    2650           0 :     if (parentOfContainer && parentOfContainer == root) {
    2651             :       return EditorRawDOMPoint(parentOfContainer,
    2652           0 :                                aPoint.GetContainerAsContent(), 0);
    2653             :     }
    2654             :   }
    2655             : 
    2656           0 :   return aPoint;
    2657             : }
    2658             : 
    2659             : nsresult
    2660           0 : EditorBase::InsertTextWithTransaction(
    2661             :               nsIDocument& aDocument,
    2662             :               const nsAString& aStringToInsert,
    2663             :               const EditorRawDOMPoint& aPointToInsert,
    2664             :               EditorRawDOMPoint* aPointAfterInsertedString)
    2665             : {
    2666             :   // NOTE: caller *must* have already used AutoTransactionsConserveSelection
    2667             :   // stack-based class to turn off txn selection updating.  Caller also turned
    2668             :   // on rules sniffing if desired.
    2669             : 
    2670           0 :   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
    2671             :     return NS_ERROR_INVALID_ARG;
    2672             :   }
    2673             : 
    2674           0 :   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
    2675             : 
    2676           0 :   if (!ShouldHandleIMEComposition() && aStringToInsert.IsEmpty()) {
    2677           0 :     if (aPointAfterInsertedString) {
    2678           0 :       *aPointAfterInsertedString = aPointToInsert;
    2679             :     }
    2680             :     return NS_OK;
    2681             :   }
    2682             : 
    2683             :   // This method doesn't support over INT32_MAX length text since aInOutOffset
    2684             :   // is int32_t*.
    2685           0 :   CheckedInt<int32_t> lengthToInsert(aStringToInsert.Length());
    2686           0 :   if (NS_WARN_IF(!lengthToInsert.isValid())) {
    2687             :     return NS_ERROR_INVALID_ARG;
    2688             :   }
    2689             : 
    2690             :   // In some cases, the node may be the anonymous div elemnt or a mozBR
    2691             :   // element.  Let's try to look for better insertion point in the nearest
    2692             :   // text node if there is.
    2693           0 :   EditorRawDOMPoint pointToInsert = FindBetterInsertionPoint(aPointToInsert);
    2694             : 
    2695             :   // If a neighboring text node already exists, use that
    2696           0 :   if (!pointToInsert.IsInTextNode()) {
    2697           0 :     nsIContent* child = nullptr;
    2698           0 :     if (!pointToInsert.IsStartOfContainer() &&
    2699           0 :         (child = pointToInsert.GetPreviousSiblingOfChild()) &&
    2700           0 :         child->IsText()) {
    2701           0 :       pointToInsert.Set(child, child->Length());
    2702           0 :     } else if (!pointToInsert.IsEndOfContainer() &&
    2703           0 :                (child = pointToInsert.GetChild()) &&
    2704           0 :                child->IsText()) {
    2705           0 :       pointToInsert.Set(child, 0);
    2706             :     }
    2707             :   }
    2708             : 
    2709           0 :   if (ShouldHandleIMEComposition()) {
    2710           0 :     CheckedInt<int32_t> newOffset;
    2711           0 :     if (!pointToInsert.IsInTextNode()) {
    2712             :       // create a text node
    2713             :       RefPtr<nsTextNode> newNode =
    2714           0 :         EditorBase::CreateTextNode(aDocument, EmptyString());
    2715             :       // then we insert it into the dom tree
    2716           0 :       nsresult rv = InsertNodeWithTransaction(*newNode, pointToInsert);
    2717           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2718           0 :         return rv;
    2719             :       }
    2720           0 :       pointToInsert.Set(newNode, 0);
    2721           0 :       newOffset = lengthToInsert;
    2722             :     } else {
    2723           0 :       newOffset = lengthToInsert + pointToInsert.Offset();
    2724           0 :       NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
    2725             :     }
    2726             :     nsresult rv =
    2727           0 :       InsertTextIntoTextNodeWithTransaction(aStringToInsert,
    2728           0 :                                             *pointToInsert.GetContainerAsText(),
    2729           0 :                                             pointToInsert.Offset());
    2730           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2731           0 :     if (aPointAfterInsertedString) {
    2732           0 :       aPointAfterInsertedString->Set(pointToInsert.GetContainer(),
    2733           0 :                                      newOffset.value());
    2734             :     }
    2735             :     return NS_OK;
    2736             :   }
    2737             : 
    2738           0 :   if (pointToInsert.IsInTextNode()) {
    2739           0 :     CheckedInt<int32_t> newOffset = lengthToInsert + pointToInsert.Offset();
    2740           0 :     NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
    2741             :     // we are inserting text into an existing text node.
    2742             :     nsresult rv =
    2743           0 :       InsertTextIntoTextNodeWithTransaction(aStringToInsert,
    2744           0 :                                             *pointToInsert.GetContainerAsText(),
    2745           0 :                                             pointToInsert.Offset());
    2746           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2747           0 :     if (aPointAfterInsertedString) {
    2748           0 :       aPointAfterInsertedString->Set(pointToInsert.GetContainer(),
    2749           0 :                                      newOffset.value());
    2750             :     }
    2751             :     return NS_OK;
    2752             :   }
    2753             : 
    2754             :   // we are inserting text into a non-text node.  first we have to create a
    2755             :   // textnode (this also populates it with the text)
    2756             :   RefPtr<nsTextNode> newNode =
    2757           0 :     EditorBase::CreateTextNode(aDocument, aStringToInsert);
    2758             :   // then we insert it into the dom tree
    2759           0 :   nsresult rv = InsertNodeWithTransaction(*newNode, pointToInsert);
    2760           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2761             :     return rv;
    2762             :   }
    2763           0 :   if (aPointAfterInsertedString) {
    2764           0 :     aPointAfterInsertedString->Set(newNode, lengthToInsert.value());
    2765             :   }
    2766             :   return NS_OK;
    2767             : }
    2768             : 
    2769             : nsresult
    2770           0 : EditorBase::InsertTextIntoTextNodeWithTransaction(
    2771             :               const nsAString& aStringToInsert,
    2772             :               Text& aTextNode,
    2773             :               int32_t aOffset,
    2774             :               bool aSuppressIME)
    2775             : {
    2776           0 :   RefPtr<EditTransactionBase> transaction;
    2777           0 :   bool isIMETransaction = false;
    2778           0 :   RefPtr<Text> insertedTextNode = &aTextNode;
    2779           0 :   int32_t insertedOffset = aOffset;
    2780             :   // aSuppressIME is used when editor must insert text, yet this text is not
    2781             :   // part of the current IME operation. Example: adjusting whitespace around an
    2782             :   // IME insertion.
    2783           0 :   if (ShouldHandleIMEComposition() && !aSuppressIME) {
    2784             :     transaction =
    2785           0 :       CompositionTransaction::Create(*this, aStringToInsert,
    2786           0 :                                      aTextNode, aOffset);
    2787           0 :     isIMETransaction = true;
    2788             :     // All characters of the composition string will be replaced with
    2789             :     // aStringToInsert.  So, we need to emulate to remove the composition
    2790             :     // string.
    2791             :     // FYI: The text node information in mComposition has been updated by
    2792             :     //      CompositionTransaction::Create().
    2793           0 :     insertedTextNode = mComposition->GetContainerTextNode();
    2794           0 :     insertedOffset = mComposition->XPOffsetInTextNode();
    2795             :   } else {
    2796             :     transaction =
    2797           0 :       InsertTextTransaction::Create(*this, aStringToInsert, aTextNode, aOffset);
    2798             :   }
    2799             : 
    2800             :   // XXX We may not need these view batches anymore.  This is handled at a
    2801             :   // higher level now I believe.
    2802           0 :   BeginUpdateViewBatch();
    2803           0 :   nsresult rv = DoTransaction(transaction);
    2804           0 :   EndUpdateViewBatch();
    2805             : 
    2806           0 :   if (mRules && mRules->AsHTMLEditRules() && insertedTextNode) {
    2807           0 :     Selection* selection = GetSelection();
    2808           0 :     if (selection) {
    2809           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    2810           0 :       htmlEditRules->DidInsertText(*selection, *insertedTextNode,
    2811           0 :                                    insertedOffset, aStringToInsert);
    2812             :     } else {
    2813           0 :       NS_WARNING("Selection has gone");
    2814             :     }
    2815             :   }
    2816             : 
    2817             :   // let listeners know what happened
    2818           0 :   if (!mActionListeners.IsEmpty()) {
    2819           0 :     AutoActionListenerArray listeners(mActionListeners);
    2820           0 :     for (auto& listener : listeners) {
    2821           0 :       listener->DidInsertText(insertedTextNode, insertedOffset,
    2822           0 :                               aStringToInsert, rv);
    2823             :     }
    2824             :   }
    2825             : 
    2826             :   // Added some cruft here for bug 43366.  Layout was crashing because we left
    2827             :   // an empty text node lying around in the document.  So I delete empty text
    2828             :   // nodes caused by IME.  I have to mark the IME transaction as "fixed", which
    2829             :   // means that furure IME txns won't merge with it.  This is because we don't
    2830             :   // want future IME txns trying to put their text into a node that is no
    2831             :   // longer in the document.  This does not break undo/redo, because all these
    2832             :   // txns are wrapped in a parent PlaceHolder txn, and placeholder txns are
    2833             :   // already savvy to having multiple ime txns inside them.
    2834             : 
    2835             :   // Delete empty IME text node if there is one
    2836           0 :   if (isIMETransaction && mComposition) {
    2837           0 :     Text* textNode = mComposition->GetContainerTextNode();
    2838           0 :     if (textNode && !textNode->Length()) {
    2839           0 :       DeleteNodeWithTransaction(*textNode);
    2840           0 :       mComposition->OnTextNodeRemoved();
    2841           0 :       static_cast<CompositionTransaction*>(transaction.get())->MarkFixed();
    2842             :     }
    2843             :   }
    2844             : 
    2845           0 :   return rv;
    2846             : }
    2847             : 
    2848             : nsresult
    2849           0 : EditorBase::SelectEntireDocument(Selection* aSelection)
    2850             : {
    2851           0 :   if (!aSelection) {
    2852             :     return NS_ERROR_NULL_POINTER;
    2853             :   }
    2854             : 
    2855           0 :   Element* rootElement = GetRoot();
    2856           0 :   if (!rootElement) {
    2857             :     return NS_ERROR_NOT_INITIALIZED;
    2858             :   }
    2859             : 
    2860           0 :   ErrorResult errorResult;
    2861           0 :   aSelection->SelectAllChildren(*rootElement, errorResult);
    2862           0 :   return errorResult.StealNSResult();
    2863             : }
    2864             : 
    2865             : nsINode*
    2866           0 : EditorBase::GetFirstEditableNode(nsINode* aRoot)
    2867             : {
    2868           0 :   MOZ_ASSERT(aRoot);
    2869             : 
    2870           0 :   nsIContent* node = GetLeftmostChild(aRoot);
    2871           0 :   if (node && !IsEditable(node)) {
    2872           0 :     node = GetNextEditableNode(*node);
    2873             :   }
    2874             : 
    2875           0 :   return (node != aRoot) ? node : nullptr;
    2876             : }
    2877             : 
    2878             : nsresult
    2879           4 : EditorBase::NotifyDocumentListeners(
    2880             :               TDocumentListenerNotification aNotificationType)
    2881             : {
    2882           8 :   if (!mDocStateListeners.Length()) {
    2883             :     // Maybe there just aren't any.
    2884             :     return NS_OK;
    2885             :   }
    2886             : 
    2887           0 :   AutoDocumentStateListenerArray listeners(mDocStateListeners);
    2888           0 :   nsresult rv = NS_OK;
    2889             : 
    2890           0 :   switch (aNotificationType) {
    2891             :     case eDocumentCreated:
    2892           0 :       for (auto& listener : listeners) {
    2893           0 :         rv = listener->NotifyDocumentCreated();
    2894           0 :         if (NS_FAILED(rv)) {
    2895             :           break;
    2896             :         }
    2897             :       }
    2898           0 :       break;
    2899             : 
    2900             :     case eDocumentToBeDestroyed:
    2901           0 :       for (auto& listener : listeners) {
    2902           0 :         rv = listener->NotifyDocumentWillBeDestroyed();
    2903           0 :         if (NS_FAILED(rv)) {
    2904             :           break;
    2905             :         }
    2906             :       }
    2907           0 :       break;
    2908             : 
    2909             :     case eDocumentStateChanged: {
    2910             :       bool docIsDirty;
    2911           0 :       rv = GetDocumentModified(&docIsDirty);
    2912           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2913             : 
    2914           0 :       if (static_cast<int8_t>(docIsDirty) == mDocDirtyState) {
    2915             :         return NS_OK;
    2916             :       }
    2917             : 
    2918           0 :       mDocDirtyState = docIsDirty;
    2919             : 
    2920           0 :       for (auto& listener : listeners) {
    2921           0 :         rv = listener->NotifyDocumentStateChanged(mDocDirtyState);
    2922           0 :         if (NS_FAILED(rv)) {
    2923             :           break;
    2924             :         }
    2925             :       }
    2926           0 :       break;
    2927             :     }
    2928             :     default:
    2929           0 :       NS_NOTREACHED("Unknown notification");
    2930             :   }
    2931             : 
    2932             :   return rv;
    2933             : }
    2934             : 
    2935             : nsresult
    2936           0 : EditorBase::SetTextImpl(Selection& aSelection, const nsAString& aString,
    2937             :                         Text& aCharData)
    2938             : {
    2939           0 :   const uint32_t length = aCharData.Length();
    2940             : 
    2941             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    2942             :                                       *this, EditSubAction::eSetText,
    2943           0 :                                       nsIEditor::eNext);
    2944             : 
    2945             :   // Let listeners know what's up
    2946           0 :   if (!mActionListeners.IsEmpty() && length) {
    2947           0 :     AutoActionListenerArray listeners(mActionListeners);
    2948           0 :     for (auto& listener : listeners) {
    2949           0 :       listener->WillDeleteText(&aCharData, 0, length);
    2950             :     }
    2951             :   }
    2952             : 
    2953             :   // We don't support undo here, so we don't really need all of the transaction
    2954             :   // machinery, therefore we can run our transaction directly, breaking all of
    2955             :   // the rules!
    2956           0 :   ErrorResult res;
    2957           0 :   aCharData.SetData(aString, res);
    2958           0 :   nsresult rv = res.StealNSResult();
    2959           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2960             :     return rv;
    2961             :   }
    2962             : 
    2963           0 :   RefPtr<Selection> selection = GetSelection();
    2964           0 :   if (NS_WARN_IF(!selection)) {
    2965             :     return NS_ERROR_FAILURE;
    2966             :   }
    2967             : 
    2968             :   {
    2969             :     // Create a nested scope to not overwrite rv from the outer scope.
    2970           0 :     DebugOnly<nsresult> rv = selection->Collapse(&aCharData, aString.Length());
    2971           0 :     NS_ASSERTION(NS_SUCCEEDED(rv),
    2972             :                  "Selection could not be collapsed after insert");
    2973             :   }
    2974             : 
    2975           0 :   mRangeUpdater.SelAdjDeleteText(&aCharData, 0, length);
    2976           0 :   mRangeUpdater.SelAdjInsertText(aCharData, 0, aString);
    2977             : 
    2978           0 :   if (mRules && mRules->AsHTMLEditRules()) {
    2979           0 :     RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    2980           0 :     if (length) {
    2981           0 :       htmlEditRules->DidDeleteText(*selection, aCharData, 0, length);
    2982             :     }
    2983           0 :     if (!aString.IsEmpty()) {
    2984           0 :       htmlEditRules->DidInsertText(*selection, aCharData, 0, aString);
    2985             :     }
    2986             :   }
    2987             : 
    2988             :   // Let listeners know what happened
    2989           0 :   if (!mActionListeners.IsEmpty()) {
    2990           0 :     AutoActionListenerArray listeners(mActionListeners);
    2991           0 :     for (auto& listener : listeners) {
    2992           0 :       if (length) {
    2993           0 :         listener->DidDeleteText(&aCharData, 0, length, rv);
    2994             :       }
    2995           0 :       if (!aString.IsEmpty()) {
    2996           0 :         listener->DidInsertText(&aCharData, 0, aString, rv);
    2997             :       }
    2998             :     }
    2999             :   }
    3000             : 
    3001             :   return rv;
    3002             : }
    3003             : 
    3004             : nsresult
    3005           0 : EditorBase::DeleteTextWithTransaction(CharacterData& aCharData,
    3006             :                                       uint32_t aOffset,
    3007             :                                       uint32_t aLength)
    3008             : {
    3009             :   RefPtr<DeleteTextTransaction> transaction =
    3010           0 :     DeleteTextTransaction::MaybeCreate(*this, aCharData, aOffset, aLength);
    3011           0 :   if (NS_WARN_IF(!transaction)) {
    3012             :     return NS_ERROR_FAILURE;
    3013             :   }
    3014             : 
    3015             :   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
    3016             :                                       *this, EditSubAction::eDeleteText,
    3017           0 :                                       nsIEditor::ePrevious);
    3018             : 
    3019             :   // Let listeners know what's up
    3020           0 :   if (!mActionListeners.IsEmpty()) {
    3021           0 :     AutoActionListenerArray listeners(mActionListeners);
    3022           0 :     for (auto& listener : listeners) {
    3023           0 :       listener->WillDeleteText(&aCharData, aOffset, aLength);
    3024             :     }
    3025             :   }
    3026             : 
    3027           0 :   nsresult rv = DoTransaction(transaction);
    3028             : 
    3029           0 :   if (mRules && mRules->AsHTMLEditRules()) {
    3030           0 :     RefPtr<Selection> selection = GetSelection();
    3031           0 :     if (selection) {
    3032           0 :       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
    3033           0 :       htmlEditRules->DidDeleteText(*selection, aCharData, aOffset, aLength);
    3034             :     } else {
    3035           0 :       NS_WARNING("Selection has gone");
    3036             :     }
    3037             :   }
    3038             : 
    3039             :   // Let listeners know what happened
    3040           0 :   if (!mActionListeners.IsEmpty()) {
    3041           0 :     AutoActionListenerArray listeners(mActionListeners);
    3042           0 :     for (auto& listener : listeners) {
    3043           0 :       listener->DidDeleteText(&aCharData, aOffset, aLength, rv);
    3044             :     }
    3045             :   }
    3046             : 
    3047             :   return rv;
    3048             : }
    3049             : 
    3050           0 : struct SavedRange final
    3051             : {
    3052             :   RefPtr<Selection> mSelection;
    3053             :   nsCOMPtr<nsINode> mStartContainer;
    3054             :   nsCOMPtr<nsINode> mEndContainer;
    3055             :   int32_t mStartOffset;
    3056             :   int32_t mEndOffset;
    3057             : };
    3058             : 
    3059             : void
    3060           0 : EditorBase::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
    3061             :                         nsIContent& aNewLeftNode,
    3062             :                         ErrorResult& aError)
    3063             : {
    3064           0 :   if (NS_WARN_IF(aError.Failed())) {
    3065           0 :     return;
    3066             :   }
    3067             : 
    3068             :   // XXX Perhaps, aStartOfRightNode may be invalid if this is a redo
    3069             :   //     operation after modifying DOM node with JS.
    3070           0 :   if (NS_WARN_IF(!aStartOfRightNode.IsSet())) {
    3071           0 :     aError.Throw(NS_ERROR_INVALID_ARG);
    3072           0 :     return;
    3073             :   }
    3074           0 :   MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
    3075             : 
    3076             :   // Remember all selection points.
    3077           0 :   AutoTArray<SavedRange, 10> savedRanges;
    3078           0 :   for (SelectionType selectionType : kPresentSelectionTypes) {
    3079           0 :     SavedRange range;
    3080           0 :     range.mSelection = GetSelection(selectionType);
    3081           0 :     if (NS_WARN_IF(!range.mSelection &&
    3082             :                    selectionType == SelectionType::eNormal)) {
    3083           0 :       aError.Throw(NS_ERROR_FAILURE);
    3084           0 :       return;
    3085           0 :     } else if (!range.mSelection) {
    3086             :       // For non-normal selections, skip over the non-existing ones.
    3087           0 :       continue;
    3088             :     }
    3089             : 
    3090           0 :     for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
    3091           0 :       RefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
    3092           0 :       MOZ_ASSERT(r->IsPositioned());
    3093             :       // XXX Looks like that SavedRange should have mStart and mEnd which
    3094             :       //     are RangeBoundary.  Then, we can avoid to compute offset here.
    3095           0 :       range.mStartContainer = r->GetStartContainer();
    3096           0 :       range.mStartOffset = r->StartOffset();
    3097           0 :       range.mEndContainer = r->GetEndContainer();
    3098           0 :       range.mEndOffset = r->EndOffset();
    3099             : 
    3100           0 :       savedRanges.AppendElement(range);
    3101             :     }
    3102             :   }
    3103             : 
    3104           0 :   nsCOMPtr<nsINode> parent = aStartOfRightNode.GetContainer()->GetParentNode();
    3105           0 :   if (NS_WARN_IF(!parent)) {
    3106           0 :     aError.Throw(NS_ERROR_FAILURE);
    3107           0 :     return;
    3108             :   }
    3109             : 
    3110             :   // Fix the child before mutation observer may touch the DOM tree.
    3111           0 :   nsIContent* firstChildOfRightNode = aStartOfRightNode.GetChild();
    3112           0 :   parent->InsertBefore(aNewLeftNode, aStartOfRightNode.GetContainer(),
    3113           0 :                        aError);
    3114           0 :   if (NS_WARN_IF(aError.Failed())) {
    3115             :     return;
    3116             :   }
    3117             : 
    3118             :   // At this point, the existing right node has all the children.  Move all
    3119             :   // the children which are before aStartOfRightNode.
    3120           0 :   if (!aStartOfRightNode.IsStartOfContainer()) {
    3121             :     // If it's a text node, just shuffle around some text
    3122           0 :     Text* rightAsText = aStartOfRightNode.GetContainerAsText();
    3123           0 :     Text* leftAsText = aNewLeftNode.GetAsText();
    3124           0 :     if (rightAsText && leftAsText) {
    3125             :       // Fix right node
    3126           0 :       nsAutoString leftText;
    3127           0 :       rightAsText->SubstringData(0, aStartOfRightNode.Offset(),
    3128           0 :                                  leftText, IgnoreErrors());
    3129           0 :       rightAsText->DeleteData(0, aStartOfRightNode.Offset(), IgnoreErrors());
    3130             :       // Fix left node
    3131           0 :       leftAsText->GetAsText()->SetData(leftText, IgnoreErrors());
    3132             :     } else {
    3133           0 :       MOZ_DIAGNOSTIC_ASSERT(!rightAsText && !leftAsText);
    3134             :       // Otherwise it's an interior node, so shuffle around the children. Go
    3135             :       // through list backwards so deletes don't interfere with the iteration.
    3136           0 :       if (!firstChildOfRightNode) {
    3137           0 :         MoveAllChildren(*aStartOfRightNode.GetContainer(),
    3138           0 :                         EditorRawDOMPoint(&aNewLeftNode, 0), aError);
    3139           0 :         NS_WARNING_ASSERTION(!aError.Failed(),
    3140             :           "Failed to move all children from the right node to the left node");
    3141           0 :       } else if (NS_WARN_IF(aStartOfRightNode.GetContainer() !=
    3142             :                               firstChildOfRightNode->GetParentNode())) {
    3143             :           // firstChildOfRightNode has been moved by mutation observer.
    3144             :           // In this case, we what should we do?  Use offset?  But we cannot
    3145             :           // check if the offset is still expected.
    3146             :       } else {
    3147             :         MovePreviousSiblings(*firstChildOfRightNode,
    3148           0 :                              EditorRawDOMPoint(&aNewLeftNode, 0), aError);
    3149           0 :         NS_WARNING_ASSERTION(!aError.Failed(),
    3150             :           "Failed to move some children from the right node to the left node");
    3151             :       }
    3152             :     }
    3153             :   }
    3154             : 
    3155             :   // XXX Why do we ignore an error while moving nodes from the right node to
    3156             :   //     the left node?
    3157           0 :   aError.SuppressException();
    3158             : 
    3159             :   // Handle selection
    3160           0 :   nsCOMPtr<nsIPresShell> ps = GetPresShell();
    3161           0 :   if (ps) {
    3162           0 :     ps->FlushPendingNotifications(FlushType::Frames);
    3163             :   }
    3164           0 :   NS_WARNING_ASSERTION(!Destroyed(),
    3165             :     "The editor is destroyed during splitting a node");
    3166             : 
    3167           0 :   bool shouldSetSelection = GetShouldTxnSetSelection();
    3168             : 
    3169           0 :   RefPtr<Selection> previousSelection;
    3170           0 :   for (size_t i = 0; i < savedRanges.Length(); ++i) {
    3171             :     // Adjust the selection if needed.
    3172           0 :     SavedRange& range = savedRanges[i];
    3173             : 
    3174             :     // If we have not seen the selection yet, clear all of its ranges.
    3175           0 :     if (range.mSelection != previousSelection) {
    3176           0 :       range.mSelection->RemoveAllRanges(aError);
    3177           0 :       if (NS_WARN_IF(aError.Failed())) {
    3178           0 :         return;
    3179             :       }
    3180           0 :       previousSelection = range.mSelection;
    3181             :     }
    3182             : 
    3183             :     // XXX Looks like that we don't need to modify normal selection here
    3184             :     //     because selection will be modified by the caller if
    3185             :     //     GetShouldTxnSetSelection() will return true.
    3186           0 :     if (shouldSetSelection &&
    3187           0 :         range.mSelection->Type() == SelectionType::eNormal) {
    3188             :       // If the editor should adjust the selection, don't bother restoring
    3189             :       // the ranges for the normal selection here.
    3190           0 :       continue;
    3191             :     }
    3192             : 
    3193             :     // Split the selection into existing node and new node.
    3194           0 :     if (range.mStartContainer == aStartOfRightNode.GetContainer()) {
    3195           0 :       if (static_cast<uint32_t>(range.mStartOffset) <
    3196           0 :             aStartOfRightNode.Offset()) {
    3197           0 :         range.mStartContainer = &aNewLeftNode;
    3198             :       } else {
    3199           0 :         range.mStartOffset -= aStartOfRightNode.Offset();
    3200             :       }
    3201             :     }
    3202             : 
    3203           0 :     if (range.mEndContainer == aStartOfRightNode.GetContainer()) {
    3204           0 :       if (static_cast<uint32_t>(range.mEndOffset) <
    3205           0 :             aStartOfRightNode.Offset()) {
    3206           0 :         range.mEndContainer = &aNewLeftNode;
    3207             :       } else {
    3208           0 :         range.mEndOffset -= aStartOfRightNode.Offset();
    3209             :       }
    3210             :     }
    3211             : 
    3212           0 :     RefPtr<nsRange> newRange;
    3213           0 :     nsresult rv = nsRange::CreateRange(range.mStartContainer,
    3214           0 :                                        range.mStartOffset,
    3215             :                                        range.mEndContainer,
    3216           0 :                                        range.mEndOffset,
    3217           0 :                                        getter_AddRefs(newRange));
    3218           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3219           0 :       aError.Throw(rv);
    3220           0 :       return;
    3221             :     }
    3222           0 :     range.mSelection->AddRange(*newRange, aError);
    3223           0 :     if (NS_WARN_IF(aError.Failed())) {
    3224             :       return;
    3225             :     }
    3226             :   }
    3227             : 
    3228             :   // We don't need to set selection here because the caller should do that
    3229             :   // in any case.
    3230             : }
    3231             : 
    3232             : nsresult
    3233           0 : EditorBase::DoJoinNodes(nsINode* aNodeToKeep,
    3234             :                         nsINode* aNodeToJoin,
    3235             :                         nsINode* aParent)
    3236             : {
    3237           0 :   MOZ_ASSERT(aNodeToKeep);
    3238           0 :   MOZ_ASSERT(aNodeToJoin);
    3239           0 :   MOZ_ASSERT(aParent);
    3240             : 
    3241           0 :   uint32_t firstNodeLength = aNodeToJoin->Length();
    3242             : 
    3243             :   int32_t joinOffset;
    3244           0 :   GetNodeLocation(aNodeToJoin, &joinOffset);
    3245             :   int32_t keepOffset;
    3246           0 :   nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset);
    3247             : 
    3248             :   // Remember all selection points.
    3249           0 :   AutoTArray<SavedRange, 10> savedRanges;
    3250           0 :   for (SelectionType selectionType : kPresentSelectionTypes) {
    3251           0 :     SavedRange range;
    3252           0 :     range.mSelection = GetSelection(selectionType);
    3253           0 :     if (selectionType == SelectionType::eNormal) {
    3254           0 :       NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
    3255           0 :     } else if (!range.mSelection) {
    3256             :       // For non-normal selections, skip over the non-existing ones.
    3257           0 :       continue;
    3258             :     }
    3259             : 
    3260           0 :     for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
    3261           0 :       RefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
    3262           0 :       MOZ_ASSERT(r->IsPositioned());
    3263           0 :       range.mStartContainer = r->GetStartContainer();
    3264           0 :       range.mStartOffset = r->StartOffset();
    3265           0 :       range.mEndContainer = r->GetEndContainer();
    3266           0 :       range.mEndOffset = r->EndOffset();
    3267             : 
    3268             :       // If selection endpoint is between the nodes, remember it as being
    3269             :       // in the one that is going away instead.  This simplifies later selection
    3270             :       // adjustment logic at end of this method.
    3271           0 :       if (range.mStartContainer) {
    3272           0 :         if (range.mStartContainer == parent &&
    3273           0 :             joinOffset < range.mStartOffset &&
    3274           0 :             range.mStartOffset <= keepOffset) {
    3275           0 :           range.mStartContainer = aNodeToJoin;
    3276           0 :           range.mStartOffset = firstNodeLength;
    3277             :         }
    3278           0 :         if (range.mEndContainer == parent &&
    3279           0 :             joinOffset < range.mEndOffset &&
    3280           0 :             range.mEndOffset <= keepOffset) {
    3281           0 :           range.mEndContainer = aNodeToJoin;
    3282           0 :           range.mEndOffset = firstNodeLength;
    3283             :         }
    3284             :       }
    3285             : 
    3286           0 :       savedRanges.AppendElement(range);
    3287             :     }
    3288             :   }
    3289             : 
    3290             :   // OK, ready to do join now.
    3291             :   // If it's a text node, just shuffle around some text.
    3292           0 :   if (IsTextNode(aNodeToKeep) && IsTextNode(aNodeToJoin)) {
    3293           0 :     nsAutoString rightText;
    3294           0 :     nsAutoString leftText;
    3295           0 :     aNodeToKeep->GetAsText()->GetData(rightText);
    3296           0 :     aNodeToJoin->GetAsText()->GetData(leftText);
    3297           0 :     leftText += rightText;
    3298           0 :     aNodeToKeep->GetAsText()->SetData(leftText, IgnoreErrors());
    3299             :   } else {
    3300             :     // Otherwise it's an interior node, so shuffle around the children.
    3301           0 :     nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes();
    3302           0 :     MOZ_ASSERT(childNodes);
    3303             : 
    3304             :     // Remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
    3305             :     // GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's OK.
    3306           0 :     nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild();
    3307             : 
    3308             :     // Have to go through the list backwards to keep deletes from interfering with iteration.
    3309           0 :     for (uint32_t i = childNodes->Length(); i; --i) {
    3310           0 :       nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1);
    3311           0 :       if (childNode) {
    3312             :         // prepend children of aNodeToJoin
    3313           0 :         ErrorResult err;
    3314           0 :         aNodeToKeep->InsertBefore(*childNode, firstNode, err);
    3315           0 :         NS_ENSURE_TRUE(!err.Failed(), err.StealNSResult());
    3316           0 :         firstNode = childNode.forget();
    3317             :       }
    3318             :     }
    3319             :   }
    3320             : 
    3321             :   // Delete the extra node.
    3322           0 :   ErrorResult err;
    3323           0 :   aParent->RemoveChild(*aNodeToJoin, err);
    3324             : 
    3325           0 :   bool shouldSetSelection = GetShouldTxnSetSelection();
    3326             : 
    3327           0 :   RefPtr<Selection> previousSelection;
    3328           0 :   for (size_t i = 0; i < savedRanges.Length(); ++i) {
    3329             :     // And adjust the selection if needed.
    3330           0 :     SavedRange& range = savedRanges[i];
    3331             : 
    3332             :     // If we have not seen the selection yet, clear all of its ranges.
    3333           0 :     if (range.mSelection != previousSelection) {
    3334           0 :       ErrorResult rv;
    3335           0 :       range.mSelection->RemoveAllRanges(rv);
    3336           0 :       if (NS_WARN_IF(rv.Failed())) {
    3337           0 :         return rv.StealNSResult();
    3338             :       }
    3339           0 :       previousSelection = range.mSelection;
    3340             :     }
    3341             : 
    3342           0 :     if (shouldSetSelection &&
    3343           0 :         range.mSelection->Type() == SelectionType::eNormal) {
    3344             :       // If the editor should adjust the selection, don't bother restoring
    3345             :       // the ranges for the normal selection here.
    3346           0 :       continue;
    3347             :     }
    3348             : 
    3349             :     // Check to see if we joined nodes where selection starts.
    3350           0 :     if (range.mStartContainer == aNodeToJoin) {
    3351           0 :       range.mStartContainer = aNodeToKeep;
    3352           0 :     } else if (range.mStartContainer == aNodeToKeep) {
    3353           0 :       range.mStartOffset += firstNodeLength;
    3354             :     }
    3355             : 
    3356             :     // Check to see if we joined nodes where selection ends.
    3357           0 :     if (range.mEndContainer == aNodeToJoin) {
    3358           0 :       range.mEndContainer = aNodeToKeep;
    3359           0 :     } else if (range.mEndContainer == aNodeToKeep) {
    3360           0 :       range.mEndOffset += firstNodeLength;
    3361             :     }
    3362             : 
    3363           0 :     RefPtr<nsRange> newRange;
    3364           0 :     nsresult rv = nsRange::CreateRange(range.mStartContainer,
    3365           0 :                                        range.mStartOffset,
    3366             :                                        range.mEndContainer,
    3367           0 :                                        range.mEndOffset,
    3368           0 :                                        getter_AddRefs(newRange));
    3369           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3370           0 :     ErrorResult err;
    3371           0 :     range.mSelection->AddRange(*newRange, err);
    3372           0 :     if (NS_WARN_IF(err.Failed())) {
    3373           0 :       return err.StealNSResult();
    3374             :     }
    3375             :   }
    3376             : 
    3377           0 :   if (shouldSetSelection) {
    3378             :     // Editor wants us to set selection at join point.
    3379           0 :     RefPtr<Selection> selection = GetSelection();
    3380           0 :     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
    3381           0 :     selection->Collapse(aNodeToKeep, AssertedCast<int32_t>(firstNodeLength));
    3382             :   }
    3383             : 
    3384           0 :   return err.StealNSResult();
    3385             : }
    3386             : 
    3387             : // static
    3388             : int32_t
    3389           0 : EditorBase::GetChildOffset(nsINode* aChild,
    3390             :                            nsINode* aParent)
    3391             : {
    3392           0 :   MOZ_ASSERT(aChild);
    3393           0 :   MOZ_ASSERT(aParent);
    3394             : 
    3395             :   // nsINode::ComputeIndexOf() is expensive.  So, if we can return index
    3396             :   // without calling it, we should do that.
    3397             : 
    3398             :   // If there is no previous siblings, it means that it's the first child.
    3399           0 :   if (aParent->GetFirstChild() == aChild) {
    3400           0 :     MOZ_ASSERT(aParent->ComputeIndexOf(aChild) == 0);
    3401             :     return 0;
    3402             :   }
    3403             : 
    3404             :   // If there is no next siblings, it means that it's the last child.
    3405           0 :   if (aParent->GetLastChild() == aChild) {
    3406           0 :     int32_t lastChildIndex = static_cast<int32_t>(aParent->Length() - 1);
    3407           0 :     MOZ_ASSERT(aParent->ComputeIndexOf(aChild) == lastChildIndex);
    3408             :     return lastChildIndex;
    3409             :   }
    3410             : 
    3411           0 :   int32_t index = aParent->ComputeIndexOf(aChild);
    3412           0 :   MOZ_ASSERT(index != -1);
    3413             :   return index;
    3414             : }
    3415             : 
    3416             : // static
    3417             : nsINode*
    3418           0 : EditorBase::GetNodeLocation(nsINode* aChild,
    3419             :                             int32_t* aOffset)
    3420             : {
    3421           0 :   MOZ_ASSERT(aChild);
    3422           0 :   MOZ_ASSERT(aOffset);
    3423             : 
    3424           0 :   nsINode* parent = aChild->GetParentNode();
    3425           0 :   if (parent) {
    3426           0 :     *aOffset = GetChildOffset(aChild, parent);
    3427           0 :     MOZ_ASSERT(*aOffset != -1);
    3428             :   } else {
    3429           0 :     *aOffset = -1;
    3430             :   }
    3431           0 :   return parent;
    3432             : }
    3433             : 
    3434             : nsIContent*
    3435           0 : EditorBase::GetPreviousNodeInternal(nsINode& aNode,
    3436             :                                     bool aFindEditableNode,
    3437             :                                     bool aFindAnyDataNode,
    3438             :                                     bool aNoBlockCrossing)
    3439             : {
    3440           0 :   if (!IsDescendantOfEditorRoot(&aNode)) {
    3441             :     return nullptr;
    3442             :   }
    3443           0 :   return FindNode(&aNode, false,
    3444           0 :                   aFindEditableNode, aFindAnyDataNode, aNoBlockCrossing);
    3445             : }
    3446             : 
    3447             : nsIContent*
    3448           0 : EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
    3449             :                                     bool aFindEditableNode,
    3450             :                                     bool aFindAnyDataNode,
    3451             :                                     bool aNoBlockCrossing)
    3452             : {
    3453           0 :   MOZ_ASSERT(aPoint.IsSetAndValid());
    3454           0 :   NS_WARNING_ASSERTION(!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
    3455             :     "GetPreviousNodeInternal() doesn't assume that the start point is a "
    3456             :     "data node except text node");
    3457             : 
    3458             :   // If we are at the beginning of the node, or it is a text node, then just
    3459             :   // look before it.
    3460           0 :   if (aPoint.IsStartOfContainer() || aPoint.IsInTextNode()) {
    3461           0 :     if (aNoBlockCrossing && IsBlockNode(aPoint.GetContainer())) {
    3462             :       // If we aren't allowed to cross blocks, don't look before this block.
    3463             :       return nullptr;
    3464             :     }
    3465           0 :     return GetPreviousNodeInternal(*aPoint.GetContainer(),
    3466             :                                    aFindEditableNode, aFindAnyDataNode,
    3467           0 :                                    aNoBlockCrossing);
    3468             :   }
    3469             : 
    3470             :   // else look before the child at 'aOffset'
    3471           0 :   if (aPoint.GetChild()) {
    3472           0 :     return GetPreviousNodeInternal(*aPoint.GetChild(),
    3473             :                                    aFindEditableNode, aFindAnyDataNode,
    3474           0 :                                    aNoBlockCrossing);
    3475             :   }
    3476             : 
    3477             :   // unless there isn't one, in which case we are at the end of the node
    3478             :   // and want the deep-right child.
    3479             :   nsIContent* rightMostNode =
    3480           0 :     GetRightmostChild(aPoint.GetContainer(), aNoBlockCrossing);
    3481           0 :   if (!rightMostNode) {
    3482             :     return nullptr;
    3483             :   }
    3484             : 
    3485           0 :   if ((!aFindEditableNode || IsEditable(rightMostNode)) &&
    3486           0 :       (aFindAnyDataNode || IsElementOrText(*rightMostNode))) {
    3487             :     return rightMostNode;
    3488             :   }
    3489             : 
    3490             :   // restart the search from the non-editable node we just found
    3491           0 :   return GetPreviousNodeInternal(*rightMostNode,
    3492             :                                  aFindEditableNode, aFindAnyDataNode,
    3493           0 :                                  aNoBlockCrossing);
    3494             : }
    3495             : 
    3496             : nsIContent*
    3497           0 : EditorBase::GetNextNodeInternal(nsINode& aNode,
    3498             :                                 bool aFindEditableNode,
    3499             :                                 bool aFindAnyDataNode,
    3500             :                                 bool aNoBlockCrossing)
    3501             : {
    3502           0 :   if (!IsDescendantOfEditorRoot(&aNode)) {
    3503             :     return nullptr;
    3504             :   }
    3505           0 :   return FindNode(&aNode, true,
    3506           0 :                   aFindEditableNode, aFindAnyDataNode, aNoBlockCrossing);
    3507             : }
    3508             : 
    3509             : nsIContent*
    3510           0 : EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint,
    3511             :                                 bool aFindEditableNode,
    3512             :                                 bool aFindAnyDataNode,
    3513             :                                 bool aNoBlockCrossing)
    3514             : {
    3515           0 :   MOZ_ASSERT(aPoint.IsSetAndValid());
    3516           0 :   NS_WARNING_ASSERTION(!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
    3517             :     "GetNextNodeInternal() doesn't assume that the start point is a "
    3518             :     "data node except text node");
    3519             : 
    3520           0 :   EditorRawDOMPoint point(aPoint);
    3521             : 
    3522             :   // if the container is a text node, use its location instead
    3523           0 :   if (point.IsInTextNode()) {
    3524           0 :     point.Set(point.GetContainer());
    3525           0 :     bool advanced = point.AdvanceOffset();
    3526           0 :     if (NS_WARN_IF(!advanced)) {
    3527             :       return nullptr;
    3528             :     }
    3529             :   }
    3530             : 
    3531           0 :   if (point.GetChild()) {
    3532           0 :     if (aNoBlockCrossing && IsBlockNode(point.GetChild())) {
    3533           0 :       return point.GetChild();
    3534             :     }
    3535             : 
    3536             :     nsIContent* leftMostNode =
    3537           0 :       GetLeftmostChild(point.GetChild(), aNoBlockCrossing);
    3538           0 :     if (!leftMostNode) {
    3539           0 :       return point.GetChild();
    3540             :     }
    3541             : 
    3542           0 :     if (!IsDescendantOfEditorRoot(leftMostNode)) {
    3543             :       return nullptr;
    3544             :     }
    3545             : 
    3546           0 :     if ((!aFindEditableNode || IsEditable(leftMostNode)) &&
    3547           0 :         (aFindAnyDataNode || IsElementOrText(*leftMostNode))) {
    3548             :       return leftMostNode;
    3549             :     }
    3550             : 
    3551             :     // restart the search from the non-editable node we just found
    3552           0 :     return GetNextNodeInternal(*leftMostNode,
    3553             :                                aFindEditableNode, aFindAnyDataNode,
    3554           0 :                                aNoBlockCrossing);
    3555             :   }
    3556             : 
    3557             :   // unless there isn't one, in which case we are at the end of the node
    3558             :   // and want the next one.
    3559           0 :   if (aNoBlockCrossing && IsBlockNode(point.GetContainer())) {
    3560             :     // don't cross out of parent block
    3561             :     return nullptr;
    3562             :   }
    3563             : 
    3564           0 :   return GetNextNodeInternal(*point.GetContainer(),
    3565             :                              aFindEditableNode, aFindAnyDataNode,
    3566           0 :                              aNoBlockCrossing);
    3567             : }
    3568             : 
    3569             : nsIContent*
    3570           0 : EditorBase::FindNextLeafNode(nsINode* aCurrentNode,
    3571             :                              bool aGoForward,
    3572             :                              bool bNoBlockCrossing)
    3573             : {
    3574             :   // called only by GetPriorNode so we don't need to check params.
    3575           0 :   MOZ_ASSERT(IsDescendantOfEditorRoot(aCurrentNode) &&
    3576             :              !IsEditorRoot(aCurrentNode),
    3577             :              "Bogus arguments");
    3578             : 
    3579             :   nsINode* cur = aCurrentNode;
    3580             :   for (;;) {
    3581             :     // if aCurrentNode has a sibling in the right direction, return
    3582             :     // that sibling's closest child (or itself if it has no children)
    3583             :     nsIContent* sibling =
    3584           0 :       aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling();
    3585           0 :     if (sibling) {
    3586           0 :       if (bNoBlockCrossing && IsBlockNode(sibling)) {
    3587             :         // don't look inside prevsib, since it is a block
    3588             :         return sibling;
    3589             :       }
    3590             :       nsIContent *leaf =
    3591           0 :         aGoForward ? GetLeftmostChild(sibling, bNoBlockCrossing) :
    3592           0 :                      GetRightmostChild(sibling, bNoBlockCrossing);
    3593           0 :       if (!leaf) {
    3594             :         return sibling;
    3595             :       }
    3596             : 
    3597           0 :       return leaf;
    3598             :     }
    3599             : 
    3600           0 :     nsINode *parent = cur->GetParentNode();
    3601           0 :     if (!parent) {
    3602             :       return nullptr;
    3603             :     }
    3604             : 
    3605           0 :     NS_ASSERTION(IsDescendantOfEditorRoot(parent),
    3606             :                  "We started with a proper descendant of root, and should stop "
    3607             :                  "if we ever hit the root, so we better have a descendant of "
    3608             :                  "root now!");
    3609           0 :     if (IsEditorRoot(parent) ||
    3610           0 :         (bNoBlockCrossing && IsBlockNode(parent))) {
    3611             :       return nullptr;
    3612             :     }
    3613             : 
    3614             :     cur = parent;
    3615             :   }
    3616             : 
    3617             :   NS_NOTREACHED("What part of for(;;) do you not understand?");
    3618             :   return nullptr;
    3619             : }
    3620             : 
    3621             : nsIContent*
    3622           0 : EditorBase::FindNode(nsINode* aCurrentNode,
    3623             :                      bool aGoForward,
    3624             :                      bool aEditableNode,
    3625             :                      bool aFindAnyDataNode,
    3626             :                      bool bNoBlockCrossing)
    3627             : {
    3628           0 :   if (IsEditorRoot(aCurrentNode)) {
    3629             :     // Don't allow traversal above the root node! This helps
    3630             :     // prevent us from accidentally editing browser content
    3631             :     // when the editor is in a text widget.
    3632             : 
    3633             :     return nullptr;
    3634             :   }
    3635             : 
    3636             :   nsCOMPtr<nsIContent> candidate =
    3637           0 :     FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
    3638             : 
    3639           0 :   if (!candidate) {
    3640             :     return nullptr;
    3641             :   }
    3642             : 
    3643           0 :   if ((!aEditableNode || IsEditable(candidate)) &&
    3644           0 :       (aFindAnyDataNode || IsElementOrText(*candidate))) {
    3645           0 :     return candidate;
    3646             :   }
    3647             : 
    3648           0 :   return FindNode(candidate, aGoForward,
    3649           0 :                   aEditableNode, aFindAnyDataNode, bNoBlockCrossing);
    3650             : }
    3651             : 
    3652             : nsIContent*
    3653           0 : EditorBase::GetRightmostChild(nsINode* aCurrentNode,
    3654             :                               bool bNoBlockCrossing)
    3655             : {
    3656           0 :   NS_ENSURE_TRUE(aCurrentNode, nullptr);
    3657           0 :   nsIContent *cur = aCurrentNode->GetLastChild();
    3658           0 :   if (!cur) {
    3659             :     return nullptr;
    3660             :   }
    3661             :   for (;;) {
    3662           0 :     if (bNoBlockCrossing && IsBlockNode(cur)) {
    3663             :       return cur;
    3664             :     }
    3665           0 :     nsIContent* next = cur->GetLastChild();
    3666           0 :     if (!next) {
    3667             :       return cur;
    3668             :     }
    3669             :     cur = next;
    3670             :   }
    3671             : 
    3672             :   NS_NOTREACHED("What part of for(;;) do you not understand?");
    3673             :   return nullptr;
    3674             : }
    3675             : 
    3676             : nsIContent*
    3677           0 : EditorBase::GetLeftmostChild(nsINode* aCurrentNode,
    3678             :                              bool bNoBlockCrossing)
    3679             : {
    3680           0 :   NS_ENSURE_TRUE(aCurrentNode, nullptr);
    3681           0 :   nsIContent *cur = aCurrentNode->GetFirstChild();
    3682           0 :   if (!cur) {
    3683             :     return nullptr;
    3684             :   }
    3685             :   for (;;) {
    3686           0 :     if (bNoBlockCrossing && IsBlockNode(cur)) {
    3687             :       return cur;
    3688             :     }
    3689           0 :     nsIContent *next = cur->GetFirstChild();
    3690           0 :     if (!next) {
    3691             :       return cur;
    3692             :     }
    3693             :     cur = next;
    3694             :   }
    3695             : 
    3696             :   NS_NOTREACHED("What part of for(;;) do you not understand?");
    3697             :   return nullptr;
    3698             : }
    3699             : 
    3700             : bool
    3701           0 : EditorBase::IsBlockNode(nsINode* aNode)
    3702             : {
    3703             :   // stub to be overridden in HTMLEditor.
    3704             :   // screwing around with the class hierarchy here in order
    3705             :   // to not duplicate the code in GetNextNode/GetPrevNode
    3706             :   // across both EditorBase/HTMLEditor.
    3707           0 :   return false;
    3708             : }
    3709             : 
    3710             : bool
    3711           0 : EditorBase::CanContain(nsINode& aParent,
    3712             :                        nsIContent& aChild) const
    3713             : {
    3714           0 :   switch (aParent.NodeType()) {
    3715             :     case nsINode::ELEMENT_NODE:
    3716             :     case nsINode::DOCUMENT_FRAGMENT_NODE:
    3717           0 :       return TagCanContain(*aParent.NodeInfo()->NameAtom(), aChild);
    3718             :   }
    3719             :   return false;
    3720             : }
    3721             : 
    3722             : bool
    3723           0 : EditorBase::CanContainTag(nsINode& aParent,
    3724             :                           nsAtom& aChildTag) const
    3725             : {
    3726           0 :   switch (aParent.NodeType()) {
    3727             :     case nsINode::ELEMENT_NODE:
    3728             :     case nsINode::DOCUMENT_FRAGMENT_NODE:
    3729           0 :       return TagCanContainTag(*aParent.NodeInfo()->NameAtom(), aChildTag);
    3730             :   }
    3731             :   return false;
    3732             : }
    3733             : 
    3734             : bool
    3735           0 : EditorBase::TagCanContain(nsAtom& aParentTag,
    3736             :                           nsIContent& aChild) const
    3737             : {
    3738           0 :   switch (aChild.NodeType()) {
    3739             :     case nsINode::TEXT_NODE:
    3740             :     case nsINode::ELEMENT_NODE:
    3741             :     case nsINode::DOCUMENT_FRAGMENT_NODE:
    3742           0 :       return TagCanContainTag(aParentTag, *aChild.NodeInfo()->NameAtom());
    3743             :   }
    3744             :   return false;
    3745             : }
    3746             : 
    3747             : bool
    3748           0 : EditorBase::TagCanContainTag(nsAtom& aParentTag,
    3749             :                              nsAtom& aChildTag) const
    3750             : {
    3751           0 :   return true;
    3752             : }
    3753             : 
    3754             : bool
    3755           0 : EditorBase::IsRoot(nsINode* inNode)
    3756             : {
    3757           0 :   NS_ENSURE_TRUE(inNode, false);
    3758             : 
    3759           0 :   nsCOMPtr<nsINode> rootNode = GetRoot();
    3760             : 
    3761           0 :   return inNode == rootNode;
    3762             : }
    3763             : 
    3764             : bool
    3765           0 : EditorBase::IsEditorRoot(nsINode* aNode)
    3766             : {
    3767           0 :   NS_ENSURE_TRUE(aNode, false);
    3768           0 :   nsCOMPtr<nsINode> rootNode = GetEditorRoot();
    3769           0 :   return aNode == rootNode;
    3770             : }
    3771             : 
    3772             : bool
    3773           0 : EditorBase::IsDescendantOfRoot(nsINode* inNode)
    3774             : {
    3775           0 :   NS_ENSURE_TRUE(inNode, false);
    3776           0 :   nsCOMPtr<nsIContent> root = GetRoot();
    3777           0 :   NS_ENSURE_TRUE(root, false);
    3778             : 
    3779           0 :   return nsContentUtils::ContentIsDescendantOf(inNode, root);
    3780             : }
    3781             : 
    3782             : bool
    3783           0 : EditorBase::IsDescendantOfEditorRoot(nsINode* aNode)
    3784             : {
    3785           0 :   NS_ENSURE_TRUE(aNode, false);
    3786           0 :   nsCOMPtr<nsIContent> root = GetEditorRoot();
    3787           0 :   NS_ENSURE_TRUE(root, false);
    3788             : 
    3789           0 :   return nsContentUtils::ContentIsDescendantOf(aNode, root);
    3790             : }
    3791             : 
    3792             : bool
    3793           0 : EditorBase::IsContainer(nsINode* aNode)
    3794             : {
    3795           0 :   return aNode ? true : false;
    3796             : }
    3797             : 
    3798             : uint32_t
    3799           0 : EditorBase::CountEditableChildren(nsINode* aNode)
    3800             : {
    3801           0 :   MOZ_ASSERT(aNode);
    3802           0 :   uint32_t count = 0;
    3803           0 :   for (nsIContent* child = aNode->GetFirstChild();
    3804           0 :        child;
    3805           0 :        child = child->GetNextSibling()) {
    3806           0 :     if (IsEditable(child)) {
    3807           0 :       ++count;
    3808             :     }
    3809             :   }
    3810           0 :   return count;
    3811             : }
    3812             : 
    3813             : NS_IMETHODIMP
    3814           0 : EditorBase::IncrementModificationCount(int32_t inNumMods)
    3815             : {
    3816           0 :   uint32_t oldModCount = mModCount;
    3817             : 
    3818           0 :   mModCount += inNumMods;
    3819             : 
    3820           1 :   if ((!oldModCount && mModCount) ||
    3821           0 :       (oldModCount && !mModCount)) {
    3822           1 :     NotifyDocumentListeners(eDocumentStateChanged);
    3823             :   }
    3824           1 :   return NS_OK;
    3825             : }
    3826             : 
    3827             : 
    3828             : NS_IMETHODIMP
    3829           1 : EditorBase::GetModificationCount(int32_t* outModCount)
    3830             : {
    3831           1 :   NS_ENSURE_ARG_POINTER(outModCount);
    3832           1 :   *outModCount = mModCount;
    3833           1 :   return NS_OK;
    3834             : }
    3835             : 
    3836             : 
    3837             : NS_IMETHODIMP
    3838           1 : EditorBase::ResetModificationCount()
    3839             : {
    3840           0 :   bool doNotify = (mModCount != 0);
    3841             : 
    3842           1 :   mModCount = 0;
    3843             : 
    3844           1 :   if (doNotify) {
    3845           1 :     NotifyDocumentListeners(eDocumentStateChanged);
    3846             :   }
    3847           1 :   return NS_OK;
    3848             : }
    3849             : 
    3850             : bool
    3851           0 : EditorBase::AreNodesSameType(nsIContent* aNode1,
    3852             :                              nsIContent* aNode2)
    3853             : {
    3854           0 :   MOZ_ASSERT(aNode1);
    3855           0 :   MOZ_ASSERT(aNode2);
    3856           0 :   return aNode1->NodeInfo()->NameAtom() == aNode2->NodeInfo()->NameAtom();
    3857             : }
    3858             : 
    3859             : // static
    3860             : nsIContent*
    3861           0 : EditorBase::GetNodeAtRangeOffsetPoint(const RawRangeBoundary& aPoint)
    3862             : {
    3863           0 :   if (NS_WARN_IF(!aPoint.IsSet())) {
    3864             :     return nullptr;
    3865             :   }
    3866           0 :   if (aPoint.Container()->GetAsText()) {
    3867           0 :     return aPoint.Container()->AsContent();
    3868             :   }
    3869           0 :   return aPoint.GetChildAtOffset();
    3870             : }
    3871             : 
    3872             : // static
    3873             : EditorRawDOMPoint
    3874           1 : EditorBase::GetStartPoint(Selection* aSelection)
    3875             : {
    3876           0 :   MOZ_ASSERT(aSelection);
    3877             : 
    3878           2 :   if (NS_WARN_IF(!aSelection->RangeCount())) {
    3879             :     return EditorRawDOMPoint();
    3880             :   }
    3881             : 
    3882           1 :   const nsRange* range = aSelection->GetRangeAt(0);
    3883           2 :   if (NS_WARN_IF(!range) ||
    3884           1 :       NS_WARN_IF(!range->IsPositioned())) {
    3885             :     return EditorRawDOMPoint();
    3886             :   }
    3887             : 
    3888           0 :   return EditorRawDOMPoint(range->StartRef());
    3889             : }
    3890             : 
    3891             : // static
    3892             : EditorRawDOMPoint
    3893           0 : EditorBase::GetEndPoint(Selection* aSelection)
    3894             : {
    3895           0 :   MOZ_ASSERT(aSelection);
    3896             : 
    3897           0 :   if (NS_WARN_IF(!aSelection->RangeCount())) {
    3898             :     return EditorRawDOMPoint();
    3899             :   }
    3900             : 
    3901           0 :   const nsRange* range = aSelection->GetRangeAt(0);
    3902           0 :   if (NS_WARN_IF(!range) ||
    3903           0 :       NS_WARN_IF(!range->IsPositioned())) {
    3904             :     return EditorRawDOMPoint();
    3905             :   }
    3906             : 
    3907           0 :   return EditorRawDOMPoint(range->EndRef());
    3908             : }
    3909             : 
    3910             : nsresult
    3911           0 : EditorBase::GetEndChildNode(Selection* aSelection,
    3912             :                             nsIContent** aEndNode)
    3913             : {
    3914           0 :   MOZ_ASSERT(aSelection);
    3915           0 :   MOZ_ASSERT(aEndNode);
    3916             : 
    3917           0 :   *aEndNode = nullptr;
    3918             : 
    3919           0 :   if (NS_WARN_IF(!aSelection->RangeCount())) {
    3920             :     return NS_ERROR_FAILURE;
    3921             :   }
    3922             : 
    3923           0 :   const nsRange* range = aSelection->GetRangeAt(0);
    3924           0 :   if (NS_WARN_IF(!range)) {
    3925             :     return NS_ERROR_FAILURE;
    3926             :   }
    3927             : 
    3928           0 :   if (NS_WARN_IF(!range->IsPositioned())) {
    3929             :     return NS_ERROR_FAILURE;
    3930             :   }
    3931             : 
    3932           0 :   NS_IF_ADDREF(*aEndNode = range->GetChildAtEndOffset());
    3933           0 :   return NS_OK;
    3934             : }
    3935             : 
    3936             : /**
    3937             :  * IsPreformatted() checks the style info for the node for the preformatted
    3938             :  * text style.
    3939             :  */
    3940             : // static
    3941             : bool
    3942           0 : EditorBase::IsPreformatted(nsINode* aNode)
    3943             : {
    3944           0 :   if (NS_WARN_IF(!aNode)) {
    3945             :     return false;
    3946             :   }
    3947             :   // Look at the node (and its parent if it's not an element), and grab its
    3948             :   // ComputedStyle.
    3949           0 :   Element* element = Element::FromNode(aNode);
    3950           0 :   if (!element) {
    3951           0 :     element = aNode->GetParentElement();
    3952           0 :     if (!element) {
    3953             :       return false;
    3954             :     }
    3955             :   }
    3956             : 
    3957             :   RefPtr<ComputedStyle> elementStyle =
    3958           0 :     nsComputedDOMStyle::GetComputedStyleNoFlush(element, nullptr);
    3959           0 :   if (!elementStyle) {
    3960             :     // Consider nodes without a ComputedStyle to be NOT preformatted:
    3961             :     // For instance, this is true of JS tags inside the body (which show
    3962             :     // up as #text nodes but have no ComputedStyle).
    3963             :     return false;
    3964             :   }
    3965             : 
    3966           0 :   const nsStyleText* styleText = elementStyle->StyleText();
    3967             : 
    3968           0 :   return styleText->WhiteSpaceIsSignificant();
    3969             : }
    3970             : 
    3971             : template<typename PT, typename CT>
    3972             : SplitNodeResult
    3973           0 : EditorBase::SplitNodeDeepWithTransaction(
    3974             :               nsIContent& aMostAncestorToSplit,
    3975             :               const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode,
    3976             :               SplitAtEdges aSplitAtEdges)
    3977             : {
    3978           0 :   MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
    3979           0 :   MOZ_ASSERT(aStartOfDeepestRightNode.GetContainer() == &aMostAncestorToSplit ||
    3980             :              EditorUtils::IsDescendantOf(
    3981             :                             *aStartOfDeepestRightNode.GetContainer(),
    3982             :                             aMostAncestorToSplit));
    3983             : 
    3984           0 :   if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
    3985           0 :     return SplitNodeResult(NS_ERROR_INVALID_ARG);
    3986             :   }
    3987             : 
    3988           0 :   nsCOMPtr<nsIContent> newLeftNodeOfMostAncestor;
    3989           0 :   EditorDOMPoint atStartOfRightNode(aStartOfDeepestRightNode);
    3990             :   while (true) {
    3991             :     // Need to insert rules code call here to do things like not split a list
    3992             :     // if you are after the last <li> or before the first, etc.  For now we
    3993             :     // just have some smarts about unneccessarily splitting text nodes, which
    3994             :     // should be universal enough to put straight in this EditorBase routine.
    3995           0 :     if (NS_WARN_IF(!atStartOfRightNode.GetContainerAsContent())) {
    3996           0 :       return SplitNodeResult(NS_ERROR_FAILURE);
    3997             :     }
    3998             :     // If we meet an orphan node before meeting aMostAncestorToSplit, we need
    3999             :     // to stop splitting.  This is a bug of the caller.
    4000           0 :     if (NS_WARN_IF(atStartOfRightNode.GetContainer() != &aMostAncestorToSplit &&
    4001             :                    !atStartOfRightNode.GetContainer()->GetParent())) {
    4002           0 :       return SplitNodeResult(NS_ERROR_FAILURE);
    4003             :     }
    4004             : 
    4005           0 :     nsIContent* currentRightNode = atStartOfRightNode.GetContainerAsContent();
    4006             : 
    4007             :     // If the split point is middle of the node or the node is not a text node
    4008             :     // and we're allowed to create empty element node, split it.
    4009           0 :     if ((aSplitAtEdges == SplitAtEdges::eAllowToCreateEmptyContainer &&
    4010           0 :          !atStartOfRightNode.GetContainerAsText()) ||
    4011           0 :         (!atStartOfRightNode.IsStartOfContainer() &&
    4012           0 :          !atStartOfRightNode.IsEndOfContainer())) {
    4013           0 :       ErrorResult error;
    4014             :       nsCOMPtr<nsIContent> newLeftNode =
    4015           0 :         SplitNodeWithTransaction(atStartOfRightNode, error);
    4016           0 :       if (NS_WARN_IF(error.Failed())) {
    4017           0 :         return SplitNodeResult(error.StealNSResult());
    4018             :       }
    4019             : 
    4020           0 :       if (currentRightNode == &aMostAncestorToSplit) {
    4021             :         // Actually, we split aMostAncestorToSplit.
    4022           0 :         return SplitNodeResult(newLeftNode, &aMostAncestorToSplit);
    4023             :       }
    4024             : 
    4025             :       // Then, try to split its parent before current node.
    4026           0 :       atStartOfRightNode.Set(currentRightNode);
    4027             :     }
    4028             :     // If the split point is end of the node and it is a text node or we're not
    4029             :     // allowed to create empty container node, try to split its parent after it.
    4030           0 :     else if (!atStartOfRightNode.IsStartOfContainer()) {
    4031           0 :       if (currentRightNode == &aMostAncestorToSplit) {
    4032           0 :         return SplitNodeResult(&aMostAncestorToSplit, nullptr);
    4033             :       }
    4034             : 
    4035             :       // Try to split its parent after current node.
    4036           0 :       atStartOfRightNode.Set(currentRightNode);
    4037           0 :       DebugOnly<bool> advanced = atStartOfRightNode.AdvanceOffset();
    4038           0 :       NS_WARNING_ASSERTION(advanced,
    4039             :         "Failed to advance offset after current node");
    4040             :     }
    4041             :     // If the split point is start of the node and it is a text node or we're
    4042             :     // not allowed to create empty container node, try to split its parent.
    4043             :     else {
    4044           0 :       if (currentRightNode == &aMostAncestorToSplit) {
    4045           0 :         return SplitNodeResult(nullptr, &aMostAncestorToSplit);
    4046             :       }
    4047             : 
    4048             :       // Try to split its parent before current node.
    4049           0 :       atStartOfRightNode.Set(currentRightNode);
    4050             :     }
    4051             :   }
    4052             : 
    4053             :   return SplitNodeResult(NS_ERROR_FAILURE);
    4054             : }
    4055             : 
    4056             : EditorDOMPoint
    4057           0 : EditorBase::JoinNodesDeepWithTransaction(nsIContent& aLeftNode,
    4058             :                                          nsIContent& aRightNode)
    4059             : {
    4060             :   // While the rightmost children and their descendants of the left node match
    4061             :   // the leftmost children and their descendants of the right node, join them
    4062             :   // up.
    4063             : 
    4064           0 :   nsCOMPtr<nsIContent> leftNodeToJoin = &aLeftNode;
    4065           0 :   nsCOMPtr<nsIContent> rightNodeToJoin = &aRightNode;
    4066           0 :   nsCOMPtr<nsINode> parentNode = aRightNode.GetParentNode();
    4067             : 
    4068           0 :   EditorDOMPoint ret;
    4069           0 :   while (leftNodeToJoin && rightNodeToJoin && parentNode &&
    4070           0 :          AreNodesSameType(leftNodeToJoin, rightNodeToJoin)) {
    4071           0 :     uint32_t length = leftNodeToJoin->Length();
    4072             : 
    4073             :     // Do the join
    4074           0 :     nsresult rv = JoinNodesWithTransaction(*leftNodeToJoin, *rightNodeToJoin);
    4075           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4076             :       return EditorDOMPoint();
    4077             :     }
    4078             : 
    4079           0 :     ret.Set(rightNodeToJoin, length);
    4080             : 
    4081           0 :     if (parentNode->GetAsText()) {
    4082             :       // We've joined all the way down to text nodes, we're done!
    4083           0 :       return ret;
    4084             :     }
    4085             : 
    4086             :     // Get new left and right nodes, and begin anew
    4087           0 :     parentNode = rightNodeToJoin;
    4088           0 :     rightNodeToJoin = parentNode->GetChildAt_Deprecated(length);
    4089           0 :     if (rightNodeToJoin) {
    4090           0 :       leftNodeToJoin = rightNodeToJoin->GetPreviousSibling();
    4091             :     } else {
    4092           0 :       leftNodeToJoin = nullptr;
    4093             :     }
    4094             : 
    4095             :     // Skip over non-editable nodes
    4096           0 :     while (leftNodeToJoin && !IsEditable(leftNodeToJoin)) {
    4097           0 :       leftNodeToJoin = leftNodeToJoin->GetPreviousSibling();
    4098             :     }
    4099           0 :     if (!leftNodeToJoin) {
    4100           0 :       return ret;
    4101             :     }
    4102             : 
    4103           0 :     while (rightNodeToJoin && !IsEditable(rightNodeToJoin)) {
    4104           0 :       rightNodeToJoin = rightNodeToJoin->GetNextSibling();
    4105             :     }
    4106           0 :     if (!rightNodeToJoin) {
    4107           0 :       return ret;
    4108             :     }
    4109             :   }
    4110             : 
    4111           0 :   if (NS_WARN_IF(!ret.IsSet())) {
    4112             :     return EditorDOMPoint();
    4113             :   }
    4114             : 
    4115           0 :   return ret;
    4116             : }
    4117             : 
    4118             : void
    4119           0 : EditorBase::BeginUpdateViewBatch()
    4120             : {
    4121           0 :   MOZ_ASSERT(mUpdateCount >= 0, "bad state");
    4122             : 
    4123           0 :   if (!mUpdateCount) {
    4124             :     // Turn off selection updates and notifications.
    4125           0 :     RefPtr<Selection> selection = GetSelection();
    4126           0 :     if (selection) {
    4127           0 :       selection->StartBatchChanges();
    4128             :     }
    4129             :   }
    4130             : 
    4131           0 :   mUpdateCount++;
    4132           0 : }
    4133             : 
    4134             : nsresult
    4135           0 : EditorBase::EndUpdateViewBatch()
    4136             : {
    4137           0 :   MOZ_ASSERT(mUpdateCount > 0, "bad state");
    4138             : 
    4139           0 :   if (mUpdateCount <= 0) {
    4140           0 :     mUpdateCount = 0;
    4141           0 :     return NS_ERROR_FAILURE;
    4142             :   }
    4143             : 
    4144           0 :   mUpdateCount--;
    4145             : 
    4146           0 :   if (!mUpdateCount) {
    4147             :     // Turn selection updating and notifications back on.
    4148           0 :     RefPtr<Selection> selection = GetSelection();
    4149           0 :     if (selection) {
    4150           0 :       selection->EndBatchChanges();
    4151             :     }
    4152             :   }
    4153             : 
    4154             :   return NS_OK;
    4155             : }
    4156             : 
    4157             : bool
    4158           1 : EditorBase::GetShouldTxnSetSelection()
    4159             : {
    4160           1 :   return mShouldTxnSetSelection;
    4161             : }
    4162             : 
    4163             : TextComposition*
    4164           0 : EditorBase::GetComposition() const
    4165             : {
    4166           0 :   return mComposition;
    4167             : }
    4168             : 
    4169             : bool
    4170           0 : EditorBase::IsIMEComposing() const
    4171             : {
    4172           0 :   return mComposition && mComposition->IsComposing();
    4173             : }
    4174             : 
    4175             : bool
    4176           0 : EditorBase::ShouldHandleIMEComposition() const
    4177             : {
    4178             :   // When the editor is being reframed, the old value may be restored with
    4179             :   // InsertText().  In this time, the text should be inserted as not a part
    4180             :   // of the composition.
    4181           0 :   return mComposition && mDidPostCreate;
    4182             : }
    4183             : 
    4184             : void
    4185           0 : EditorBase::DoAfterDoTransaction(nsITransaction* aTxn)
    4186             : {
    4187             :   bool isTransientTransaction;
    4188           0 :   MOZ_ALWAYS_SUCCEEDS(aTxn->GetIsTransient(&isTransientTransaction));
    4189             : 
    4190           1 :   if (!isTransientTransaction) {
    4191             :     // we need to deal here with the case where the user saved after some
    4192             :     // edits, then undid one or more times. Then, the undo count is -ve,
    4193             :     // but we can't let a do take it back to zero. So we flip it up to
    4194             :     // a +ve number.
    4195             :     int32_t modCount;
    4196           1 :     GetModificationCount(&modCount);
    4197           1 :     if (modCount < 0) {
    4198           0 :       modCount = -modCount;
    4199             :     }
    4200             : 
    4201             :     // don't count transient transactions
    4202           1 :     MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(1));
    4203             :   }
    4204           1 : }
    4205             : 
    4206             : void
    4207           0 : EditorBase::DoAfterUndoTransaction()
    4208             : {
    4209             :   // all undoable transactions are non-transient
    4210           0 :   MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(-1));
    4211           0 : }
    4212             : 
    4213             : void
    4214           0 : EditorBase::DoAfterRedoTransaction()
    4215             : {
    4216             :   // all redoable transactions are non-transient
    4217           0 :   MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(1));
    4218           0 : }
    4219             : 
    4220             : already_AddRefed<EditAggregateTransaction>
    4221           0 : EditorBase::CreateTxnForDeleteSelection(EDirection aAction,
    4222             :                                         nsINode** aRemovingNode,
    4223             :                                         int32_t* aOffset,
    4224             :                                         int32_t* aLength)
    4225             : {
    4226           0 :   RefPtr<Selection> selection = GetSelection();
    4227           0 :   if (NS_WARN_IF(!selection)) {
    4228             :     return nullptr;
    4229             :   }
    4230             : 
    4231             :   // Check whether the selection is collapsed and we should do nothing:
    4232           0 :   if (NS_WARN_IF(selection->IsCollapsed() && aAction == eNone)) {
    4233             :     return nullptr;
    4234             :   }
    4235             : 
    4236             :   // allocate the out-param transaction
    4237             :   RefPtr<EditAggregateTransaction> aggregateTransaction =
    4238           0 :     EditAggregateTransaction::Create();
    4239             : 
    4240           0 :   for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
    4241           0 :     RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
    4242           0 :     if (NS_WARN_IF(!range)) {
    4243           0 :       return nullptr;
    4244             :     }
    4245             : 
    4246             :     // Same with range as with selection; if it is collapsed and action
    4247             :     // is eNone, do nothing.
    4248           0 :     if (!range->Collapsed()) {
    4249             :       RefPtr<DeleteRangeTransaction> deleteRangeTransaction =
    4250           0 :         DeleteRangeTransaction::Create(*this, *range);
    4251             :       // XXX Oh, not checking if deleteRangeTransaction can modify the range...
    4252           0 :       aggregateTransaction->AppendChild(deleteRangeTransaction);
    4253           0 :     } else if (aAction != eNone) {
    4254             :       // we have an insertion point.  delete the thing in front of it or
    4255             :       // behind it, depending on aAction
    4256             :       // XXX Odd, when there are two or more ranges, this returns the last
    4257             :       //     range information with aRemovingNode, aOffset and aLength.
    4258             :       RefPtr<EditTransactionBase> deleteRangeTransaction =
    4259           0 :         CreateTxnForDeleteRange(range, aAction,
    4260           0 :                                 aRemovingNode, aOffset, aLength);
    4261             :       // XXX When there are two or more ranges and at least one of them is
    4262             :       //     not editable, deleteRangeTransaction may be nullptr.
    4263             :       //     In such case, should we stop removing other ranges too?
    4264           0 :       if (NS_WARN_IF(!deleteRangeTransaction)) {
    4265           0 :         return nullptr;
    4266             :       }
    4267           0 :       aggregateTransaction->AppendChild(deleteRangeTransaction);
    4268             :     }
    4269             :   }
    4270             : 
    4271             :   return aggregateTransaction.forget();
    4272             : }
    4273             : 
    4274             : //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
    4275             : //are not implemented
    4276             : already_AddRefed<EditTransactionBase>
    4277           0 : EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
    4278             :                                     EDirection aAction,
    4279             :                                     nsINode** aRemovingNode,
    4280             :                                     int32_t* aOffset,
    4281             :                                     int32_t* aLength)
    4282             : {
    4283           0 :   MOZ_ASSERT(aAction != eNone);
    4284             : 
    4285             :   // get the node and offset of the insertion point
    4286           0 :   nsCOMPtr<nsINode> node = aRangeToDelete->GetStartContainer();
    4287           0 :   if (NS_WARN_IF(!node)) {
    4288             :     return nullptr;
    4289             :   }
    4290             : 
    4291           0 :   nsIContent* child = aRangeToDelete->GetChildAtStartOffset();
    4292           0 :   int32_t offset = aRangeToDelete->StartOffset();
    4293             : 
    4294             :   // determine if the insertion point is at the beginning, middle, or end of
    4295             :   // the node
    4296             : 
    4297           0 :   uint32_t count = node->Length();
    4298             : 
    4299           0 :   bool isFirst = !offset;
    4300           0 :   bool isLast  = (count == (uint32_t)offset);
    4301             : 
    4302             :   // XXX: if isFirst && isLast, then we'll need to delete the node
    4303             :   //      as well as the 1 child
    4304             : 
    4305             :   // build a transaction for deleting the appropriate data
    4306             :   // XXX: this has to come from rule section
    4307           0 :   if (aAction == ePrevious && isFirst) {
    4308             :     // we're backspacing from the beginning of the node.  Delete the first
    4309             :     // thing to our left
    4310           0 :     nsCOMPtr<nsIContent> priorNode = GetPreviousEditableNode(*node);
    4311           0 :     if (NS_WARN_IF(!priorNode)) {
    4312             :       return nullptr;
    4313             :     }
    4314             : 
    4315             :     // there is a priorNode, so delete its last child (if chardata, delete the
    4316             :     // last char). if it has no children, delete it
    4317           0 :     if (RefPtr<CharacterData> priorNodeAsCharData =
    4318           0 :           CharacterData::FromNode(priorNode)) {
    4319           0 :       uint32_t length = priorNode->Length();
    4320             :       // Bail out for empty chardata XXX: Do we want to do something else?
    4321           0 :       if (NS_WARN_IF(!length)) {
    4322             :         return nullptr;
    4323             :       }
    4324             :       RefPtr<DeleteTextTransaction> deleteTextTransaction =
    4325           0 :         DeleteTextTransaction::MaybeCreateForPreviousCharacter(
    4326           0 :                                  *this, *priorNodeAsCharData, length);
    4327           0 :       if (NS_WARN_IF(!deleteTextTransaction)) {
    4328             :         return nullptr;
    4329             :       }
    4330           0 :       *aOffset = deleteTextTransaction->Offset();
    4331           0 :       *aLength = deleteTextTransaction->LengthToDelete();
    4332           0 :       priorNode.forget(aRemovingNode);
    4333           0 :       return deleteTextTransaction.forget();
    4334             :     }
    4335             : 
    4336             :     // priorNode is not chardata, so tell its parent to delete it
    4337             :     RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
    4338           0 :       DeleteNodeTransaction::MaybeCreate(*this, *priorNode);
    4339           0 :     if (NS_WARN_IF(!deleteNodeTransaction)) {
    4340             :       return nullptr;
    4341             :     }
    4342           0 :     priorNode.forget(aRemovingNode);
    4343           0 :     return deleteNodeTransaction.forget();
    4344             :   }
    4345             : 
    4346           0 :   if (aAction == eNext && isLast) {
    4347             :     // we're deleting from the end of the node.  Delete the first thing to our
    4348             :     // right
    4349           0 :     nsCOMPtr<nsIContent> nextNode = GetNextEditableNode(*node);
    4350           0 :     if (NS_WARN_IF(!nextNode)) {
    4351             :       return nullptr;
    4352             :     }
    4353             : 
    4354             :     // there is a nextNode, so delete its first child (if chardata, delete the
    4355             :     // first char). if it has no children, delete it
    4356           0 :     if (RefPtr<CharacterData> nextNodeAsCharData =
    4357           0 :           CharacterData::FromNode(nextNode)) {
    4358           0 :       uint32_t length = nextNode->Length();
    4359             :       // Bail out for empty chardata XXX: Do we want to do something else?
    4360           0 :       if (NS_WARN_IF(!length)) {
    4361             :         return nullptr;
    4362             :       }
    4363             :       RefPtr<DeleteTextTransaction> deleteTextTransaction =
    4364           0 :         DeleteTextTransaction::MaybeCreateForNextCharacter(
    4365           0 :                                  *this, *nextNodeAsCharData, 0);
    4366           0 :       if (NS_WARN_IF(!deleteTextTransaction)) {
    4367             :         return nullptr;
    4368             :       }
    4369           0 :       *aOffset = deleteTextTransaction->Offset();
    4370           0 :       *aLength = deleteTextTransaction->LengthToDelete();
    4371           0 :       nextNode.forget(aRemovingNode);
    4372           0 :       return deleteTextTransaction.forget();
    4373             :     }
    4374             : 
    4375             :     // nextNode is not chardata, so tell its parent to delete it
    4376             :     RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
    4377           0 :       DeleteNodeTransaction::MaybeCreate(*this, *nextNode);
    4378           0 :     if (NS_WARN_IF(!deleteNodeTransaction)) {
    4379             :       return nullptr;
    4380             :     }
    4381           0 :     nextNode.forget(aRemovingNode);
    4382           0 :     return deleteNodeTransaction.forget();
    4383             :   }
    4384             : 
    4385           0 :   if (RefPtr<CharacterData> nodeAsCharData = CharacterData::FromNode(node)) {
    4386           0 :     if (NS_WARN_IF(aAction != ePrevious && aAction != eNext)) {
    4387             :       return nullptr;
    4388             :     }
    4389             :     // We have chardata, so delete a char at the proper offset
    4390             :     RefPtr<DeleteTextTransaction> deleteTextTransaction =
    4391           0 :       aAction == ePrevious ?
    4392             :         DeleteTextTransaction::MaybeCreateForPreviousCharacter(
    4393             :                                  *this, *nodeAsCharData, offset) :
    4394             :         DeleteTextTransaction::MaybeCreateForNextCharacter(
    4395           0 :                                  *this, *nodeAsCharData, offset);
    4396           0 :     if (NS_WARN_IF(!deleteTextTransaction)) {
    4397             :       return nullptr;
    4398             :     }
    4399           0 :     *aOffset = deleteTextTransaction->Offset();
    4400           0 :     *aLength = deleteTextTransaction->LengthToDelete();
    4401           0 :     node.forget(aRemovingNode);
    4402           0 :     return deleteTextTransaction.forget();
    4403             :   }
    4404             : 
    4405             :   // we're either deleting a node or chardata, need to dig into the next/prev
    4406             :   // node to find out
    4407           0 :   nsCOMPtr<nsINode> selectedNode;
    4408           0 :   if (aAction == ePrevious) {
    4409             :     selectedNode =
    4410           0 :       GetPreviousEditableNode(EditorRawDOMPoint(node, child, offset));
    4411           0 :   } else if (aAction == eNext) {
    4412           0 :     selectedNode = GetNextEditableNode(EditorRawDOMPoint(node, child, offset));
    4413             :   }
    4414             : 
    4415           0 :   while (selectedNode &&
    4416           0 :          selectedNode->IsCharacterData() &&
    4417           0 :          !selectedNode->Length()) {
    4418             :     // Can't delete an empty chardata node (bug 762183)
    4419           0 :     if (aAction == ePrevious) {
    4420           0 :       selectedNode = GetPreviousEditableNode(*selectedNode);
    4421           0 :     } else if (aAction == eNext) {
    4422           0 :       selectedNode = GetNextEditableNode(*selectedNode);
    4423             :     }
    4424             :   }
    4425             : 
    4426           0 :   if (NS_WARN_IF(!selectedNode)) {
    4427             :     return nullptr;
    4428             :   }
    4429             : 
    4430           0 :   if (RefPtr<CharacterData> selectedNodeAsCharData =
    4431           0 :         CharacterData::FromNode(selectedNode)) {
    4432           0 :     if (NS_WARN_IF(aAction != ePrevious && aAction != eNext)) {
    4433             :       return nullptr;
    4434             :     }
    4435             :     // we are deleting from a chardata node, so do a character deletion
    4436           0 :     uint32_t position = 0;
    4437           0 :     if (aAction == ePrevious) {
    4438           0 :       position = selectedNode->Length();
    4439             :     }
    4440             :     RefPtr<DeleteTextTransaction> deleteTextTransaction =
    4441           0 :       aAction == ePrevious ?
    4442             :         DeleteTextTransaction::MaybeCreateForPreviousCharacter(
    4443             :                                  *this, *selectedNodeAsCharData, position) :
    4444             :         DeleteTextTransaction::MaybeCreateForNextCharacter(
    4445           0 :                                  *this, *selectedNodeAsCharData, position);
    4446           0 :     if (NS_WARN_IF(!deleteTextTransaction)) {
    4447             :       return nullptr;
    4448             :     }
    4449           0 :     *aOffset = deleteTextTransaction->Offset();
    4450           0 :     *aLength = deleteTextTransaction->LengthToDelete();
    4451           0 :     selectedNode.forget(aRemovingNode);
    4452           0 :     return deleteTextTransaction.forget();
    4453             :   }
    4454             : 
    4455             :   RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
    4456           0 :     DeleteNodeTransaction::MaybeCreate(*this, *selectedNode);
    4457           0 :   if (NS_WARN_IF(!deleteNodeTransaction)) {
    4458             :     return nullptr;
    4459             :   }
    4460           0 :   selectedNode.forget(aRemovingNode);
    4461           0 :   return deleteNodeTransaction.forget();
    4462             : }
    4463             : 
    4464             : nsresult
    4465           0 : EditorBase::CreateRange(nsINode* aStartContainer,
    4466             :                         int32_t aStartOffset,
    4467             :                         nsINode* aEndContainer,
    4468             :                         int32_t aEndOffset,
    4469             :                         nsRange** aRange)
    4470             : {
    4471           0 :   return nsRange::CreateRange(aStartContainer, aStartOffset,
    4472           0 :                               aEndContainer, aEndOffset, aRange);
    4473             : }
    4474             : 
    4475             : nsresult
    4476           0 : EditorBase::AppendNodeToSelectionAsRange(nsINode* aNode)
    4477             : {
    4478           0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    4479           0 :   RefPtr<Selection> selection = GetSelection();
    4480           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    4481             : 
    4482           0 :   nsCOMPtr<nsINode> parentNode = aNode->GetParentNode();
    4483           0 :   NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
    4484             : 
    4485           0 :   int32_t offset = GetChildOffset(aNode, parentNode);
    4486             : 
    4487           0 :   RefPtr<nsRange> range;
    4488           0 :   nsresult rv = CreateRange(parentNode, offset, parentNode, offset + 1,
    4489           0 :                             getter_AddRefs(range));
    4490           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4491           0 :   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
    4492             : 
    4493           0 :   ErrorResult err;
    4494           0 :   selection->AddRange(*range, err);
    4495           0 :   return err.StealNSResult();
    4496             : }
    4497             : 
    4498             : nsresult
    4499           0 : EditorBase::ClearSelection()
    4500             : {
    4501           0 :   RefPtr<Selection> selection = GetSelection();
    4502           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    4503           0 :   ErrorResult rv;
    4504           0 :   selection->RemoveAllRanges(rv);
    4505           0 :   return rv.StealNSResult();
    4506             : }
    4507             : 
    4508             : already_AddRefed<Element>
    4509           0 : EditorBase::CreateHTMLContent(nsAtom* aTag)
    4510             : {
    4511           1 :   MOZ_ASSERT(aTag);
    4512             : 
    4513           0 :   nsCOMPtr<nsIDocument> doc = GetDocument();
    4514           1 :   if (!doc) {
    4515             :     return nullptr;
    4516             :   }
    4517             : 
    4518             :   // XXX Wallpaper over editor bug (editor tries to create elements with an
    4519             :   //     empty nodename).
    4520           1 :   if (aTag == nsGkAtoms::_empty) {
    4521           0 :     NS_ERROR("Don't pass an empty tag to EditorBase::CreateHTMLContent, "
    4522             :              "check caller.");
    4523             :     return nullptr;
    4524             :   }
    4525             : 
    4526           1 :   return doc->CreateElem(nsDependentAtomString(aTag), nullptr,
    4527           1 :                          kNameSpaceID_XHTML);
    4528             : }
    4529             : 
    4530             : // static
    4531             : already_AddRefed<nsTextNode>
    4532           0 : EditorBase::CreateTextNode(nsIDocument& aDocument,
    4533             :                            const nsAString& aData)
    4534             : {
    4535           0 :   RefPtr<nsTextNode> text = aDocument.CreateEmptyTextNode();
    4536           0 :   text->MarkAsMaybeModifiedFrequently();
    4537             :   // Don't notify; this node is still being created.
    4538           0 :   text->SetText(aData, false);
    4539           0 :   return text.forget();
    4540             : }
    4541             : 
    4542             : NS_IMETHODIMP
    4543           0 : EditorBase::SetAttributeOrEquivalent(Element* aElement,
    4544             :                                      const nsAString& aAttribute,
    4545             :                                      const nsAString& aValue,
    4546             :                                      bool aSuppressTransaction)
    4547             : {
    4548           0 :   if (NS_WARN_IF(!aElement)) {
    4549             :     return NS_ERROR_NULL_POINTER;
    4550             :   }
    4551           0 :   RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
    4552           0 :   return SetAttributeOrEquivalent(aElement, attribute, aValue,
    4553           0 :                                   aSuppressTransaction);
    4554             : }
    4555             : 
    4556             : NS_IMETHODIMP
    4557           0 : EditorBase::RemoveAttributeOrEquivalent(Element* aElement,
    4558             :                                         const nsAString& aAttribute,
    4559             :                                         bool aSuppressTransaction)
    4560             : {
    4561           0 :   if (NS_WARN_IF(!aElement)) {
    4562             :     return NS_ERROR_NULL_POINTER;
    4563             :   }
    4564           0 :   RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
    4565           0 :   return RemoveAttributeOrEquivalent(aElement, attribute, aSuppressTransaction);
    4566             : }
    4567             : 
    4568             : nsresult
    4569           0 : EditorBase::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent)
    4570             : {
    4571             :   // NOTE: When you change this method, you should also change:
    4572             :   //   * editor/libeditor/tests/test_texteditor_keyevent_handling.html
    4573             :   //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
    4574             :   //
    4575             :   // And also when you add new key handling, you need to change the subclass's
    4576             :   // HandleKeyPressEvent()'s switch statement.
    4577             : 
    4578           0 :   if (NS_WARN_IF(!aKeyboardEvent)) {
    4579             :     return NS_ERROR_UNEXPECTED;
    4580             :   }
    4581           0 :   MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
    4582             :              "HandleKeyPressEvent gets non-keypress event");
    4583             : 
    4584             :   // if we are readonly or disabled, then do nothing.
    4585           0 :   if (IsReadonly() || IsDisabled()) {
    4586             :     // consume backspace for disabled and readonly textfields, to prevent
    4587             :     // back in history, which could be confusing to users
    4588           0 :     if (aKeyboardEvent->mKeyCode == NS_VK_BACK) {
    4589           0 :       aKeyboardEvent->PreventDefault();
    4590             :     }
    4591             :     return NS_OK;
    4592             :   }
    4593             : 
    4594           0 :   switch (aKeyboardEvent->mKeyCode) {
    4595             :     case NS_VK_META:
    4596             :     case NS_VK_WIN:
    4597             :     case NS_VK_SHIFT:
    4598             :     case NS_VK_CONTROL:
    4599             :     case NS_VK_ALT:
    4600           0 :       aKeyboardEvent->PreventDefault(); // consumed
    4601           0 :       return NS_OK;
    4602             :   }
    4603             :   return NS_OK;
    4604             : }
    4605             : 
    4606             : nsresult
    4607           1 : EditorBase::HandleInlineSpellCheck(EditSubAction aEditSubAction,
    4608             :                                    Selection& aSelection,
    4609             :                                    nsINode* previousSelectedNode,
    4610             :                                    uint32_t previousSelectedOffset,
    4611             :                                    nsINode* aStartContainer,
    4612             :                                    uint32_t aStartOffset,
    4613             :                                    nsINode* aEndContainer,
    4614             :                                    uint32_t aEndOffset)
    4615             : {
    4616           2 :   if (!mInlineSpellChecker) {
    4617             :     return NS_OK;
    4618             :   }
    4619           0 :   return mInlineSpellChecker->SpellCheckAfterEditorChange(
    4620             :                                 aEditSubAction, aSelection,
    4621             :                                 previousSelectedNode, previousSelectedOffset,
    4622             :                                 aStartContainer, aStartOffset, aEndContainer,
    4623           0 :                                 aEndOffset);
    4624             : }
    4625             : 
    4626             : already_AddRefed<nsIContent>
    4627           0 : EditorBase::FindSelectionRoot(nsINode* aNode)
    4628             : {
    4629           4 :   nsCOMPtr<nsIContent> rootContent = GetRoot();
    4630           4 :   return rootContent.forget();
    4631             : }
    4632             : 
    4633             : void
    4634           1 : EditorBase::InitializeSelectionAncestorLimit(Selection& aSelection,
    4635             :                                              nsIContent& aAncestorLimit)
    4636             : {
    4637           0 :   aSelection.SetAncestorLimiter(&aAncestorLimit);
    4638           0 : }
    4639             : 
    4640             : nsresult
    4641           0 : EditorBase::InitializeSelection(EventTarget* aFocusEventTarget)
    4642             : {
    4643           2 :   nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget);
    4644           1 :   NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG);
    4645           0 :   nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode);
    4646           0 :   if (!selectionRootContent) {
    4647             :     return NS_OK;
    4648             :   }
    4649             : 
    4650           0 :   RefPtr<Selection> selection = GetSelection();
    4651           1 :   NS_ENSURE_STATE(selection);
    4652             : 
    4653           2 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    4654           1 :   NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
    4655             : 
    4656             :   nsCOMPtr<nsISelectionController> selectionController =
    4657           0 :     GetSelectionController();
    4658           0 :   if (NS_WARN_IF(!selectionController)) {
    4659             :     return NS_ERROR_FAILURE;
    4660             :   }
    4661             : 
    4662             :   // Init the caret
    4663           3 :   RefPtr<nsCaret> caret = presShell->GetCaret();
    4664           1 :   NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED);
    4665           1 :   caret->SetIgnoreUserModify(false);
    4666           0 :   caret->SetSelection(selection);
    4667           2 :   selectionController->SetCaretReadOnly(IsReadonly());
    4668           1 :   selectionController->SetCaretEnabled(true);
    4669             : 
    4670             :   // Init selection
    4671           1 :   selectionController->SetDisplaySelection(
    4672           1 :                          nsISelectionController::SELECTION_ON);
    4673           0 :   selectionController->SetSelectionFlags(
    4674           1 :                          nsISelectionDisplay::DISPLAY_ALL);
    4675           0 :   selectionController->RepaintSelection(
    4676           0 :                          nsISelectionController::SELECTION_NORMAL);
    4677             : 
    4678             :   // If the computed selection root isn't root content, we should set it
    4679             :   // as selection ancestor limit.  However, if that is root element, it means
    4680             :   // there is not limitation of the selection, then, we must set nullptr.
    4681             :   // NOTE: If we set a root element to the ancestor limit, some selection
    4682             :   // methods don't work fine.
    4683           1 :   if (selectionRootContent->GetParent()) {
    4684           1 :     InitializeSelectionAncestorLimit(*selection, *selectionRootContent);
    4685             :   } else {
    4686           0 :     selection->SetAncestorLimiter(nullptr);
    4687             :   }
    4688             : 
    4689             :   // If there is composition when this is called, we may need to restore IME
    4690             :   // selection because if the editor is reframed, this already forgot IME
    4691             :   // selection and the transaction.
    4692           2 :   if (mComposition && mComposition->IsMovingToNewTextNode()) {
    4693             :     // We need to look for the new text node from current selection.
    4694             :     // XXX If selection is changed during reframe, this doesn't work well!
    4695           0 :     nsRange* firstRange = selection->GetRangeAt(0);
    4696           0 :     if (NS_WARN_IF(!firstRange)) {
    4697           0 :       return NS_ERROR_FAILURE;
    4698             :     }
    4699           0 :     EditorRawDOMPoint atStartOfFirstRange(firstRange->StartRef());
    4700             :     EditorRawDOMPoint betterInsertionPoint =
    4701           0 :       FindBetterInsertionPoint(atStartOfFirstRange);
    4702           0 :     Text* textNode = betterInsertionPoint.GetContainerAsText();
    4703           0 :     MOZ_ASSERT(textNode,
    4704             :                "There must be text node if composition string is not empty");
    4705           0 :     if (textNode) {
    4706           0 :       MOZ_ASSERT(textNode->Length() >= mComposition->XPEndOffsetInTextNode(),
    4707             :                  "The text node must be different from the old text node");
    4708           0 :       CompositionTransaction::SetIMESelection(
    4709             :                                 *this, textNode,
    4710             :                                 mComposition->XPOffsetInTextNode(),
    4711             :                                 mComposition->XPLengthInTextNode(),
    4712           0 :                                 mComposition->GetRanges());
    4713             :     }
    4714             :   }
    4715             : 
    4716             :   return NS_OK;
    4717             : }
    4718             : 
    4719           0 : class RepaintSelectionRunner final : public Runnable {
    4720             : public:
    4721           0 :   explicit RepaintSelectionRunner(nsISelectionController* aSelectionController)
    4722           0 :     : Runnable("RepaintSelectionRunner")
    4723           0 :     , mSelectionController(aSelectionController)
    4724             :   {
    4725           0 :   }
    4726             : 
    4727           0 :   NS_IMETHOD Run() override
    4728             :   {
    4729           0 :     nsCOMPtr<nsIPresShell> shell = do_QueryInterface(mSelectionController);
    4730           0 :     if (!shell || shell->IsDestroying()) {
    4731             :       return NS_OK;
    4732             :     }
    4733             : 
    4734           0 :     mSelectionController->RepaintSelection(
    4735           0 :                             nsISelectionController::SELECTION_NORMAL);
    4736           0 :     return NS_OK;
    4737             :   }
    4738             : 
    4739             : private:
    4740             :   nsCOMPtr<nsISelectionController> mSelectionController;
    4741             : };
    4742             : 
    4743             : nsresult
    4744           0 : EditorBase::FinalizeSelection()
    4745             : {
    4746             :   nsCOMPtr<nsISelectionController> selectionController =
    4747           0 :     GetSelectionController();
    4748           0 :   if (NS_WARN_IF(!selectionController)) {
    4749             :     return NS_ERROR_FAILURE;
    4750             :   }
    4751             : 
    4752           0 :   RefPtr<Selection> selection = GetSelection();
    4753           0 :   NS_ENSURE_STATE(selection);
    4754             : 
    4755           0 :   selection->SetAncestorLimiter(nullptr);
    4756             : 
    4757           0 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    4758           0 :   NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
    4759             : 
    4760           0 :   selectionController->SetCaretEnabled(false);
    4761             : 
    4762           0 :   nsFocusManager* fm = nsFocusManager::GetFocusManager();
    4763           0 :   NS_ENSURE_TRUE(fm, NS_ERROR_NOT_INITIALIZED);
    4764           0 :   fm->UpdateCaretForCaretBrowsingMode();
    4765             : 
    4766           0 :   if (!HasIndependentSelection()) {
    4767             :     // If this editor doesn't have an independent selection, i.e., it must
    4768             :     // mean that it is an HTML editor, the selection controller is shared with
    4769             :     // presShell.  So, even this editor loses focus, other part of the document
    4770             :     // may still have focus.
    4771           0 :     nsCOMPtr<nsIDocument> doc = GetDocument();
    4772           0 :     ErrorResult ret;
    4773           0 :     if (!doc || !doc->HasFocus(ret)) {
    4774             :       // If the document already lost focus, mark the selection as disabled.
    4775           0 :       selectionController->SetDisplaySelection(
    4776           0 :                              nsISelectionController::SELECTION_DISABLED);
    4777             :     } else {
    4778             :       // Otherwise, mark selection as normal because outside of a
    4779             :       // contenteditable element should be selected with normal selection
    4780             :       // color after here.
    4781           0 :       selectionController->SetDisplaySelection(
    4782           0 :                              nsISelectionController::SELECTION_ON);
    4783             :     }
    4784           0 :   } else if (IsFormWidget() || IsPasswordEditor() ||
    4785           0 :              IsReadonly() || IsDisabled() || IsInputFiltered()) {
    4786             :     // In <input> or <textarea>, the independent selection should be hidden
    4787             :     // while this editor doesn't have focus.
    4788           0 :     selectionController->SetDisplaySelection(
    4789           0 :                            nsISelectionController::SELECTION_HIDDEN);
    4790             :   } else {
    4791             :     // Otherwise, although we're not sure how this case happens, the
    4792             :     // independent selection should be marked as disabled.
    4793           0 :     selectionController->SetDisplaySelection(
    4794           0 :                            nsISelectionController::SELECTION_DISABLED);
    4795             :   }
    4796             : 
    4797             :   // FinalizeSelection might be called from ContentRemoved even if selection
    4798             :   // isn't updated.  So we need to call RepaintSelection after updated it.
    4799             :   nsContentUtils::AddScriptRunner(
    4800           0 :                     new RepaintSelectionRunner(selectionController));
    4801           0 :   return NS_OK;
    4802             : }
    4803             : 
    4804             : Element*
    4805           0 : EditorBase::GetEditorRoot()
    4806             : {
    4807           0 :   return GetRoot();
    4808             : }
    4809             : 
    4810             : Element*
    4811           1 : EditorBase::GetExposedRoot()
    4812             : {
    4813           0 :   Element* rootElement = GetRoot();
    4814             : 
    4815             :   // For plaintext editors, we need to ask the input/textarea element directly.
    4816           1 :   if (rootElement && rootElement->IsRootOfNativeAnonymousSubtree()) {
    4817           1 :     rootElement = rootElement->GetParent()->AsElement();
    4818             :   }
    4819             : 
    4820           1 :   return rootElement;
    4821             : }
    4822             : 
    4823             : nsresult
    4824           0 : EditorBase::DetermineCurrentDirection()
    4825             : {
    4826             :   // Get the current root direction from its frame
    4827           0 :   nsIContent* rootElement = GetExposedRoot();
    4828           0 :   NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
    4829             : 
    4830             :   // If we don't have an explicit direction, determine our direction
    4831             :   // from the content's direction
    4832           0 :   if (!IsRightToLeft() && !IsLeftToRight()) {
    4833           0 :     nsIFrame* frame = rootElement->GetPrimaryFrame();
    4834           0 :     NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    4835             : 
    4836             :     // Set the flag here, to enable us to use the same code path below.
    4837             :     // It will be flipped before returning from the function.
    4838           0 :     if (frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
    4839           0 :       mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
    4840             :     } else {
    4841           0 :       mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
    4842             :     }
    4843             :   }
    4844             : 
    4845             :   return NS_OK;
    4846             : }
    4847             : 
    4848             : nsresult
    4849           0 : EditorBase::ToggleTextDirection()
    4850             : {
    4851             :   // XXX Oddly, Chrome does not dispatch beforeinput event in this case but
    4852             :   //     dispatches input event.
    4853             : 
    4854           0 :   nsresult rv = DetermineCurrentDirection();
    4855           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4856             :     return rv;
    4857             :   }
    4858             : 
    4859           0 :   if (IsRightToLeft()) {
    4860           0 :     nsresult rv = SetTextDirectionTo(TextDirection::eLTR);
    4861           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4862             :       return rv;
    4863             :     }
    4864           0 :   } else if (IsLeftToRight()) {
    4865           0 :     nsresult rv = SetTextDirectionTo(TextDirection::eRTL);
    4866           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4867             :       return rv;
    4868             :     }
    4869             :   }
    4870             : 
    4871             :   // XXX When we don't change the text direction, do we really need to
    4872             :   //     dispatch input event?
    4873           0 :   FireInputEvent();
    4874             : 
    4875           0 :   return NS_OK;
    4876             : }
    4877             : 
    4878             : void
    4879           0 : EditorBase::SwitchTextDirectionTo(TextDirection aTextDirection)
    4880             : {
    4881             :   // XXX Oddly, Chrome does not dispatch beforeinput event in this case but
    4882             :   //     dispatches input event.
    4883             : 
    4884           0 :   nsresult rv = DetermineCurrentDirection();
    4885           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4886             :     return;
    4887             :   }
    4888             : 
    4889           0 :   if (aTextDirection == TextDirection::eLTR && IsRightToLeft()) {
    4890           0 :     if (NS_WARN_IF(NS_FAILED(SetTextDirectionTo(aTextDirection)))) {
    4891             :       return;
    4892             :     }
    4893           0 :   } else if (aTextDirection == TextDirection::eRTL && IsLeftToRight()) {
    4894           0 :     if (NS_WARN_IF(NS_FAILED(SetTextDirectionTo(aTextDirection)))) {
    4895             :       return;
    4896             :     }
    4897             :   }
    4898             : 
    4899             :   // XXX When we don't change the text direction, do we really need to
    4900             :   //     dispatch input event?
    4901           0 :   FireInputEvent();
    4902             : }
    4903             : 
    4904             : nsresult
    4905           0 : EditorBase::SetTextDirectionTo(TextDirection aTextDirection)
    4906             : {
    4907           0 :   Element* rootElement = GetExposedRoot();
    4908             : 
    4909           0 :   if (aTextDirection == TextDirection::eLTR) {
    4910           0 :     NS_ASSERTION(!IsLeftToRight(), "Unexpected mutually exclusive flag");
    4911           0 :     mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
    4912           0 :     mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
    4913           0 :     nsresult rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
    4914           0 :                                        NS_LITERAL_STRING("ltr"), true);
    4915           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4916             :       return rv;
    4917             :     }
    4918           0 :     return NS_OK;
    4919             :   }
    4920             : 
    4921           0 :   if (aTextDirection == TextDirection::eRTL) {
    4922           0 :     NS_ASSERTION(!IsRightToLeft(), "Unexpected mutually exclusive flag");
    4923           0 :     mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
    4924           0 :     mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
    4925           0 :     nsresult rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
    4926           0 :                                        NS_LITERAL_STRING("rtl"), true);
    4927           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4928             :       return rv;
    4929             :     }
    4930           0 :     return NS_OK;
    4931             :   }
    4932             : 
    4933             :   return NS_OK;
    4934             : }
    4935             : 
    4936             : bool
    4937           7 : EditorBase::IsModifiableNode(nsINode* aNode)
    4938             : {
    4939           0 :   return true;
    4940             : }
    4941             : 
    4942             : nsIContent*
    4943           4 : EditorBase::GetFocusedContent()
    4944             : {
    4945           4 :   EventTarget* piTarget = GetDOMEventTarget();
    4946           4 :   if (!piTarget) {
    4947             :     return nullptr;
    4948             :   }
    4949             : 
    4950           0 :   nsFocusManager* fm = nsFocusManager::GetFocusManager();
    4951           4 :   NS_ENSURE_TRUE(fm, nullptr);
    4952             : 
    4953           8 :   nsIContent* content = fm->GetFocusedElement();
    4954           4 :   MOZ_ASSERT((content == piTarget) == SameCOMIdentity(content, piTarget));
    4955             : 
    4956           4 :   return (content == piTarget) ? content : nullptr;
    4957             : }
    4958             : 
    4959             : already_AddRefed<nsIContent>
    4960           0 : EditorBase::GetFocusedContentForIME()
    4961             : {
    4962           4 :   nsCOMPtr<nsIContent> content = GetFocusedContent();
    4963           4 :   return content.forget();
    4964             : }
    4965             : 
    4966             : bool
    4967           0 : EditorBase::IsActiveInDOMWindow()
    4968             : {
    4969           0 :   EventTarget* piTarget = GetDOMEventTarget();
    4970           0 :   if (!piTarget) {
    4971             :     return false;
    4972             :   }
    4973             : 
    4974           0 :   nsFocusManager* fm = nsFocusManager::GetFocusManager();
    4975           0 :   NS_ENSURE_TRUE(fm, false);
    4976             : 
    4977           0 :   nsCOMPtr<nsIDocument> document = GetDocument();
    4978           0 :   if (NS_WARN_IF(!document)) {
    4979             :     return false;
    4980             :   }
    4981           0 :   nsPIDOMWindowOuter* ourWindow = document->GetWindow();
    4982           0 :   nsCOMPtr<nsPIDOMWindowOuter> win;
    4983             :   nsIContent* content =
    4984           0 :     nsFocusManager::GetFocusedDescendant(ourWindow,
    4985             :                                          nsFocusManager::eOnlyCurrentWindow,
    4986           0 :                                          getter_AddRefs(win));
    4987           0 :   return SameCOMIdentity(content, piTarget);
    4988             : }
    4989             : 
    4990             : bool
    4991           0 : EditorBase::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
    4992             : {
    4993             :   // If the event is trusted, the event should always cause input.
    4994           0 :   if (NS_WARN_IF(!aGUIEvent)) {
    4995             :     return false;
    4996             :   }
    4997             : 
    4998             :   // If this is dispatched by using cordinates but this editor doesn't have
    4999             :   // focus, we shouldn't handle it.
    5000           0 :   if (aGUIEvent->IsUsingCoordinates()) {
    5001           0 :     nsIContent* focusedContent = GetFocusedContent();
    5002           0 :     if (!focusedContent) {
    5003             :       return false;
    5004             :     }
    5005             :   }
    5006             : 
    5007             :   // If a composition event isn't dispatched via widget, we need to ignore them
    5008             :   // since they cannot be managed by TextComposition. E.g., the event was
    5009             :   // created by chrome JS.
    5010             :   // Note that if we allow to handle such events, editor may be confused by
    5011             :   // strange event order.
    5012           0 :   bool needsWidget = false;
    5013           0 :   switch (aGUIEvent->mMessage) {
    5014             :     case eUnidentifiedEvent:
    5015             :       // If events are not created with proper event interface, their message
    5016             :       // are initialized with eUnidentifiedEvent.  Let's ignore such event.
    5017             :       return false;
    5018             :     case eCompositionStart:
    5019             :     case eCompositionEnd:
    5020             :     case eCompositionUpdate:
    5021             :     case eCompositionChange:
    5022             :     case eCompositionCommitAsIs:
    5023             :       // Don't allow composition events whose internal event are not
    5024             :       // WidgetCompositionEvent.
    5025           0 :       if (!aGUIEvent->AsCompositionEvent()) {
    5026             :         return false;
    5027             :       }
    5028             :       needsWidget = true;
    5029             :       break;
    5030             :     default:
    5031             :       break;
    5032             :   }
    5033           0 :   if (needsWidget && !aGUIEvent->mWidget) {
    5034             :     return false;
    5035             :   }
    5036             : 
    5037             :   // Accept all trusted events.
    5038           0 :   if (aGUIEvent->IsTrusted()) {
    5039             :     return true;
    5040             :   }
    5041             : 
    5042             :   // Ignore untrusted mouse event.
    5043             :   // XXX Why are we handling other untrusted input events?
    5044           0 :   if (aGUIEvent->AsMouseEventBase()) {
    5045             :     return false;
    5046             :   }
    5047             : 
    5048             :   // Otherwise, we shouldn't handle any input events when we're not an active
    5049             :   // element of the DOM window.
    5050           0 :   return IsActiveInDOMWindow();
    5051             : }
    5052             : 
    5053             : void
    5054           1 : EditorBase::OnFocus(EventTarget* aFocusEventTarget)
    5055             : {
    5056           1 :   InitializeSelection(aFocusEventTarget);
    5057           0 :   mSpellCheckerDictionaryUpdated = false;
    5058           0 :   if (mInlineSpellChecker && CanEnableSpellCheck()) {
    5059           0 :     mInlineSpellChecker->UpdateCurrentDictionary();
    5060           0 :     mSpellCheckerDictionaryUpdated = true;
    5061             :   }
    5062           0 : }
    5063             : 
    5064             : int32_t
    5065           0 : EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
    5066             : {
    5067           0 :   MOZ_ASSERT(aTextNode, "aTextNode must not be nullptr");
    5068             : 
    5069           0 :   nsISelectionController* selectionController = GetSelectionController();
    5070           0 :   if (NS_WARN_IF(!selectionController)) {
    5071             :     return -1;
    5072             :   }
    5073             : 
    5074             :   uint32_t minOffset = UINT32_MAX;
    5075             :   static const SelectionType kIMESelectionTypes[] = {
    5076             :     SelectionType::eIMERawClause,
    5077             :     SelectionType::eIMESelectedRawClause,
    5078             :     SelectionType::eIMEConvertedClause,
    5079             :     SelectionType::eIMESelectedClause
    5080             :   };
    5081             :   for (auto selectionType : kIMESelectionTypes) {
    5082             :     RefPtr<Selection> selection = GetSelection(selectionType);
    5083             :     if (!selection) {
    5084             :       continue;
    5085             :     }
    5086             :     for (uint32_t i = 0; i < selection->RangeCount(); i++) {
    5087             :       RefPtr<nsRange> range = selection->GetRangeAt(i);
    5088             :       if (NS_WARN_IF(!range)) {
    5089             :         continue;
    5090             :       }
    5091             :       if (NS_WARN_IF(range->GetStartContainer() != aTextNode)) {
    5092             :         // ignore the start offset...
    5093             :       } else {
    5094             :         minOffset = std::min(minOffset, range->StartOffset());
    5095             :       }
    5096             :       if (NS_WARN_IF(range->GetEndContainer() != aTextNode)) {
    5097             :         // ignore the end offset...
    5098             :       } else {
    5099             :         minOffset = std::min(minOffset, range->EndOffset());
    5100             :       }
    5101             :     }
    5102             :   }
    5103             :   return minOffset < INT32_MAX ? minOffset : -1;
    5104             : }
    5105             : 
    5106             : void
    5107             : EditorBase::HideCaret(bool aHide)
    5108             : {
    5109             :   if (mHidingCaret == aHide) {
    5110             :     return;
    5111             :   }
    5112             : 
    5113             :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    5114             :   NS_ENSURE_TRUE_VOID(presShell);
    5115             :   RefPtr<nsCaret> caret = presShell->GetCaret();
    5116             :   NS_ENSURE_TRUE_VOID(caret);
    5117             : 
    5118             :   mHidingCaret = aHide;
    5119             :   if (aHide) {
    5120             :     caret->AddForceHide();
    5121             :   } else {
    5122             :     caret->RemoveForceHide();
    5123             :   }
    5124             : }
    5125             : 
    5126             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952