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

Generated by: LCOV version 1.13