tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EditorBase.cpp (292624B)


      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 "EditorBase.h"
      7 
      8 #include <stdio.h>   // for nullptr, stdout
      9 #include <string.h>  // for strcmp
     10 
     11 #include "AutoClonedRangeArray.h"  // for AutoClonedRangeArray and AutoClonedSelectionRangeArray
     12 #include "AutoSelectionRestorer.h"
     13 #include "ChangeAttributeTransaction.h"
     14 #include "CompositionTransaction.h"
     15 #include "DeleteContentTransactionBase.h"
     16 #include "DeleteMultipleRangesTransaction.h"
     17 #include "DeleteNodeTransaction.h"
     18 #include "DeleteRangeTransaction.h"
     19 #include "DeleteTextTransaction.h"
     20 #include "EditAction.h"           // for EditSubAction
     21 #include "EditorDOMAPIWrapper.h"  // for AutoCharacterDataAPIWrapper, etc
     22 #include "EditorDOMPoint.h"       // for EditorDOMPoint
     23 #include "EditorForwards.h"
     24 #include "EditorUtils.h"          // for various helper classes.
     25 #include "EditTransactionBase.h"  // for EditTransactionBase
     26 #include "EditorEventListener.h"  // for EditorEventListener
     27 #include "HTMLEditor.h"           // for HTMLEditor
     28 #include "HTMLEditorInlines.h"
     29 #include "HTMLEditUtils.h"           // for HTMLEditUtils
     30 #include "InsertNodeTransaction.h"   // for InsertNodeTransaction
     31 #include "InsertTextTransaction.h"   // for InsertTextTransaction
     32 #include "JoinNodesTransaction.h"    // for JoinNodesTransaction
     33 #include "PlaceholderTransaction.h"  // for PlaceholderTransaction
     34 #include "SplitNodeTransaction.h"    // for SplitNodeTransaction
     35 #include "TextEditor.h"              // for TextEditor
     36 
     37 #include "ErrorList.h"
     38 #include "gfxFontUtils.h"  // for gfxFontUtils
     39 #include "mozilla/Assertions.h"
     40 #include "mozilla/AsyncEventDispatcher.h"
     41 #include "mozilla/EditorDOMPoint.h"
     42 #include "mozilla/intl/BidiEmbeddingLevel.h"
     43 #include "mozilla/BasePrincipal.h"            // for BasePrincipal
     44 #include "mozilla/ComposerCommandsUpdater.h"  // for ComposerCommandsUpdater
     45 #include "mozilla/ContentEvents.h"            // for InternalClipboardEvent
     46 #include "mozilla/DebugOnly.h"                // for DebugOnly
     47 #include "mozilla/EditorSpellCheck.h"         // for EditorSpellCheck
     48 #include "mozilla/Encoding.h"  // for Encoding (used in Document::GetDocumentCharacterSet)
     49 #include "mozilla/EventDispatcher.h"        // for EventChainPreVisitor, etc.
     50 #include "mozilla/FlushType.h"              // for FlushType::Frames
     51 #include "mozilla/IMEContentObserver.h"     // for IMEContentObserver
     52 #include "mozilla/IMEStateManager.h"        // for IMEStateManager
     53 #include "mozilla/InputEventOptions.h"      // for InputEventOptions
     54 #include "mozilla/IntegerRange.h"           // for IntegerRange
     55 #include "mozilla/Logging.h"                //for MOZ_LOG
     56 #include "mozilla/mozalloc.h"               // for operator new, etc.
     57 #include "mozilla/mozInlineSpellChecker.h"  // for mozInlineSpellChecker
     58 #include "mozilla/mozSpellChecker.h"        // for mozSpellChecker
     59 #include "mozilla/Preferences.h"            // for Preferences
     60 #include "mozilla/PresShell.h"              // for PresShell
     61 #include "mozilla/RangeBoundary.h"       // for RawRangeBoundary, RangeBoundary
     62 #include "mozilla/ScopeExit.h"           // for MakeScopeExit
     63 #include "mozilla/Services.h"            // for GetObserverService
     64 #include "mozilla/StaticPrefs_bidi.h"    // for StaticPrefs::bidi_*
     65 #include "mozilla/StaticPrefs_dom.h"     // for StaticPrefs::dom_*
     66 #include "mozilla/StaticPrefs_editor.h"  // for StaticPrefs::editor_*
     67 #include "mozilla/StaticPrefs_layout.h"  // for StaticPrefs::layout_*
     68 #include "mozilla/TextComposition.h"     // for TextComposition
     69 #include "mozilla/TextControlElement.h"  // for TextControlElement
     70 #include "mozilla/TextInputListener.h"   // for TextInputListener
     71 #include "mozilla/TextServicesDocument.h"  // for TextServicesDocument
     72 #include "mozilla/TextEvents.h"
     73 #include "mozilla/ToString.h"
     74 #include "mozilla/TransactionManager.h"    // for TransactionManager
     75 #include "mozilla/dom/AbstractRange.h"     // for AbstractRange
     76 #include "mozilla/dom/Attr.h"              // for Attr
     77 #include "mozilla/dom/BorrowedAttrInfo.h"  // for BorrowedAttrInfo
     78 #include "mozilla/dom/BrowsingContext.h"   // for BrowsingContext
     79 #include "mozilla/dom/CharacterData.h"     // for CharacterData
     80 #include "mozilla/dom/ContentParent.h"     // for ContentParent
     81 #include "mozilla/dom/DataTransfer.h"      // for DataTransfer
     82 #include "mozilla/dom/Document.h"          // for Document
     83 #include "mozilla/dom/DocumentInlines.h"   // for GetObservingPresShell
     84 #include "mozilla/dom/DragEvent.h"         // for DragEvent
     85 #include "mozilla/dom/Element.h"           // for Element, nsINode::AsElement
     86 #include "mozilla/dom/EventTarget.h"       // for EventTarget
     87 #include "mozilla/dom/HTMLBodyElement.h"
     88 #include "mozilla/dom/HTMLBRElement.h"
     89 #include "mozilla/dom/Selection.h"    // for Selection, etc.
     90 #include "mozilla/dom/StaticRange.h"  // for StaticRange
     91 #include "mozilla/dom/Text.h"
     92 #include "mozilla/dom/Event.h"
     93 #include "nsAString.h"                // for nsAString::Length, etc.
     94 #include "nsCCUncollectableMarker.h"  // for nsCCUncollectableMarker
     95 #include "nsCaret.h"                  // for nsCaret
     96 #include "nsCaseTreatment.h"
     97 #include "nsCharTraits.h"              // for NS_IS_HIGH_SURROGATE, etc.
     98 #include "nsContentUtils.h"            // for nsContentUtils
     99 #include "nsCopySupport.h"             // for nsCopySupport
    100 #include "nsDOMString.h"               // for DOMStringIsNull
    101 #include "nsDebug.h"                   // for NS_WARNING, etc.
    102 #include "nsError.h"                   // for NS_OK, etc.
    103 #include "nsFocusManager.h"            // for nsFocusManager
    104 #include "nsFrameSelection.h"          // for nsFrameSelection
    105 #include "nsGenericHTMLElement.h"      // for nsGenericHTMLElement
    106 #include "nsGkAtoms.h"                 // for nsGkAtoms, nsGkAtoms::dir
    107 #include "nsIClipboard.h"              // for nsIClipboard
    108 #include "nsIContent.h"                // for nsIContent
    109 #include "nsIContentInlines.h"         // for nsINode::IsInDesignMode()
    110 #include "nsIDocumentEncoder.h"        // for nsIDocumentEncoder
    111 #include "nsIDocumentStateListener.h"  // for nsIDocumentStateListener
    112 #include "nsIDocShell.h"               // for nsIDocShell
    113 #include "nsIEditActionListener.h"     // for nsIEditActionListener
    114 #include "nsIFrame.h"                  // for nsIFrame
    115 #include "nsIInlineSpellChecker.h"     // for nsIInlineSpellChecker, etc.
    116 #include "nsNameSpaceManager.h"        // for kNameSpaceID_None, etc.
    117 #include "nsINode.h"                   // for nsINode, etc.
    118 #include "nsISelectionController.h"    // for nsISelectionController, etc.
    119 #include "nsISelectionDisplay.h"       // for nsISelectionDisplay, etc.
    120 #include "nsISupports.h"               // for nsISupports
    121 #include "nsISupportsUtils.h"          // for NS_ADDREF, NS_IF_ADDREF
    122 #include "nsITransferable.h"           // for nsITransferable
    123 #include "nsIWeakReference.h"          // for nsISupportsWeakReference
    124 #include "nsIWidget.h"                 // for nsIWidget, IMEState, etc.
    125 #include "nsPIDOMWindow.h"             // for nsPIDOMWindow
    126 #include "nsPresContext.h"             // for nsPresContext
    127 #include "nsRange.h"                   // for nsRange
    128 #include "nsReadableUtils.h"           // for EmptyString, ToNewCString
    129 #include "nsString.h"                  // for nsAutoString, nsString, etc.
    130 #include "nsStringFwd.h"               // for nsString
    131 #include "nsStyleConsts.h"             // for StyleDirection::Rtl, etc.
    132 #include "nsStyleStruct.h"             // for nsStyleDisplay, nsStyleText, etc.
    133 #include "nsStyleStructFwd.h"          // for nsIFrame::StyleUIReset, etc.
    134 #include "nsTextNode.h"                // for nsTextNode
    135 #include "nsThreadUtils.h"             // for nsRunnable
    136 #include "prtime.h"                    // for PR_Now
    137 
    138 class nsIOutputStream;
    139 class nsITransferable;
    140 
    141 namespace mozilla {
    142 
    143 using namespace dom;
    144 using namespace widget;
    145 
    146 using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
    147 using LeafNodeType = HTMLEditUtils::LeafNodeType;
    148 using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
    149 using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
    150 
    151 static LazyLogModule gEventLog("EditorEvent");
    152 static LazyLogModule gHTMLEditorEditActionStartLog("HTMLEditorEditActionStart");
    153 
    154 LazyLogModule gTextInputLog("EditorTextInput");
    155 
    156 /*****************************************************************************
    157 * mozilla::EditorBase
    158 *****************************************************************************/
    159 template EditorDOMPoint EditorBase::GetFirstIMESelectionStartPoint() const;
    160 template EditorRawDOMPoint EditorBase::GetFirstIMESelectionStartPoint() const;
    161 template EditorDOMPoint EditorBase::GetLastIMESelectionEndPoint() const;
    162 template EditorRawDOMPoint EditorBase::GetLastIMESelectionEndPoint() const;
    163 
    164 template Result<CreateContentResult, nsresult>
    165 EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert,
    166                                      const EditorDOMPoint& aPointToInsert);
    167 template Result<CreateElementResult, nsresult>
    168 EditorBase::InsertNodeWithTransaction(Element& aContentToInsert,
    169                                      const EditorDOMPoint& aPointToInsert);
    170 template Result<CreateTextResult, nsresult>
    171 EditorBase::InsertNodeWithTransaction(Text& aContentToInsert,
    172                                      const EditorDOMPoint& aPointToInsert);
    173 
    174 template EditorDOMPoint EditorBase::GetFirstSelectionStartPoint() const;
    175 template EditorRawDOMPoint EditorBase::GetFirstSelectionStartPoint() const;
    176 template EditorDOMPoint EditorBase::GetFirstSelectionEndPoint() const;
    177 template EditorRawDOMPoint EditorBase::GetFirstSelectionEndPoint() const;
    178 
    179 template EditorBase::AutoCaretBidiLevelManager::AutoCaretBidiLevelManager(
    180    const EditorBase& aEditorBase, nsIEditor::EDirection aDirectionAndAmount,
    181    const EditorDOMPoint& aPointAtCaret);
    182 template EditorBase::AutoCaretBidiLevelManager::AutoCaretBidiLevelManager(
    183    const EditorBase& aEditorBase, nsIEditor::EDirection aDirectionAndAmount,
    184    const EditorRawDOMPoint& aPointAtCaret);
    185 
    186 EditorBase::EditorBase(EditorType aEditorType)
    187    : mEditActionData(nullptr),
    188      mPlaceholderName(nullptr),
    189      mModCount(0),
    190      mFlags(0),
    191      mUpdateCount(0),
    192      mPlaceholderBatch(0),
    193      mNewlineHandling(StaticPrefs::editor_singleLine_pasteNewlines()),
    194      mCaretStyle(StaticPrefs::layout_selection_caret_style()),
    195      mDocDirtyState(-1),
    196      mSpellcheckCheckboxState(eTriUnset),
    197      mInitSucceeded(false),
    198      mAllowsTransactionsToChangeSelection(true),
    199      mDidPreDestroy(false),
    200      mDidPostCreate(false),
    201      mDispatchInputEvent(true),
    202      mIsInEditSubAction(false),
    203      mHidingCaret(false),
    204      mSpellCheckerDictionaryUpdated(true),
    205      mIsHTMLEditorClass(aEditorType == EditorType::HTML) {
    206 #ifdef XP_WIN
    207  if (!mCaretStyle && !IsTextEditor()) {
    208    // Wordpad-like caret behavior.
    209    mCaretStyle = 1;
    210  }
    211 #endif  // #ifdef XP_WIN
    212  if (mNewlineHandling < nsIEditor::eNewlinesPasteIntact ||
    213      mNewlineHandling > nsIEditor::eNewlinesStripSurroundingWhitespace) {
    214    mNewlineHandling = nsIEditor::eNewlinesPasteToFirst;
    215  }
    216 }
    217 
    218 EditorBase::~EditorBase() {
    219  MOZ_ASSERT(!IsInitialized() || mDidPreDestroy,
    220             "Why PreDestroy hasn't been called?");
    221 
    222  if (mComposition) {
    223    mComposition->OnEditorDestroyed();
    224    mComposition = nullptr;
    225  }
    226  // If this editor is still hiding the caret, we need to restore it.
    227  HideCaret(false);
    228  mTransactionManager = nullptr;
    229 }
    230 
    231 NS_IMPL_CYCLE_COLLECTION_CLASS(EditorBase)
    232 
    233 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase)
    234  // Remove event listeners first since EditorEventListener may need
    235  // mDocument, mEventTarget, etc.
    236  if (tmp->mEventListener) {
    237    tmp->mEventListener->Disconnect();
    238    tmp->mEventListener = nullptr;
    239  }
    240 
    241  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
    242  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionController)
    243  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
    244  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMEContentObserver)
    245  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
    246  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextServicesDocument)
    247  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextInputListener)
    248  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransactionManager)
    249  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
    250  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
    251  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
    252  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderTransaction)
    253  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedDocumentEncoder)
    254  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
    255 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    256 
    257 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase)
    258  Document* currentDoc =
    259      tmp->mRootElement ? tmp->mRootElement->GetUncomposedDoc() : nullptr;
    260  if (currentDoc && nsCCUncollectableMarker::InGeneration(
    261                        cb, currentDoc->GetMarkedCCGeneration())) {
    262    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
    263  }
    264  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
    265  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionController)
    266  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
    267  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
    268  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
    269  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextServicesDocument)
    270  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextInputListener)
    271  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransactionManager)
    272  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
    273  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
    274  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
    275  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
    276  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderTransaction)
    277  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedDocumentEncoder)
    278 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    279 
    280 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditorBase)
    281  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
    282  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    283  NS_INTERFACE_MAP_ENTRY(nsIEditor)
    284  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
    285 NS_INTERFACE_MAP_END
    286 
    287 NS_IMPL_CYCLE_COLLECTING_ADDREF(EditorBase)
    288 NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase)
    289 
    290 nsresult EditorBase::InitInternal(Document& aDocument, Element* aRootElement,
    291                                  nsISelectionController& aSelectionController,
    292                                  uint32_t aFlags) {
    293  MOZ_ASSERT_IF(
    294      !mEditActionData ||
    295          !mEditActionData->HasEditorDestroyedDuringHandlingEditAction(),
    296      GetTopLevelEditSubAction() == EditSubAction::eNone);
    297 
    298  // First only set flags, but other stuff shouldn't be initialized now.
    299  // Note that SetFlags() will be called by PostCreate().
    300  mFlags = aFlags;
    301 
    302  mDocument = &aDocument;
    303  // nsISelectionController should be stored only when we're a `TextEditor`.
    304  // Otherwise, in `HTMLEditor`, it's `PresShell`, and grabbing it causes
    305  // a circular reference and memory leak.
    306  // XXX Should we move `mSelectionController to `TextEditor`?
    307  MOZ_ASSERT_IF(!IsTextEditor(), &aSelectionController == GetPresShell());
    308  if (IsTextEditor()) {
    309    MOZ_ASSERT(&aSelectionController != GetPresShell());
    310    mSelectionController = &aSelectionController;
    311  }
    312 
    313  if (mEditActionData) {
    314    // During edit action, selection is cached. But this selection is invalid
    315    // now since selection controller is updated, so we have to update this
    316    // cache.
    317    Selection* selection = aSelectionController.GetSelection(
    318        nsISelectionController::SELECTION_NORMAL);
    319    NS_WARNING_ASSERTION(selection,
    320                         "SelectionController::GetSelection() failed");
    321    if (selection) {
    322      mEditActionData->UpdateSelectionCache(*selection);
    323    }
    324  }
    325 
    326  // set up root element if we are passed one.
    327  if (aRootElement) {
    328    mRootElement = aRootElement;
    329  }
    330 
    331  // If this is an editor for <input> or <textarea>, the text node which
    332  // has composition string is always recreated with same content. Therefore,
    333  // we need to nodify mComposition of text node destruction and replacing
    334  // composing string when this receives eCompositionChange event next time.
    335  if (mComposition && mComposition->GetContainerTextNode() &&
    336      !mComposition->GetContainerTextNode()->IsInComposedDoc()) {
    337    mComposition->OnTextNodeRemoved();
    338  }
    339 
    340  // Show the caret.
    341  DebugOnly<nsresult> rvIgnored = aSelectionController.SetCaretReadOnly(false);
    342  NS_WARNING_ASSERTION(
    343      NS_SUCCEEDED(rvIgnored),
    344      "nsISelectionController::SetCaretReadOnly(false) failed, but ignored");
    345  // Show all the selection reflected to user.
    346  rvIgnored =
    347      aSelectionController.SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
    348  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    349                       "nsISelectionController::SetSelectionFlags("
    350                       "nsISelectionDisplay::DISPLAY_ALL) failed, but ignored");
    351 
    352  // Make sure that the editor will be destroyed properly
    353  mDidPreDestroy = false;
    354  // Make sure that the editor will be created properly
    355  mDidPostCreate = false;
    356 
    357  MOZ_ASSERT(IsBeingInitialized());
    358 
    359  AutoEditActionDataSetter editActionData(*this, EditAction::eInitializing);
    360  if (NS_WARN_IF(!editActionData.CanHandle())) {
    361    return NS_ERROR_FAILURE;
    362  }
    363 
    364  SelectionRef().AddSelectionListener(this);
    365 
    366  return NS_OK;
    367 }
    368 
    369 bool EditorBase::MaybeNodeRemovalsObservedByDevTools() const {
    370  if (IsTextEditor()) {
    371    // DOM mutation event listeners cannot catch the changes of
    372    // <input type="text"> nor <textarea>.
    373    return false;
    374  }
    375 #ifdef DEBUG
    376  // On debug build, this should always return true for testing complicated
    377  // path without mutation event listeners because when mutation event
    378  // listeners do not touch the DOM, editor needs to run as there is no
    379  // mutation event listeners.
    380  return true;
    381 #else   // #ifdef DEBUG
    382  Document* const doc = GetDocument();
    383  return doc && doc->MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc();
    384 #endif  // #ifdef DEBUG #else
    385 }
    386 
    387 nsresult EditorBase::EnsureEmptyTextFirstChild() {
    388  MOZ_ASSERT(IsTextEditor());
    389  RefPtr<Element> root = GetRoot();
    390  nsIContent* firstChild = root->GetFirstChild();
    391 
    392  if (!firstChild || !firstChild->IsText()) {
    393    RefPtr<nsTextNode> newTextNode = CreateTextNode(u""_ns);
    394    if (!newTextNode) {
    395      NS_WARNING("EditorBase::CreateTextNode() failed");
    396      return NS_ERROR_UNEXPECTED;
    397    }
    398    IgnoredErrorResult ignoredError;
    399    root->InsertChildBefore(newTextNode, root->GetFirstChild(), true,
    400                            ignoredError);
    401    MOZ_ASSERT(!ignoredError.Failed());
    402  }
    403 
    404  return NS_OK;
    405 }
    406 
    407 nsresult EditorBase::PostCreateInternal() {
    408  MOZ_ASSERT(IsEditActionDataAvailable());
    409 
    410  // Synchronize some stuff for the flags.  SetFlags() will initialize
    411  // something by the flag difference.  This is first time of that, so, all
    412  // initializations must be run.  For such reason, we need to invert mFlags
    413  // value first.
    414  mFlags = ~mFlags;
    415  nsresult rv = SetFlags(~mFlags);
    416  if (NS_FAILED(rv)) {
    417    NS_WARNING("EditorBase::SetFlags() failed");
    418    return EditorBase::ToGenericNSResult(rv);
    419  }
    420 
    421  // These operations only need to happen on the first PostCreate call
    422  if (!mDidPostCreate) {
    423    mDidPostCreate = true;
    424 
    425    // Set up listeners
    426    CreateEventListeners();
    427    nsresult rv = InstallEventListeners();
    428    if (NS_FAILED(rv)) {
    429      NS_WARNING("EditorBase::InstallEventListeners() failed");
    430      return EditorBase::ToGenericNSResult(rv);
    431    }
    432 
    433    // nuke the modification count, so the doc appears unmodified
    434    // do this before we notify listeners
    435    DebugOnly<nsresult> rvIgnored = ResetModificationCount();
    436    NS_WARNING_ASSERTION(
    437        NS_SUCCEEDED(rvIgnored),
    438        "EditorBase::ResetModificationCount() failed, but ignored");
    439 
    440    // update the UI with our state
    441    rvIgnored = NotifyDocumentListeners(eDocumentCreated);
    442    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    443                         "EditorBase::NotifyDocumentListeners(eDocumentCreated)"
    444                         " failed, but ignored");
    445    rvIgnored = NotifyDocumentListeners(eDocumentStateChanged);
    446    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    447                         "EditorBase::NotifyDocumentListeners("
    448                         "eDocumentStateChanged) failed, but ignored");
    449  }
    450 
    451  // update nsTextStateManager and caret if we have focus
    452  if (RefPtr<Element> focusedElement = GetFocusedElement()) {
    453    DebugOnly<nsresult> rvIgnored = InitializeSelection(*focusedElement);
    454    NS_WARNING_ASSERTION(
    455        NS_SUCCEEDED(rvIgnored),
    456        "EditorBase::InitializeSelection() failed, but ignored");
    457 
    458    // If the text control gets reframed during focus, Focus() would not be
    459    // called, so take a chance here to see if we need to spell check the text
    460    // control.
    461    nsresult rv = FlushPendingSpellCheck();
    462    if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
    463      NS_WARNING(
    464          "EditorBase::FlushPendingSpellCheck() caused destroying the editor");
    465      return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
    466    }
    467    NS_WARNING_ASSERTION(
    468        NS_SUCCEEDED(rv),
    469        "EditorBase::FlushPendingSpellCheck() failed, but ignored");
    470 
    471    Result<IMEState, nsresult> newStateOrError = GetPreferredIMEState();
    472    if (MOZ_UNLIKELY(newStateOrError.isErr())) {
    473      NS_WARNING("EditorBase::GetPreferredIMEState() failed");
    474      return NS_OK;
    475    }
    476    IMEStateManager::UpdateIMEState(newStateOrError.unwrap(), focusedElement,
    477                                    *this);
    478  }
    479 
    480  // FYI: This call might cause destroying this editor.
    481  IMEStateManager::OnEditorInitialized(*this);
    482 
    483  return NS_OK;
    484 }
    485 
    486 void EditorBase::SetTextInputListener(TextInputListener* aTextInputListener) {
    487  MOZ_ASSERT(!mTextInputListener || !aTextInputListener ||
    488             mTextInputListener == aTextInputListener);
    489  mTextInputListener = aTextInputListener;
    490 }
    491 
    492 void EditorBase::SetIMEContentObserver(
    493    IMEContentObserver* aIMEContentObserver) {
    494  MOZ_ASSERT(!mIMEContentObserver || !aIMEContentObserver ||
    495             mIMEContentObserver == aIMEContentObserver);
    496  mIMEContentObserver = aIMEContentObserver;
    497 }
    498 
    499 void EditorBase::CreateEventListeners() {
    500  // Don't create the handler twice
    501  if (!mEventListener) {
    502    mEventListener = new EditorEventListener();
    503  }
    504 }
    505 
    506 nsresult EditorBase::InstallEventListeners() {
    507  // FIXME InstallEventListeners() should not be called if we failed to set
    508  // document or create an event listener.  So, these checks should be
    509  // MOZ_DIAGNOSTIC_ASSERT instead.
    510  MOZ_ASSERT(GetDocument());
    511  if (MOZ_UNLIKELY(!GetDocument()) || NS_WARN_IF(!mEventListener)) {
    512    return NS_ERROR_NOT_INITIALIZED;
    513  }
    514 
    515  // Initialize the event target.
    516  mEventTarget = GetExposedRoot();
    517  if (NS_WARN_IF(!mEventTarget)) {
    518    return NS_ERROR_NOT_AVAILABLE;
    519  }
    520 
    521  nsresult rv = mEventListener->Connect(this);
    522  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    523                       "EditorEventListener::Connect() failed");
    524  if (mComposition) {
    525    // If mComposition has already been destroyed, we should forget it.
    526    // This may happen if it ended while we don't listen to composition
    527    // events.
    528    if (mComposition->Destroyed()) {
    529      // XXX We may need to fix existing composition transaction here.
    530      //     However, this may be called when it's not safe.
    531      //     Perhaps, we should stop handling composition with events.
    532      mComposition = nullptr;
    533    }
    534    // Otherwise, Restart to handle composition with new editor contents.
    535    else {
    536      mComposition->StartHandlingComposition(this);
    537    }
    538  }
    539  return rv;
    540 }
    541 
    542 void EditorBase::RemoveEventListeners() {
    543  if (!mEventListener) {
    544    return;
    545  }
    546  mEventListener->Disconnect();
    547  if (mComposition) {
    548    // Even if this is called, don't release mComposition because this is
    549    // may be reused after reframing.
    550    mComposition->EndHandlingComposition(this);
    551  }
    552  mEventTarget = nullptr;
    553 }
    554 
    555 bool EditorBase::IsListeningToEvents() const {
    556  return mEventListener && !mEventListener->DetachedFromEditor();
    557 }
    558 
    559 bool EditorBase::GetDesiredSpellCheckState() {
    560  // Check user override on this element
    561  if (mSpellcheckCheckboxState != eTriUnset) {
    562    return (mSpellcheckCheckboxState == eTriTrue);
    563  }
    564 
    565  // Check user preferences
    566  int32_t spellcheckLevel = StaticPrefs::layout_spellcheckDefault();
    567 
    568  if (!spellcheckLevel) {
    569    return false;  // Spellchecking forced off globally
    570  }
    571 
    572  if (!CanEnableSpellCheck()) {
    573    return false;
    574  }
    575 
    576  PresShell* presShell = GetPresShell();
    577  if (presShell) {
    578    nsPresContext* context = presShell->GetPresContext();
    579    if (context && !context->IsDynamic()) {
    580      return false;
    581    }
    582  }
    583 
    584  // Check DOM state
    585  nsCOMPtr<nsIContent> content = GetExposedRoot();
    586  if (!content) {
    587    return false;
    588  }
    589 
    590  auto element = nsGenericHTMLElement::FromNode(content);
    591  if (!element) {
    592    return false;
    593  }
    594 
    595  // XXX I'm not sure whether we don't use this path when we're a plaintext mail
    596  // composer.
    597  if (IsHTMLEditor() && !AsHTMLEditor()->IsPlaintextMailComposer()) {
    598    // Some of the page content might be editable and some not, if spellcheck=
    599    // is explicitly set anywhere, so if there's anything editable on the page,
    600    // return true and let the spellchecker figure it out.
    601    Document* doc = content->GetComposedDoc();
    602    return doc && doc->IsEditingOn();
    603  }
    604 
    605  return element->Spellcheck();
    606 }
    607 
    608 void EditorBase::PreDestroyInternal() {
    609  MOZ_ASSERT(!mDidPreDestroy);
    610 
    611  mInitSucceeded = false;
    612 
    613  Selection* selection = GetSelection();
    614  if (selection) {
    615    selection->RemoveSelectionListener(this);
    616  }
    617 
    618  IMEStateManager::OnEditorDestroying(*this);
    619 
    620  // Let spellchecker clean up its observers etc. It is important not to
    621  // actually free the spellchecker here, since the spellchecker could have
    622  // caused flush notifications, which could have gotten here if a textbox
    623  // is being removed. Setting the spellchecker to nullptr could free the
    624  // object that is still in use! It will be freed when the editor is
    625  // destroyed.
    626  if (mInlineSpellChecker) {
    627    DebugOnly<nsresult> rvIgnored =
    628        mInlineSpellChecker->Cleanup(IsTextEditor());
    629    NS_WARNING_ASSERTION(
    630        NS_SUCCEEDED(rvIgnored),
    631        "mozInlineSpellChecker::Cleanup() failed, but ignored");
    632  }
    633 
    634  // tell our listeners that the doc is going away
    635  DebugOnly<nsresult> rvIgnored =
    636      NotifyDocumentListeners(eDocumentToBeDestroyed);
    637  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    638                       "EditorBase::NotifyDocumentListeners("
    639                       "eDocumentToBeDestroyed) failed, but ignored");
    640 
    641  // Unregister event listeners
    642  RemoveEventListeners();
    643  // If this editor is still hiding the caret, we need to restore it.
    644  HideCaret(false);
    645  mActionListeners.Clear();
    646  mDocStateListeners.Clear();
    647  mInlineSpellChecker = nullptr;
    648  mTextServicesDocument = nullptr;
    649  mTextInputListener = nullptr;
    650  mSpellcheckCheckboxState = eTriUnset;
    651  mRootElement = nullptr;
    652 
    653  // Transaction may grab this instance.  Therefore, they should be released
    654  // here for stopping the circular reference with this instance.
    655  if (mTransactionManager) {
    656    DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
    657    NS_WARNING_ASSERTION(disabledUndoRedo,
    658                         "EditorBase::DisableUndoRedo() failed, but ignored");
    659    mTransactionManager = nullptr;
    660  }
    661 
    662  if (mEditActionData) {
    663    mEditActionData->OnEditorDestroy();
    664  }
    665 
    666  mDidPreDestroy = true;
    667 }
    668 
    669 NS_IMETHODIMP EditorBase::GetFlags(uint32_t* aFlags) {
    670  // NOTE: If you need to override this method, you need to make Flags()
    671  //       virtual.
    672  *aFlags = Flags();
    673  return NS_OK;
    674 }
    675 
    676 NS_IMETHODIMP EditorBase::SetFlags(uint32_t aFlags) {
    677  if (mFlags == aFlags) {
    678    return NS_OK;
    679  }
    680 
    681  // If we're a `TextEditor` instance, it's always a plaintext editor.
    682  // Therefore, `eEditorPlaintextMask` is not necessary and should not be set
    683  // for the performance reason.
    684  MOZ_ASSERT_IF(IsTextEditor(), !(aFlags & nsIEditor::eEditorPlaintextMask));
    685  // If we're an `HTMLEditor` instance, we cannot treat it as a single line
    686  // editor.  So, eEditorSingleLineMask is available only when we're a
    687  // `TextEditor` instance.
    688  MOZ_ASSERT_IF(IsHTMLEditor(), !(aFlags & nsIEditor::eEditorSingleLineMask));
    689  // If we're an `HTMLEditor` instance, we cannot treat it as a password editor.
    690  // So, eEditorPasswordMask is available only when we're a `TextEditor`
    691  // instance.
    692  MOZ_ASSERT_IF(IsHTMLEditor(), !(aFlags & nsIEditor::eEditorPasswordMask));
    693  // eEditorAllowInteraction changes the behavior of `HTMLEditor`.  So, it's
    694  // not available with `TextEditor` instance.
    695  MOZ_ASSERT_IF(IsTextEditor(), !(aFlags & nsIEditor::eEditorAllowInteraction));
    696 
    697  const bool isCalledByPostCreate = (mFlags == ~aFlags);
    698  // We don't support dynamic password flag change.
    699  MOZ_ASSERT_IF(!isCalledByPostCreate,
    700                !((mFlags ^ aFlags) & nsIEditor::eEditorPasswordMask));
    701  bool spellcheckerWasEnabled = !isCalledByPostCreate && CanEnableSpellCheck();
    702  mFlags = aFlags;
    703 
    704  if (!IsInitialized()) {
    705    // If we're initializing, we shouldn't do anything now.
    706    // SetFlags() will be called by PostCreate(),
    707    // we should synchronize some stuff for the flags at that time.
    708    return NS_OK;
    709  }
    710 
    711  // The flag change may cause the spellchecker state change
    712  if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
    713    SyncRealTimeSpell();
    714  }
    715 
    716  // If this is called from PostCreate(), it will update the IME state if it's
    717  // necessary.
    718  if (!mDidPostCreate) {
    719    return NS_OK;
    720  }
    721 
    722  // Might be changing editable state, so, we need to reset current IME state
    723  // if we're focused and the flag change causes IME state change.
    724  if (RefPtr<Element> focusedElement = GetFocusedElement()) {
    725    Result<IMEState, nsresult> newStateOrError = GetPreferredIMEState();
    726    NS_WARNING_ASSERTION(newStateOrError.isOk(),
    727                         "EditorBase::GetPreferredIMEState() failed");
    728    if (MOZ_LIKELY(newStateOrError.isOk())) {
    729      // NOTE: When the enabled state isn't going to be modified, this method
    730      // is going to do nothing.
    731      IMEStateManager::UpdateIMEState(newStateOrError.unwrap(), focusedElement,
    732                                      *this);
    733    }
    734  }
    735 
    736  return NS_OK;
    737 }
    738 
    739 NS_IMETHODIMP EditorBase::GetIsSelectionEditable(bool* aIsSelectionEditable) {
    740  if (NS_WARN_IF(!aIsSelectionEditable)) {
    741    return NS_ERROR_INVALID_ARG;
    742  }
    743  *aIsSelectionEditable = IsSelectionEditable();
    744  return NS_OK;
    745 }
    746 
    747 bool EditorBase::IsSelectionEditable() {
    748  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    749  if (NS_WARN_IF(!editActionData.CanHandle())) {
    750    return false;
    751  }
    752 
    753  if (IsTextEditor()) {
    754    // XXX we just check that the anchor node is editable at the moment
    755    //     we should check that all nodes in the selection are editable
    756    const nsINode* anchorNode = SelectionRef().GetAnchorNode();
    757    return anchorNode && anchorNode->IsContent() && anchorNode->IsEditable();
    758  }
    759 
    760  const nsINode* anchorNode = SelectionRef().GetAnchorNode();
    761  const nsINode* focusNode = SelectionRef().GetFocusNode();
    762  if (!anchorNode || !focusNode) {
    763    return false;
    764  }
    765 
    766  // if anchorNode or focusNode is in a native anonymous subtree, HTMLEditor
    767  // shouldn't edit content in it.
    768  // XXX This must be a bug of Selection API.
    769  if (MOZ_UNLIKELY(anchorNode->IsInNativeAnonymousSubtree() ||
    770                   focusNode->IsInNativeAnonymousSubtree())) {
    771    return false;
    772  }
    773 
    774  // Per the editing spec as of June 2012: we have to have a selection whose
    775  // start and end nodes are editable, and which share an ancestor editing
    776  // host.  (Bug 766387.)
    777  bool isSelectionEditable = SelectionRef().RangeCount() &&
    778                             anchorNode->IsEditable() &&
    779                             focusNode->IsEditable();
    780  if (!isSelectionEditable) {
    781    return false;
    782  }
    783 
    784  const nsINode* commonAncestor =
    785      SelectionRef().GetAnchorFocusRange()->GetClosestCommonInclusiveAncestor();
    786  while (commonAncestor && !commonAncestor->IsEditable()) {
    787    commonAncestor = commonAncestor->GetParentNode();
    788  }
    789  // If there is no editable common ancestor, return false.
    790  return !!commonAncestor;
    791 }
    792 
    793 NS_IMETHODIMP EditorBase::GetIsDocumentEditable(bool* aIsDocumentEditable) {
    794  if (NS_WARN_IF(!aIsDocumentEditable)) {
    795    return NS_ERROR_INVALID_ARG;
    796  }
    797  RefPtr<Document> document = GetDocument();
    798  *aIsDocumentEditable = document && IsModifiable();
    799  return NS_OK;
    800 }
    801 
    802 NS_IMETHODIMP EditorBase::GetDocument(Document** aDocument) {
    803  if (NS_WARN_IF(!aDocument)) {
    804    return NS_ERROR_INVALID_ARG;
    805  }
    806  *aDocument = do_AddRef(mDocument).take();
    807  return NS_WARN_IF(!*aDocument) ? NS_ERROR_NOT_INITIALIZED : NS_OK;
    808 }
    809 
    810 already_AddRefed<nsIWidget> EditorBase::GetWidget() const {
    811  nsPresContext* presContext = GetPresContext();
    812  if (NS_WARN_IF(!presContext)) {
    813    return nullptr;
    814  }
    815  nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
    816  return NS_WARN_IF(!widget) ? nullptr : widget.forget();
    817 }
    818 
    819 NS_IMETHODIMP EditorBase::GetContentsMIMEType(nsAString& aContentsMIMEType) {
    820  aContentsMIMEType = mContentMIMEType;
    821  return NS_OK;
    822 }
    823 
    824 NS_IMETHODIMP EditorBase::SetContentsMIMEType(
    825    const nsAString& aContentsMIMEType) {
    826  mContentMIMEType.Assign(aContentsMIMEType);
    827  return NS_OK;
    828 }
    829 
    830 NS_IMETHODIMP EditorBase::GetSelectionController(
    831    nsISelectionController** aSelectionController) {
    832  if (NS_WARN_IF(!aSelectionController)) {
    833    return NS_ERROR_INVALID_ARG;
    834  }
    835  *aSelectionController = do_AddRef(GetSelectionController()).take();
    836  return NS_WARN_IF(!*aSelectionController) ? NS_ERROR_FAILURE : NS_OK;
    837 }
    838 
    839 NS_IMETHODIMP EditorBase::DeleteSelection(EDirection aAction,
    840                                          EStripWrappers aStripWrappers) {
    841  nsresult rv = DeleteSelectionAsAction(aAction, aStripWrappers);
    842  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    843                       "EditorBase::DeleteSelectionAsAction() failed");
    844  return rv;
    845 }
    846 
    847 NS_IMETHODIMP EditorBase::GetSelection(Selection** aSelection) {
    848  nsresult rv = GetSelection(SelectionType::eNormal, aSelection);
    849  NS_WARNING_ASSERTION(
    850      NS_SUCCEEDED(rv),
    851      "EditorBase::GetSelection(SelectionType::eNormal) failed");
    852  return rv;
    853 }
    854 
    855 nsresult EditorBase::GetSelection(SelectionType aSelectionType,
    856                                  Selection** aSelection) const {
    857  if (NS_WARN_IF(!aSelection)) {
    858    return NS_ERROR_INVALID_ARG;
    859  }
    860  if (IsEditActionDataAvailable()) {
    861    *aSelection = do_AddRef(&SelectionRef()).take();
    862    return NS_OK;
    863  }
    864  nsISelectionController* selectionController = GetSelectionController();
    865  if (NS_WARN_IF(!selectionController)) {
    866    *aSelection = nullptr;
    867    return NS_ERROR_NOT_INITIALIZED;
    868  }
    869  *aSelection = do_AddRef(selectionController->GetSelection(
    870                              ToRawSelectionType(aSelectionType)))
    871                    .take();
    872  return NS_WARN_IF(!*aSelection) ? NS_ERROR_FAILURE : NS_OK;
    873 }
    874 
    875 nsresult EditorBase::DoTransactionInternal(nsITransaction* aTransaction) {
    876  MOZ_ASSERT(IsEditActionDataAvailable());
    877  MOZ_ASSERT_IF(
    878      // If the DOM is modified by a clipboard event handler,
    879      // HTMLEditor::OnModifyDocument() may need to do some transactions before
    880      // dispatching `beforeinput`.
    881      // FIXME: It shouldn't happen, and I think that it should be done once
    882      // before dispatching `input` event to hide the our editor hack from
    883      // the event listeners.
    884      GetEditAction() != EditAction::ePaste &&
    885          GetEditAction() != EditAction::eCut,
    886      !ShouldAlreadyHaveHandledBeforeInputEventDispatching());
    887 
    888  if (mPlaceholderBatch && !mPlaceholderTransaction) {
    889    MOZ_DIAGNOSTIC_ASSERT(mPlaceholderName);
    890    mPlaceholderTransaction = PlaceholderTransaction::Create(
    891        *this, *mPlaceholderName, std::move(mSelState));
    892    MOZ_ASSERT(mSelState.isNothing());
    893 
    894    // We will recurse, but will not hit this case in the nested call
    895    RefPtr<PlaceholderTransaction> placeholderTransaction =
    896        mPlaceholderTransaction;
    897    DebugOnly<nsresult> rvIgnored =
    898        DoTransactionInternal(placeholderTransaction);
    899    NS_WARNING_ASSERTION(
    900        NS_SUCCEEDED(rvIgnored),
    901        "EditorBase::DoTransactionInternal() failed, but ignored");
    902 
    903    if (mTransactionManager) {
    904      if (nsCOMPtr<nsITransaction> topTransaction =
    905              mTransactionManager->PeekUndoStack()) {
    906        if (RefPtr<EditTransactionBase> topTransactionBase =
    907                topTransaction->GetAsEditTransactionBase()) {
    908          if (PlaceholderTransaction* topPlaceholderTransaction =
    909                  topTransactionBase->GetAsPlaceholderTransaction()) {
    910            // there is a placeholder transaction on top of the undo stack.  It
    911            // is either the one we just created, or an earlier one that we are
    912            // now merging into.  From here on out remember this placeholder
    913            // instead of the one we just created.
    914            mPlaceholderTransaction = topPlaceholderTransaction;
    915          }
    916        }
    917      }
    918    }
    919  }
    920 
    921  if (aTransaction) {
    922    // XXX: Why are we doing selection specific batching stuff here?
    923    // XXX: Most entry points into the editor have auto variables that
    924    // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
    925    // XXX: these selection batch calls no-ops.
    926    // XXX:
    927    // XXX: I suspect that this was placed here to avoid multiple
    928    // XXX: selection changed notifications from happening until after
    929    // XXX: the transaction was done. I suppose that can still happen
    930    // XXX: if an embedding application called DoTransaction() directly
    931    // XXX: to pump its own transactions through the system, but in that
    932    // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
    933    // XXX: its auto equivalent AutoUpdateViewBatch to ensure that
    934    // XXX: selection listeners have access to accurate frame data?
    935    // XXX:
    936    // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
    937    // XXX: we will need to make sure that they are disabled during
    938    // XXX: the init of the editor for text widgets to avoid layout
    939    // XXX: re-entry during initial reflow. - kin
    940 
    941    // get the selection and start a batch change
    942    SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__);
    943 
    944    if (mTransactionManager) {
    945      RefPtr<TransactionManager> transactionManager(mTransactionManager);
    946      nsresult rv = transactionManager->DoTransaction(aTransaction);
    947      if (NS_FAILED(rv)) {
    948        NS_WARNING("TransactionManager::DoTransaction() failed");
    949        return rv;
    950      }
    951    } else {
    952      nsresult rv = aTransaction->DoTransaction();
    953      if (NS_FAILED(rv)) {
    954        NS_WARNING("nsITransaction::DoTransaction() failed");
    955        return rv;
    956      }
    957    }
    958 
    959    DoAfterDoTransaction(aTransaction);
    960  }
    961 
    962  return NS_OK;
    963 }
    964 
    965 NS_IMETHODIMP EditorBase::EnableUndo(bool aEnable) {
    966  // XXX Should we return NS_ERROR_FAILURE if EdnableUndoRedo() or
    967  //     DisableUndoRedo() returns false?
    968  if (aEnable) {
    969    DebugOnly<bool> enabledUndoRedo = EnableUndoRedo();
    970    NS_WARNING_ASSERTION(enabledUndoRedo,
    971                         "EditorBase::EnableUndoRedo() failed, but ignored");
    972    return NS_OK;
    973  }
    974  DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
    975  NS_WARNING_ASSERTION(disabledUndoRedo,
    976                       "EditorBase::DisableUndoRedo() failed, but ignored");
    977  return NS_OK;
    978 }
    979 
    980 NS_IMETHODIMP EditorBase::ClearUndoRedoXPCOM() {
    981  if (MOZ_UNLIKELY(!ClearUndoRedo())) {
    982    return NS_ERROR_FAILURE;  // We're handling a transaction
    983  }
    984  return NS_OK;
    985 }
    986 
    987 NS_IMETHODIMP EditorBase::Undo() {
    988  nsresult rv = UndoAsAction(1u);
    989  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed");
    990  return rv;
    991 }
    992 
    993 NS_IMETHODIMP EditorBase::UndoAll() {
    994  if (!mTransactionManager) {
    995    return NS_OK;
    996  }
    997  size_t numberOfUndoItems = mTransactionManager->NumberOfUndoItems();
    998  if (!numberOfUndoItems) {
    999    return NS_OK;  // no transactions
   1000  }
   1001  nsresult rv = UndoAsAction(numberOfUndoItems);
   1002  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed");
   1003  return rv;
   1004 }
   1005 
   1006 NS_IMETHODIMP EditorBase::GetUndoRedoEnabled(bool* aIsEnabled) {
   1007  MOZ_ASSERT(aIsEnabled);
   1008  *aIsEnabled = IsUndoRedoEnabled();
   1009  return NS_OK;
   1010 }
   1011 
   1012 NS_IMETHODIMP EditorBase::GetCanUndo(bool* aCanUndo) {
   1013  MOZ_ASSERT(aCanUndo);
   1014  *aCanUndo = CanUndo();
   1015  return NS_OK;
   1016 }
   1017 
   1018 NS_IMETHODIMP EditorBase::Redo() {
   1019  nsresult rv = RedoAsAction(1u);
   1020  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::RedoAsAction() failed");
   1021  return rv;
   1022 }
   1023 
   1024 NS_IMETHODIMP EditorBase::GetCanRedo(bool* aCanRedo) {
   1025  MOZ_ASSERT(aCanRedo);
   1026  *aCanRedo = CanRedo();
   1027  return NS_OK;
   1028 }
   1029 
   1030 nsresult EditorBase::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
   1031  if (aCount == 0 || IsReadonly()) {
   1032    return NS_OK;
   1033  }
   1034 
   1035  // If we don't have transaction in the undo stack, we shouldn't notify
   1036  // anybody of trying to undo since it's not useful notification but we
   1037  // need to pay some runtime cost.
   1038  if (!CanUndo()) {
   1039    return NS_OK;
   1040  }
   1041 
   1042  // If there is composition, we shouldn't allow to undo with committing
   1043  // composition since Chrome doesn't allow it and it doesn't make sense
   1044  // because committing composition causes one transaction and Undo(1)
   1045  // undoes the committing composition.
   1046  if (GetComposition()) {
   1047    return NS_OK;
   1048  }
   1049 
   1050  AutoEditActionDataSetter editActionData(*this, EditAction::eUndo, aPrincipal);
   1051  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1052  if (NS_FAILED(rv)) {
   1053    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1054                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   1055    return EditorBase::ToGenericNSResult(rv);
   1056  }
   1057 
   1058  AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__);
   1059 
   1060  NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   1061  if (NS_WARN_IF(!CanUndo()) || NS_WARN_IF(Destroyed())) {
   1062    return NS_ERROR_FAILURE;
   1063  }
   1064 
   1065  rv = NS_OK;
   1066  {
   1067    IgnoredErrorResult ignoredError;
   1068    AutoEditSubActionNotifier startToHandleEditSubAction(
   1069        *this, EditSubAction::eUndo, nsIEditor::eNone, ignoredError);
   1070    if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   1071      return EditorBase::ToGenericNSResult(ignoredError.StealNSResult());
   1072    }
   1073    NS_WARNING_ASSERTION(!ignoredError.Failed(),
   1074                         "TextEditor::OnStartToHandleTopLevelEditSubAction() "
   1075                         "failed, but ignored");
   1076 
   1077    RefPtr<TransactionManager> transactionManager(mTransactionManager);
   1078    for (uint32_t i = 0; i < aCount; ++i) {
   1079      if (NS_FAILED(transactionManager->Undo())) {
   1080        NS_WARNING("TransactionManager::Undo() failed");
   1081        break;
   1082      }
   1083      DoAfterUndoTransaction();
   1084    }
   1085 
   1086    if (IsHTMLEditor()) {
   1087      rv = AsHTMLEditor()->ReflectPaddingBRElementForEmptyEditor();
   1088    }
   1089  }
   1090 
   1091  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   1092  return EditorBase::ToGenericNSResult(rv);
   1093 }
   1094 
   1095 nsresult EditorBase::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
   1096  if (aCount == 0 || IsReadonly()) {
   1097    return NS_OK;
   1098  }
   1099 
   1100  // If we don't have transaction in the redo stack, we shouldn't notify
   1101  // anybody of trying to redo since it's not useful notification but we
   1102  // need to pay some runtime cost.
   1103  if (!CanRedo()) {
   1104    return NS_OK;
   1105  }
   1106 
   1107  // If there is composition, we shouldn't allow to redo with committing
   1108  // composition since Chrome doesn't allow it and it doesn't make sense
   1109  // because committing composition causes removing all transactions from
   1110  // the redo queue.  So, it becomes impossible to redo anything.
   1111  if (GetComposition()) {
   1112    return NS_OK;
   1113  }
   1114 
   1115  AutoEditActionDataSetter editActionData(*this, EditAction::eRedo, aPrincipal);
   1116  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1117  if (NS_FAILED(rv)) {
   1118    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1119                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   1120    return EditorBase::ToGenericNSResult(rv);
   1121  }
   1122 
   1123  AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__);
   1124 
   1125  NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   1126  if (NS_WARN_IF(!CanRedo()) || NS_WARN_IF(Destroyed())) {
   1127    return NS_ERROR_FAILURE;
   1128  }
   1129 
   1130  rv = NS_OK;
   1131  {
   1132    IgnoredErrorResult ignoredError;
   1133    AutoEditSubActionNotifier startToHandleEditSubAction(
   1134        *this, EditSubAction::eRedo, nsIEditor::eNone, ignoredError);
   1135    if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   1136      return ignoredError.StealNSResult();
   1137    }
   1138    NS_WARNING_ASSERTION(!ignoredError.Failed(),
   1139                         "TextEditor::OnStartToHandleTopLevelEditSubAction() "
   1140                         "failed, but ignored");
   1141 
   1142    RefPtr<TransactionManager> transactionManager(mTransactionManager);
   1143    for (uint32_t i = 0; i < aCount; ++i) {
   1144      if (NS_FAILED(transactionManager->Redo())) {
   1145        NS_WARNING("TransactionManager::Redo() failed");
   1146        break;
   1147      }
   1148      DoAfterRedoTransaction();
   1149    }
   1150 
   1151    if (IsHTMLEditor()) {
   1152      rv = AsHTMLEditor()->ReflectPaddingBRElementForEmptyEditor();
   1153    }
   1154  }
   1155 
   1156  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   1157  return EditorBase::ToGenericNSResult(rv);
   1158 }
   1159 
   1160 NS_IMETHODIMP EditorBase::BeginTransaction() {
   1161  AutoEditActionDataSetter editActionData(*this, EditAction::eUnknown);
   1162  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1163    return NS_ERROR_FAILURE;
   1164  }
   1165 
   1166  BeginTransactionInternal(__FUNCTION__);
   1167  return NS_OK;
   1168 }
   1169 
   1170 void EditorBase::BeginTransactionInternal(const char* aRequesterFuncName) {
   1171  BeginUpdateViewBatch(aRequesterFuncName);
   1172 
   1173  if (NS_WARN_IF(!mTransactionManager)) {
   1174    return;
   1175  }
   1176 
   1177  RefPtr<TransactionManager> transactionManager(mTransactionManager);
   1178  DebugOnly<nsresult> rvIgnored = transactionManager->BeginBatch(nullptr);
   1179  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1180                       "TransactionManager::BeginBatch() failed, but ignored");
   1181 }
   1182 
   1183 NS_IMETHODIMP EditorBase::EndTransaction() {
   1184  AutoEditActionDataSetter editActionData(*this, EditAction::eUnknown);
   1185  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1186    return NS_ERROR_FAILURE;
   1187  }
   1188 
   1189  EndTransactionInternal(__FUNCTION__);
   1190  return NS_OK;
   1191 }
   1192 
   1193 void EditorBase::EndTransactionInternal(const char* aRequesterFuncName) {
   1194  if (mTransactionManager) {
   1195    RefPtr<TransactionManager> transactionManager(mTransactionManager);
   1196    DebugOnly<nsresult> rvIgnored = transactionManager->EndBatch(false);
   1197    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1198                         "TransactionManager::EndBatch() failed, but ignored");
   1199  }
   1200 
   1201  EndUpdateViewBatch(aRequesterFuncName);
   1202 }
   1203 
   1204 void EditorBase::BeginPlaceholderTransaction(nsStaticAtom& aTransactionName,
   1205                                             const char* aRequesterFuncName) {
   1206  MOZ_ASSERT(IsEditActionDataAvailable());
   1207  MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!");
   1208 
   1209  if (!mPlaceholderBatch) {
   1210    NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   1211    // time to turn on the batch
   1212    BeginUpdateViewBatch(aRequesterFuncName);
   1213    mPlaceholderTransaction = nullptr;
   1214    mPlaceholderName = &aTransactionName;
   1215    mSelState.emplace();
   1216    mSelState->SaveSelection(SelectionRef());
   1217    // Composition transaction can modify multiple nodes and it merges text
   1218    // node for ime into single text node.
   1219    // So if current selection is into IME text node, it might be failed
   1220    // to restore selection by UndoTransaction.
   1221    // So we need update selection by range updater.
   1222    if (mPlaceholderName == nsGkAtoms::IMETxnName) {
   1223      RangeUpdaterRef().RegisterSelectionState(*mSelState);
   1224    }
   1225  }
   1226  mPlaceholderBatch++;
   1227 }
   1228 
   1229 void EditorBase::EndPlaceholderTransaction(
   1230    ScrollSelectionIntoView aScrollSelectionIntoView,
   1231    const char* aRequesterFuncName) {
   1232  MOZ_ASSERT(IsEditActionDataAvailable());
   1233  MOZ_ASSERT(mPlaceholderBatch > 0,
   1234             "zero or negative placeholder batch count when ending batch!");
   1235 
   1236  if (!(--mPlaceholderBatch)) {
   1237    // By making the assumption that no reflow happens during the calls
   1238    // to EndUpdateViewBatch and ScrollSelectionFocusIntoView, we are able to
   1239    // allow the selection to cache a frame offset which is used by the
   1240    // caret drawing code. We only enable this cache here; at other times,
   1241    // we have no way to know whether reflow invalidates it
   1242    // See bugs 35296 and 199412.
   1243    SelectionRef().SetCanCacheFrameOffset(true);
   1244 
   1245    // time to turn off the batch
   1246    EndUpdateViewBatch(aRequesterFuncName);
   1247    // make sure selection is in view
   1248 
   1249    // After ScrollSelectionFocusIntoView(), the pending notifications might be
   1250    // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
   1251    // XXX Even if we're destroyed, we need to keep handling below because
   1252    //     this method changes a lot of status.  We should rewrite this safer.
   1253    if (aScrollSelectionIntoView == ScrollSelectionIntoView::Yes) {
   1254      DebugOnly<nsresult> rvIgnored = ScrollSelectionFocusIntoView();
   1255      NS_WARNING_ASSERTION(
   1256          NS_SUCCEEDED(rvIgnored),
   1257          "EditorBase::ScrollSelectionFocusIntoView() failed, but Ignored");
   1258    }
   1259 
   1260    // cached for frame offset are Not available now
   1261    SelectionRef().SetCanCacheFrameOffset(false);
   1262 
   1263    if (mSelState) {
   1264      // we saved the selection state, but never got to hand it to placeholder
   1265      // (else we ould have nulled out this pointer), so destroy it to prevent
   1266      // leaks.
   1267      if (mPlaceholderName == nsGkAtoms::IMETxnName) {
   1268        RangeUpdaterRef().DropSelectionState(*mSelState);
   1269      }
   1270      mSelState.reset();
   1271    }
   1272    // We might have never made a placeholder if no action took place.
   1273    if (mPlaceholderTransaction) {
   1274      // FYI: Disconnect placeholder transaction before dispatching "input"
   1275      //      event because an input event listener may start other things.
   1276      // TODO: We should forget EditActionDataSetter too.
   1277      RefPtr<PlaceholderTransaction> placeholderTransaction =
   1278          std::move(mPlaceholderTransaction);
   1279      DebugOnly<nsresult> rvIgnored =
   1280          placeholderTransaction->EndPlaceHolderBatch();
   1281      NS_WARNING_ASSERTION(
   1282          NS_SUCCEEDED(rvIgnored),
   1283          "PlaceholderTransaction::EndPlaceHolderBatch() failed, but ignored");
   1284      // notify editor observers of action but if composing, it's done by
   1285      // compositionchange event handler.
   1286      if (!mComposition) {
   1287        NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   1288      }
   1289    } else {
   1290      NotifyEditorObservers(eNotifyEditorObserversOfCancel);
   1291    }
   1292  }
   1293 }
   1294 
   1295 NS_IMETHODIMP EditorBase::GetDocumentIsEmpty(bool* aDocumentIsEmpty) {
   1296  MOZ_ASSERT(aDocumentIsEmpty);
   1297  *aDocumentIsEmpty = IsEmpty();
   1298  return NS_OK;
   1299 }
   1300 
   1301 // XXX: The rule system should tell us which node to select all on (ie, the
   1302 //      root, or the body)
   1303 NS_IMETHODIMP EditorBase::SelectAll() {
   1304  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1305  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1306    return NS_ERROR_NOT_INITIALIZED;
   1307  }
   1308 
   1309  nsresult rv = SelectAllInternal();
   1310  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SelectAllInternal() failed");
   1311  // This is low level API for XUL applcation.  So, we should return raw
   1312  // error code here.
   1313  return rv;
   1314 }
   1315 
   1316 nsresult EditorBase::SelectAllInternal() {
   1317  MOZ_ASSERT(IsInitialized());
   1318 
   1319  DebugOnly<nsresult> rvIgnored = CommitComposition();
   1320  if (NS_WARN_IF(Destroyed())) {
   1321    return NS_ERROR_EDITOR_DESTROYED;
   1322  }
   1323  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1324                       "EditorBase::CommitComposition() failed, but ignored");
   1325 
   1326  // XXX Do we need to keep handling after committing composition causes moving
   1327  //     focus to different element?  Although TextEditor has independent
   1328  //     selection, so, we may not see any odd behavior even in such case.
   1329 
   1330  nsresult rv = SelectEntireDocument();
   1331  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1332                       "EditorBase::SelectEntireDocument() failed");
   1333  return rv;
   1334 }
   1335 
   1336 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP EditorBase::BeginningOfDocument() {
   1337  MOZ_ASSERT(IsTextEditor());
   1338 
   1339  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1340  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1341    return NS_ERROR_NOT_INITIALIZED;
   1342  }
   1343 
   1344  // get the root element
   1345  RefPtr<Element> rootElement = GetRoot();
   1346  if (NS_WARN_IF(!rootElement)) {
   1347    return NS_ERROR_NULL_POINTER;
   1348  }
   1349 
   1350  // find first editable thingy
   1351  nsCOMPtr<nsIContent> firstEditableLeaf;
   1352  // If we're `TextEditor`, the first editable leaf node is a text node or
   1353  // padding `<br>` element.  In the first case, we need to collapse selection
   1354  // into it.
   1355  if (rootElement->GetFirstChild() && rootElement->GetFirstChild()->IsText()) {
   1356    firstEditableLeaf = rootElement->GetFirstChild();
   1357  }
   1358  if (!firstEditableLeaf) {
   1359    // just the root node, set selection to inside the root
   1360    nsresult rv = CollapseSelectionToStartOf(*rootElement);
   1361    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1362                         "EditorBase::CollapseSelectionToStartOf() failed");
   1363    return rv;
   1364  }
   1365 
   1366  if (firstEditableLeaf->IsText()) {
   1367    // If firstEditableLeaf is text, set selection to beginning of the text
   1368    // node.
   1369    nsresult rv = CollapseSelectionToStartOf(*firstEditableLeaf);
   1370    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1371                         "EditorBase::CollapseSelectionToStartOf() failed");
   1372    return rv;
   1373  }
   1374 
   1375  // Otherwise, it's a leaf node and we set the selection just in front of it.
   1376  nsCOMPtr<nsIContent> parent = firstEditableLeaf->GetParent();
   1377  if (NS_WARN_IF(!parent)) {
   1378    return NS_ERROR_NULL_POINTER;
   1379  }
   1380 
   1381  MOZ_ASSERT(
   1382      parent->ComputeIndexOf(firstEditableLeaf).valueOr(UINT32_MAX) == 0,
   1383      "How come the first node isn't the left most child in its parent?");
   1384  nsresult rv = CollapseSelectionToStartOf(*parent);
   1385  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1386                       "EditorBase::CollapseSelectionToStartOf() failed");
   1387  return rv;
   1388 }
   1389 
   1390 NS_IMETHODIMP EditorBase::EndOfDocument() { return NS_ERROR_NOT_IMPLEMENTED; }
   1391 
   1392 NS_IMETHODIMP EditorBase::GetDocumentModified(bool* aOutDocModified) {
   1393  if (NS_WARN_IF(!aOutDocModified)) {
   1394    return NS_ERROR_INVALID_ARG;
   1395  }
   1396 
   1397  int32_t modCount = 0;
   1398  DebugOnly<nsresult> rvIgnored = GetModificationCount(&modCount);
   1399  NS_WARNING_ASSERTION(
   1400      NS_SUCCEEDED(rvIgnored),
   1401      "EditorBase::GetModificationCount() failed, but ignored");
   1402 
   1403  *aOutDocModified = (modCount != 0);
   1404  return NS_OK;
   1405 }
   1406 
   1407 NS_IMETHODIMP EditorBase::GetDocumentCharacterSet(nsACString& aCharacterSet) {
   1408  return NS_ERROR_NOT_AVAILABLE;
   1409 }
   1410 
   1411 nsresult EditorBase::GetDocumentCharsetInternal(nsACString& aCharset) const {
   1412  Document* document = GetDocument();
   1413  if (NS_WARN_IF(!document)) {
   1414    return NS_ERROR_NOT_INITIALIZED;
   1415  }
   1416  document->GetDocumentCharacterSet()->Name(aCharset);
   1417  return NS_OK;
   1418 }
   1419 
   1420 NS_IMETHODIMP EditorBase::SetDocumentCharacterSet(
   1421    const nsACString& aCharacterSet) {
   1422  return NS_ERROR_NOT_AVAILABLE;
   1423 }
   1424 
   1425 NS_IMETHODIMP EditorBase::OutputToString(const nsAString& aFormatType,
   1426                                         uint32_t aDocumentEncoderFlags,
   1427                                         nsAString& aOutputString) {
   1428  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1429  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1430    return NS_ERROR_NOT_INITIALIZED;
   1431  }
   1432 
   1433  nsresult rv =
   1434      ComputeValueInternal(aFormatType, aDocumentEncoderFlags, aOutputString);
   1435  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1436                       "EditorBase::ComputeValueInternal() failed");
   1437  // This is low level API for XUL application.  So, we should return raw
   1438  // error code here.
   1439  return rv;
   1440 }
   1441 
   1442 nsresult EditorBase::ComputeValueInternal(const nsAString& aFormatType,
   1443                                          uint32_t aDocumentEncoderFlags,
   1444                                          nsAString& aOutputString) const {
   1445  MOZ_ASSERT(IsEditActionDataAvailable());
   1446 
   1447  // First, let's try to get the value simply only from text node if the
   1448  // caller wants plaintext value.
   1449  if (aFormatType.LowerCaseEqualsLiteral("text/plain") &&
   1450      !(aDocumentEncoderFlags & (nsIDocumentEncoder::OutputSelectionOnly |
   1451                                 nsIDocumentEncoder::OutputWrap))) {
   1452    // Shortcut for empty editor case.
   1453    if (IsEmpty()) {
   1454      aOutputString.Truncate();
   1455      return NS_OK;
   1456    }
   1457    // NOTE: If it's neither <input type="text"> nor <textarea>, e.g., an HTML
   1458    // editor which is in plaintext mode (e.g., plaintext email composer on
   1459    // Thunderbird), it should be handled by the expensive path.
   1460    if (IsTextEditor()) {
   1461      // If it's necessary to check selection range or the editor wraps hard,
   1462      // we need some complicated handling.  In such case, we need to use the
   1463      // expensive path.
   1464      // XXX Anything else what we cannot return the text node data simply?
   1465      Result<EditActionResult, nsresult> result =
   1466          AsTextEditor()->ComputeValueFromTextNodeAndBRElement(aOutputString);
   1467      if (MOZ_UNLIKELY(result.isErr())) {
   1468        NS_WARNING("TextEditor::ComputeValueFromTextNodeAndBRElement() failed");
   1469        return result.unwrapErr();
   1470      }
   1471      if (!result.inspect().Ignored()) {
   1472        return NS_OK;
   1473      }
   1474    }
   1475  }
   1476 
   1477  nsAutoCString charset;
   1478  nsresult rv = GetDocumentCharsetInternal(charset);
   1479  if (NS_FAILED(rv) || charset.IsEmpty()) {
   1480    charset.AssignLiteral("windows-1252");  // XXX Why don't we use "UTF-8"?
   1481  }
   1482 
   1483  nsCOMPtr<nsIDocumentEncoder> encoder =
   1484      GetAndInitDocEncoder(aFormatType, aDocumentEncoderFlags, charset);
   1485  if (!encoder) {
   1486    NS_WARNING("EditorBase::GetAndInitDocEncoder() failed");
   1487    return NS_ERROR_FAILURE;
   1488  }
   1489 
   1490  rv = encoder->EncodeToString(aOutputString);
   1491  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1492                       "nsIDocumentEncoder::EncodeToString() failed");
   1493  return rv;
   1494 }
   1495 
   1496 already_AddRefed<nsIDocumentEncoder> EditorBase::GetAndInitDocEncoder(
   1497    const nsAString& aFormatType, uint32_t aDocumentEncoderFlags,
   1498    const nsACString& aCharset) const {
   1499  MOZ_ASSERT(IsEditActionDataAvailable());
   1500 
   1501  nsCOMPtr<nsIDocumentEncoder> docEncoder;
   1502  if (!mCachedDocumentEncoder ||
   1503      !mCachedDocumentEncoderType.Equals(aFormatType)) {
   1504    nsAutoCString formatType;
   1505    LossyAppendUTF16toASCII(aFormatType, formatType);
   1506    docEncoder = do_createDocumentEncoder(PromiseFlatCString(formatType).get());
   1507    if (NS_WARN_IF(!docEncoder)) {
   1508      return nullptr;
   1509    }
   1510    mCachedDocumentEncoder = docEncoder;
   1511    mCachedDocumentEncoderType = aFormatType;
   1512  } else {
   1513    docEncoder = mCachedDocumentEncoder;
   1514  }
   1515 
   1516  RefPtr<Document> doc = GetDocument();
   1517  NS_ASSERTION(doc, "Need a document");
   1518 
   1519  nsresult rv = docEncoder->Init(
   1520      doc, aFormatType,
   1521      aDocumentEncoderFlags | nsIDocumentEncoder::RequiresReinitAfterOutput);
   1522  if (NS_FAILED(rv)) {
   1523    NS_WARNING("nsIDocumentEncoder::NativeInit() failed");
   1524    return nullptr;
   1525  }
   1526 
   1527  if (!aCharset.IsEmpty() && !aCharset.EqualsLiteral("null")) {
   1528    DebugOnly<nsresult> rvIgnored = docEncoder->SetCharset(aCharset);
   1529    NS_WARNING_ASSERTION(
   1530        NS_SUCCEEDED(rvIgnored),
   1531        "nsIDocumentEncoder::SetCharset() failed, but ignored");
   1532  }
   1533 
   1534  const int32_t wrapWidth = std::max(WrapWidth(), 0);
   1535  DebugOnly<nsresult> rvIgnored = docEncoder->SetWrapColumn(wrapWidth);
   1536  NS_WARNING_ASSERTION(
   1537      NS_SUCCEEDED(rvIgnored),
   1538      "nsIDocumentEncoder::SetWrapColumn() failed, but ignored");
   1539 
   1540  // Set the selection, if appropriate.
   1541  // We do this either if the OutputSelectionOnly flag is set,
   1542  // in which case we use our existing selection ...
   1543  if (aDocumentEncoderFlags & nsIDocumentEncoder::OutputSelectionOnly) {
   1544    if (NS_FAILED(docEncoder->SetSelection(&SelectionRef()))) {
   1545      NS_WARNING("nsIDocumentEncoder::SetSelection() failed");
   1546      return nullptr;
   1547    }
   1548  }
   1549  // ... or if the root element is not a body,
   1550  // in which case we set the selection to encompass the root.
   1551  else {
   1552    Element* rootElement = GetRoot();
   1553    if (NS_WARN_IF(!rootElement)) {
   1554      return nullptr;
   1555    }
   1556    if (!rootElement->IsHTMLElement(nsGkAtoms::body)) {
   1557      if (NS_FAILED(docEncoder->SetContainerNode(rootElement))) {
   1558        NS_WARNING("nsIDocumentEncoder::SetContainerNode() failed");
   1559        return nullptr;
   1560      }
   1561    }
   1562  }
   1563 
   1564  return docEncoder.forget();
   1565 }
   1566 
   1567 bool EditorBase::AreClipboardCommandsUnconditionallyEnabled() const {
   1568  Document* document = GetDocument();
   1569  return document && document->AreClipboardCommandsUnconditionallyEnabled();
   1570 }
   1571 
   1572 bool EditorBase::CheckForClipboardCommandListener(
   1573    nsAtom* aCommand, EventMessage aEventMessage) const {
   1574  RefPtr<Document> document = GetDocument();
   1575  if (!document) {
   1576    return false;
   1577  }
   1578 
   1579  // We exclude XUL and chrome docs here to maintain current behavior where
   1580  // in these cases the editor element alone is expected to handle clipboard
   1581  // command availability.
   1582  if (!document->AreClipboardCommandsUnconditionallyEnabled()) {
   1583    return false;
   1584  }
   1585 
   1586  // So in web content documents, "unconditionally" enabled Cut/Copy are not
   1587  // really unconditional; they're enabled if there is a listener that wants
   1588  // to handle them. What they're not conditional on here is whether there is
   1589  // currently a selection in the editor.
   1590  RefPtr<PresShell> presShell = document->GetObservingPresShell();
   1591  if (!presShell) {
   1592    return false;
   1593  }
   1594  RefPtr<nsPresContext> presContext = presShell->GetPresContext();
   1595  if (!presContext) {
   1596    return false;
   1597  }
   1598 
   1599  RefPtr<EventTarget> et = IsHTMLEditor()
   1600                               ? AsHTMLEditor()->ComputeEditingHost(
   1601                                     HTMLEditor::LimitInBodyElement::No)
   1602                               : GetDOMEventTarget();
   1603 
   1604  while (et) {
   1605    EventListenerManager* elm = et->GetExistingListenerManager();
   1606    if (elm && elm->HasListenersFor(aCommand)) {
   1607      return true;
   1608    }
   1609    InternalClipboardEvent event(true, aEventMessage);
   1610    EventChainPreVisitor visitor(presContext, &event, nullptr,
   1611                                 nsEventStatus_eIgnore, false, et);
   1612    et->GetEventTargetParent(visitor);
   1613    et = visitor.GetParentTarget();
   1614  }
   1615 
   1616  return false;
   1617 }
   1618 
   1619 already_AddRefed<DataTransfer> EditorBase::CreateDataTransferForPaste(
   1620    EventMessage aEventMessage,
   1621    nsIClipboard::ClipboardType aClipboardType) const {
   1622  nsIGlobalObject* scopeObject = nullptr;
   1623  if (PresShell* presShell = GetPresShell()) {
   1624    if (Document* doc = presShell->GetDocument()) {
   1625      scopeObject = doc->GetScopeObject();
   1626    }
   1627  }
   1628 
   1629  auto dataTransfer = MakeRefPtr<DataTransfer>(scopeObject, aEventMessage, true,
   1630                                               Some(aClipboardType));
   1631  return dataTransfer.forget();
   1632 }
   1633 
   1634 Result<EditorBase::ClipboardEventResult, nsresult>
   1635 EditorBase::DispatchClipboardEventAndUpdateClipboard(
   1636    EventMessage aEventMessage,
   1637    Maybe<nsIClipboard::ClipboardType> aClipboardType,
   1638    DataTransfer* aDataTransfer /* = nullptr */) {
   1639  MOZ_ASSERT(IsEditActionDataAvailable());
   1640 
   1641  // Clipboard events are fired before `beforeinput` event.  Therefore, we
   1642  // need to forget mLastCollapsibleWhiteSpaceAppendedTextNode here to avoid
   1643  // infinite loop caused by the hack.
   1644  if (IsHTMLEditor()) {
   1645    AsHTMLEditor()->mLastCollapsibleWhiteSpaceAppendedTextNode = nullptr;
   1646  }
   1647 
   1648  const bool isPasting =
   1649      aEventMessage == ePaste || aEventMessage == ePasteNoFormatting;
   1650  if (isPasting) {
   1651    CommitComposition();
   1652    if (NS_WARN_IF(Destroyed())) {
   1653      return Err(NS_ERROR_EDITOR_DESTROYED);
   1654    }
   1655  }
   1656 
   1657  RefPtr<PresShell> presShell = GetPresShell();
   1658  if (NS_WARN_IF(!presShell)) {
   1659    return Err(NS_ERROR_NOT_AVAILABLE);
   1660  }
   1661 
   1662  const RefPtr<Selection> sel = [&]() {
   1663    if (IsHTMLEditor() && aEventMessage == eCopy &&
   1664        SelectionRef().IsCollapsed()) {
   1665      // If we don't have a usable selection for copy and we're an HTML
   1666      // editor (which is global for the document) try to use the last
   1667      // focused selection instead.
   1668      return nsCopySupport::GetSelectionForCopy(GetDocument());
   1669    }
   1670    return do_AddRef(&SelectionRef());
   1671  }();
   1672 
   1673  const auto GetDOMEventName = [&]() -> const char* {
   1674    switch (aEventMessage) {
   1675      case eCopy:
   1676        return "copy";
   1677      case eCut:
   1678        return "cut";
   1679      case ePaste:
   1680      case ePasteNoFormatting:
   1681        return "paste";
   1682      default:
   1683        return ToChar(aEventMessage);
   1684    }
   1685  };
   1686 
   1687  MOZ_LOG(
   1688      gEventLog, LogLevel::Info,
   1689      ("%p %s: Dispatching \"%s\" event...", this,
   1690       mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor", GetDOMEventName()));
   1691  bool actionTaken = false;
   1692  const bool doDefault = nsCopySupport::FireClipboardEvent(
   1693      aEventMessage, aClipboardType, presShell, sel, aDataTransfer,
   1694      &actionTaken);
   1695  MOZ_LOG(gEventLog, LogLevel::Info,
   1696          ("%p %s: Dispatched \"%s\" event, defaultPrevented=%s", this,
   1697           mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor", GetDOMEventName(),
   1698           doDefault ? "false" : "true"));
   1699  NotifyOfDispatchingClipboardEvent();
   1700 
   1701  if (NS_WARN_IF(Destroyed())) {
   1702    return Err(NS_ERROR_EDITOR_DESTROYED);
   1703  }
   1704 
   1705  if (doDefault) {
   1706    MOZ_ASSERT(actionTaken);
   1707    return ClipboardEventResult::DoDefault;
   1708  }
   1709  // If we handle a "paste" and nsCopySupport::FireClipboardEvent sets
   1710  // actionTaken to "false" means that it's an error.  Otherwise, the "paste"
   1711  // event is just canceled.
   1712  if (isPasting) {
   1713    return actionTaken ? ClipboardEventResult::DefaultPreventedOfPaste
   1714                       : ClipboardEventResult::IgnoredOrError;
   1715  }
   1716  // If we handle a "copy", actionTaken is set to true only when
   1717  // nsCopySupport::FireClipboardEvent does not meet an error.
   1718  // If we handle a "cut", actionTaken is set to true only when
   1719  // nsCopySupport::FireClipboardEvent does not meet an error and
   1720  // - the selection is collapsed in editable elements when the event is not
   1721  //   canceled.
   1722  // - the event is canceled but update the clipboard with the dataTransfer
   1723  //   of the event.
   1724  return actionTaken ? ClipboardEventResult::CopyOrCutHandled
   1725                     : ClipboardEventResult::IgnoredOrError;
   1726 }
   1727 
   1728 NS_IMETHODIMP EditorBase::Cut() {
   1729  nsresult rv = CutAsAction();
   1730  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed");
   1731  return rv;
   1732 }
   1733 
   1734 nsresult EditorBase::CutAsAction(nsIPrincipal* aPrincipal) {
   1735  AutoEditActionDataSetter editActionData(*this, EditAction::eCut, aPrincipal);
   1736  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1737    return NS_ERROR_NOT_INITIALIZED;
   1738  }
   1739 
   1740  {
   1741    RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
   1742    if (NS_WARN_IF(!focusManager)) {
   1743      return NS_ERROR_UNEXPECTED;
   1744    }
   1745    const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
   1746 
   1747    Result<ClipboardEventResult, nsresult> ret =
   1748        DispatchClipboardEventAndUpdateClipboard(
   1749            eCut, Some(nsIClipboard::kGlobalClipboard));
   1750    if (MOZ_UNLIKELY(ret.isErr())) {
   1751      NS_WARNING(
   1752          "EditorBase::DispatchClipboardEventAndUpdateClipboard(eCut, "
   1753          "nsIClipboard::kGlobalClipboard) failed");
   1754      return EditorBase::ToGenericNSResult(ret.unwrapErr());
   1755    }
   1756    switch (ret.unwrap()) {
   1757      case ClipboardEventResult::DoDefault:
   1758        break;
   1759      case ClipboardEventResult::CopyOrCutHandled:
   1760        return NS_OK;
   1761      case ClipboardEventResult::IgnoredOrError:
   1762        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   1763      case ClipboardEventResult::DefaultPreventedOfPaste:
   1764        MOZ_ASSERT_UNREACHABLE("Invalid result for eCut");
   1765    }
   1766 
   1767    // If focus is changed by a "cut" event listener, we should stop handling
   1768    // the cut.
   1769    const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
   1770    if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
   1771      if (focusManager->GetFocusedWindow() != GetWindow()) {
   1772        return NS_OK;
   1773      }
   1774      RefPtr<EditorBase> editorBase =
   1775          nsContentUtils::GetActiveEditor(GetPresContext());
   1776      if (!editorBase || (editorBase->IsHTMLEditor() &&
   1777                          !editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
   1778        return NS_OK;
   1779      }
   1780      if (editorBase != this) {
   1781        return NS_OK;
   1782      }
   1783    }
   1784  }
   1785 
   1786  // Dispatch "beforeinput" event after dispatching "cut" event.
   1787  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   1788  if (NS_FAILED(rv)) {
   1789    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1790                         "MaybeDispatchBeforeInputEvent() failed");
   1791    return EditorBase::ToGenericNSResult(rv);
   1792  }
   1793  // XXX This transaction name is referred by PlaceholderTransaction::Merge()
   1794  //     so that we need to keep using it here.
   1795  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName,
   1796                                             ScrollSelectionIntoView::Yes,
   1797                                             __FUNCTION__);
   1798  rv = DeleteSelectionAsSubAction(
   1799      eNone, IsTextEditor() ? nsIEditor::eNoStrip : nsIEditor::eStrip);
   1800  NS_WARNING_ASSERTION(
   1801      NS_SUCCEEDED(rv),
   1802      "EditorBase::DeleteSelectionAsSubAction(eNone) failed, but ignored");
   1803  return EditorBase::ToGenericNSResult(rv);
   1804 }
   1805 
   1806 NS_IMETHODIMP EditorBase::CanCut(bool* aCanCut) {
   1807  if (NS_WARN_IF(!aCanCut)) {
   1808    return NS_ERROR_INVALID_ARG;
   1809  }
   1810  *aCanCut = IsCutCommandEnabled();
   1811  return NS_OK;
   1812 }
   1813 
   1814 bool EditorBase::IsCutCommandEnabled() const {
   1815  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1816  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1817    return false;
   1818  }
   1819 
   1820  if (IsModifiable() && IsCopyToClipboardAllowedInternal()) {
   1821    return true;
   1822  }
   1823 
   1824  // If there's an event listener for "cut", we always enable the command
   1825  // as we don't really know what the listener may want to do in response.
   1826  // We look up the event target chain for a possible listener on a parent
   1827  // in addition to checking the immediate target.
   1828  return CheckForClipboardCommandListener(nsGkAtoms::oncut, eCut);
   1829 }
   1830 
   1831 NS_IMETHODIMP EditorBase::Copy() {
   1832  AutoEditActionDataSetter editActionData(*this, EditAction::eCopy);
   1833  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1834    return NS_ERROR_NOT_INITIALIZED;
   1835  }
   1836 
   1837  Result<ClipboardEventResult, nsresult> ret =
   1838      DispatchClipboardEventAndUpdateClipboard(
   1839          eCopy, Some(nsIClipboard::kGlobalClipboard));
   1840  if (MOZ_UNLIKELY(ret.isErr())) {
   1841    NS_WARNING(
   1842        "EditorBase::DispatchClipboardEventAndUpdateClipboard(eCopy, "
   1843        "nsIClipboard::kGlobalClipboard) failed");
   1844    return EditorBase::ToGenericNSResult(ret.unwrapErr());
   1845  }
   1846  switch (ret.unwrap()) {
   1847    case ClipboardEventResult::DoDefault:
   1848    case ClipboardEventResult::CopyOrCutHandled:
   1849      return NS_OK;
   1850    case ClipboardEventResult::IgnoredOrError:
   1851      return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   1852    case ClipboardEventResult::DefaultPreventedOfPaste:
   1853      MOZ_ASSERT_UNREACHABLE("Invalid result for eCopy");
   1854  }
   1855  return NS_ERROR_UNEXPECTED;
   1856 }
   1857 
   1858 NS_IMETHODIMP EditorBase::CanCopy(bool* aCanCopy) {
   1859  if (NS_WARN_IF(!aCanCopy)) {
   1860    return NS_ERROR_INVALID_ARG;
   1861  }
   1862  *aCanCopy = IsCopyCommandEnabled();
   1863  return NS_OK;
   1864 }
   1865 
   1866 bool EditorBase::IsCopyCommandEnabled() const {
   1867  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1868  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1869    return false;
   1870  }
   1871 
   1872  if (IsCopyToClipboardAllowedInternal()) {
   1873    return true;
   1874  }
   1875 
   1876  // Like "cut", always enable "copy" if there's a listener.
   1877  return CheckForClipboardCommandListener(nsGkAtoms::oncopy, eCopy);
   1878 }
   1879 
   1880 NS_IMETHODIMP EditorBase::Paste(nsIClipboard::ClipboardType aClipboardType) {
   1881  if (uint32_t(aClipboardType) >= nsIClipboard::kClipboardTypeCount) {
   1882    return NS_ERROR_INVALID_ARG;
   1883  }
   1884  const nsresult rv = PasteAsAction(aClipboardType, DispatchPasteEvent::Yes);
   1885  NS_WARNING_ASSERTION(
   1886      NS_SUCCEEDED(rv),
   1887      "EditorBase::PasteAsAction(DispatchPasteEvent::Yes) failed");
   1888  return rv;
   1889 }
   1890 
   1891 nsresult EditorBase::PasteAsAction(nsIClipboard::ClipboardType aClipboardType,
   1892                                   DispatchPasteEvent aDispatchPasteEvent,
   1893                                   DataTransfer* aDataTransfer /* = nullptr */,
   1894                                   nsIPrincipal* aPrincipal /* = nullptr */) {
   1895  if (IsHTMLEditor() && IsReadonly()) {
   1896    return NS_OK;
   1897  }
   1898 
   1899  // Create the same DataTransfer object here so we can share it between
   1900  // the clipboard event and the call to HandlePaste below. This prevents
   1901  // race conditions with Content Analysis on like we see in bug 1918027.
   1902  // Note that this is not needed if we're not going to dispatch the paste
   1903  // event and no aDataTransfer was passed in.
   1904  RefPtr<DataTransfer> dataTransfer = aDataTransfer;
   1905  if (!aDataTransfer && aDispatchPasteEvent == DispatchPasteEvent::Yes) {
   1906    dataTransfer = CreateDataTransferForPaste(ePaste, aClipboardType);
   1907  }
   1908  AutoEditActionDataSetter editActionData(*this, EditAction::ePaste,
   1909                                          aPrincipal);
   1910  const auto clearDataTransfer = MakeScopeExit([&] {
   1911    // If the caller passed in aDataTransfer, they are responsible for clearing
   1912    // this.
   1913    if (!aDataTransfer && dataTransfer) {
   1914      dataTransfer->ClearForPaste();
   1915    }
   1916  });
   1917  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1918    return NS_ERROR_NOT_INITIALIZED;
   1919  }
   1920 
   1921  if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
   1922    RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
   1923    if (NS_WARN_IF(!focusManager)) {
   1924      return NS_ERROR_UNEXPECTED;
   1925    }
   1926    const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
   1927 
   1928    Result<ClipboardEventResult, nsresult> ret = Err(NS_ERROR_FAILURE);
   1929    {
   1930      // This method is not set up to pass back the new aDataTransfer
   1931      // if it changes. If we need this in the future, we can change
   1932      // aDataTransfer to be a RefPtr<DataTransfer>*.
   1933      AutoTrackDataTransferForPaste trackDataTransfer(*this, dataTransfer);
   1934 
   1935      ret = DispatchClipboardEventAndUpdateClipboard(
   1936          ePaste, Some(aClipboardType), dataTransfer);
   1937      if (MOZ_UNLIKELY(ret.isErr())) {
   1938        NS_WARNING(
   1939            "EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
   1940            "failed");
   1941        return EditorBase::ToGenericNSResult(ret.unwrapErr());
   1942      }
   1943    }
   1944    switch (ret.inspect()) {
   1945      case ClipboardEventResult::DoDefault:
   1946        break;
   1947      case ClipboardEventResult::DefaultPreventedOfPaste:
   1948      case ClipboardEventResult::IgnoredOrError:
   1949        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   1950      case ClipboardEventResult::CopyOrCutHandled:
   1951        MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
   1952    }
   1953 
   1954    // If focus is changed by a "paste" event listener, we should keep handling
   1955    // the "pasting" in new focused editor because Chrome works as so.
   1956    const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
   1957    if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
   1958      // For the privacy reason, let's top handling it if new focused element is
   1959      // in different document.
   1960      if (focusManager->GetFocusedWindow() != GetWindow()) {
   1961        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   1962      }
   1963      RefPtr<EditorBase> editorBase =
   1964          nsContentUtils::GetActiveEditor(GetPresContext());
   1965      if (!editorBase || (editorBase->IsHTMLEditor() &&
   1966                          !editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
   1967        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   1968      }
   1969      if (editorBase != this) {
   1970        nsresult rv = editorBase->PasteAsAction(
   1971            aClipboardType, DispatchPasteEvent::No, dataTransfer, aPrincipal);
   1972        NS_WARNING_ASSERTION(
   1973            NS_SUCCEEDED(rv),
   1974            "EditorBase::PasteAsAction(DispatchPasteEvent::No) failed");
   1975        return EditorBase::ToGenericNSResult(rv);
   1976      }
   1977    }
   1978  } else {
   1979    // The caller must already have dispatched a "paste" event.
   1980    editActionData.NotifyOfDispatchingClipboardEvent();
   1981  }
   1982  nsresult rv = HandlePaste(editActionData, aClipboardType, dataTransfer);
   1983  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::HandlePaste() failed");
   1984  return EditorBase::ToGenericNSResult(rv);
   1985 }
   1986 
   1987 nsresult EditorBase::PasteAsQuotationAsAction(
   1988    nsIClipboard::ClipboardType aClipboardType,
   1989    DispatchPasteEvent aDispatchPasteEvent,
   1990    DataTransfer* aDataTransfer /* = nullptr */,
   1991    nsIPrincipal* aPrincipal /* = nullptr */) {
   1992  MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
   1993             aClipboardType == nsIClipboard::kSelectionClipboard);
   1994 
   1995  if (IsHTMLEditor() && IsReadonly()) {
   1996    return NS_OK;
   1997  }
   1998 
   1999  // Create the same DataTransfer object here so we can share it between
   2000  // the clipboard event and the call to HandlePasteAsQuotation below. This
   2001  // prevents race conditions with Content Analysis on like we see in bug
   2002  // 1918027.
   2003  // Note that this is not needed if we're not going to dispatch the paste
   2004  // event.
   2005  RefPtr<DataTransfer> dataTransfer;
   2006  if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
   2007    dataTransfer = aDataTransfer
   2008                       ? RefPtr<DataTransfer>(aDataTransfer)
   2009                       : RefPtr<DataTransfer>(CreateDataTransferForPaste(
   2010                             ePaste, aClipboardType));
   2011  }
   2012  const auto clearDataTransfer = MakeScopeExit([&] {
   2013    // If the caller passed in aDataTransfer, they are responsible for clearing
   2014    // this.
   2015    if (!aDataTransfer && dataTransfer) {
   2016      dataTransfer->ClearForPaste();
   2017    }
   2018  });
   2019  AutoEditActionDataSetter editActionData(*this, EditAction::ePasteAsQuotation,
   2020                                          aPrincipal);
   2021  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2022    return NS_ERROR_NOT_INITIALIZED;
   2023  }
   2024 
   2025  if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
   2026    RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
   2027    if (NS_WARN_IF(!focusManager)) {
   2028      return NS_ERROR_UNEXPECTED;
   2029    }
   2030    const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
   2031 
   2032    Result<ClipboardEventResult, nsresult> ret = Err(NS_ERROR_FAILURE);
   2033    {
   2034      // This method is not set up to pass back the new aDataTransfer
   2035      // if it changes. If we need this in the future, we can change
   2036      // aDataTransfer to be a RefPtr<DataTransfer>*.
   2037      MOZ_ASSERT(!aDataTransfer);
   2038      AutoTrackDataTransferForPaste trackDataTransfer(*this, dataTransfer);
   2039 
   2040      ret = DispatchClipboardEventAndUpdateClipboard(
   2041          ePaste, Some(aClipboardType), dataTransfer);
   2042      if (MOZ_UNLIKELY(ret.isErr())) {
   2043        NS_WARNING(
   2044            "EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
   2045            "failed");
   2046        return EditorBase::ToGenericNSResult(ret.unwrapErr());
   2047      }
   2048    }
   2049    switch (ret.inspect()) {
   2050      case ClipboardEventResult::DoDefault:
   2051        break;
   2052      case ClipboardEventResult::DefaultPreventedOfPaste:
   2053      case ClipboardEventResult::IgnoredOrError:
   2054        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   2055      case ClipboardEventResult::CopyOrCutHandled:
   2056        MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
   2057    }
   2058 
   2059    // If focus is changed by a "paste" event listener, we should keep handling
   2060    // the "pasting" in new focused editor because Chrome works as so.
   2061    const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
   2062    if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
   2063      // For the privacy reason, let's top handling it if new focused element is
   2064      // in different document.
   2065      if (focusManager->GetFocusedWindow() != GetWindow()) {
   2066        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   2067      }
   2068      RefPtr<EditorBase> editorBase =
   2069          nsContentUtils::GetActiveEditor(GetPresContext());
   2070      if (!editorBase || (editorBase->IsHTMLEditor() &&
   2071                          !editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
   2072        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   2073      }
   2074      if (editorBase != this) {
   2075        nsresult rv = editorBase->PasteAsQuotationAsAction(
   2076            aClipboardType, DispatchPasteEvent::No, dataTransfer, aPrincipal);
   2077        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2078                             "EditorBase::PasteAsQuotationAsAction("
   2079                             "DispatchPasteEvent::No) failed");
   2080        return EditorBase::ToGenericNSResult(rv);
   2081      }
   2082    }
   2083  } else {
   2084    // The caller must already have dispatched a "paste" event.
   2085    editActionData.NotifyOfDispatchingClipboardEvent();
   2086  }
   2087 
   2088  nsresult rv =
   2089      HandlePasteAsQuotation(editActionData, aClipboardType, dataTransfer);
   2090  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2091                       "EditorBase::HandlePasteAsQuotation() failed");
   2092  return EditorBase::ToGenericNSResult(rv);
   2093 }
   2094 
   2095 nsresult EditorBase::PasteTransferableAsAction(
   2096    nsITransferable* aTransferable, DispatchPasteEvent aDispatchPasteEvent,
   2097    nsIPrincipal* aPrincipal /* = nullptr */) {
   2098  // FIXME: This may be called as a call of nsIEditor::PasteTransferable.
   2099  // In this case, we should keep handling the paste even in the readonly mode.
   2100  if (IsHTMLEditor() && IsReadonly()) {
   2101    return NS_OK;
   2102  }
   2103 
   2104  AutoEditActionDataSetter editActionData(*this, EditAction::ePaste,
   2105                                          aPrincipal);
   2106  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2107    return NS_ERROR_NOT_INITIALIZED;
   2108  }
   2109 
   2110  if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
   2111    RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
   2112    if (NS_WARN_IF(!focusManager)) {
   2113      return NS_ERROR_UNEXPECTED;
   2114    }
   2115    const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
   2116 
   2117    // Use a nothing value for the clipboard type as data comes from
   2118    // aTransferable and we don't currently implement a way to put that in the
   2119    // data transfer in TextEditor yet.
   2120    Result<ClipboardEventResult, nsresult> ret =
   2121        DispatchClipboardEventAndUpdateClipboard(
   2122            ePaste,
   2123            IsTextEditor() ? Nothing() : Some(nsIClipboard::kGlobalClipboard));
   2124    if (MOZ_UNLIKELY(ret.isErr())) {
   2125      NS_WARNING(
   2126          "EditorBase::DispatchClipboardEventAndUpdateClipboard(ePaste) "
   2127          "failed");
   2128      return EditorBase::ToGenericNSResult(ret.unwrapErr());
   2129    }
   2130    switch (ret.inspect()) {
   2131      case ClipboardEventResult::DoDefault:
   2132        break;
   2133      case ClipboardEventResult::DefaultPreventedOfPaste:
   2134      case ClipboardEventResult::IgnoredOrError:
   2135        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   2136      case ClipboardEventResult::CopyOrCutHandled:
   2137        MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
   2138    }
   2139 
   2140    // If focus is changed by a "paste" event listener, we should keep handling
   2141    // the "pasting" in new focused editor because Chrome works as so.
   2142    const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
   2143    if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
   2144      // For the privacy reason, let's top handling it if new focused element is
   2145      // in different document.
   2146      if (focusManager->GetFocusedWindow() != GetWindow()) {
   2147        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   2148      }
   2149      RefPtr<EditorBase> editorBase =
   2150          nsContentUtils::GetActiveEditor(GetPresContext());
   2151      if (!editorBase || (editorBase->IsHTMLEditor() &&
   2152                          !editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
   2153        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
   2154      }
   2155      if (editorBase != this) {
   2156        nsresult rv = editorBase->PasteTransferableAsAction(
   2157            aTransferable, DispatchPasteEvent::No, aPrincipal);
   2158        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2159                             "EditorBase::PasteTransferableAsAction("
   2160                             "DispatchPasteEvent::No) failed");
   2161        return EditorBase::ToGenericNSResult(rv);
   2162      }
   2163    }
   2164  } else {
   2165    // The caller must already have dispatched a "paste" event.
   2166    editActionData.NotifyOfDispatchingClipboardEvent();
   2167  }
   2168 
   2169  if (NS_WARN_IF(!aTransferable)) {
   2170    return NS_ERROR_INVALID_ARG;
   2171  }
   2172 
   2173  nsresult rv = HandlePasteTransferable(editActionData, *aTransferable);
   2174  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2175                       "EditorBase::HandlePasteTransferable() failed");
   2176  return EditorBase::ToGenericNSResult(rv);
   2177 }
   2178 
   2179 nsresult EditorBase::PrepareToInsertContent(
   2180    const EditorDOMPoint& aPointToInsert,
   2181    DeleteSelectedContent aDeleteSelectedContent) {
   2182  // TODO: Move this method to `EditorBase`.
   2183  MOZ_ASSERT(IsEditActionDataAvailable());
   2184 
   2185  MOZ_ASSERT(aPointToInsert.IsSet());
   2186 
   2187  EditorDOMPoint pointToInsert(aPointToInsert);
   2188  if (aDeleteSelectedContent == DeleteSelectedContent::Yes) {
   2189    AutoTrackDOMPoint tracker(RangeUpdaterRef(), &pointToInsert);
   2190    nsresult rv = DeleteSelectionAsSubAction(
   2191        nsIEditor::eNone,
   2192        IsTextEditor() ? nsIEditor::eNoStrip : nsIEditor::eStrip);
   2193    if (NS_FAILED(rv)) {
   2194      NS_WARNING("EditorBase::DeleteSelectionAsSubAction(eNone) failed");
   2195      return rv;
   2196    }
   2197  }
   2198 
   2199  nsresult rv = CollapseSelectionTo(pointToInsert);
   2200  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2201                       "EditorBase::CollapseSelectionTo() failed");
   2202  return rv;
   2203 }
   2204 
   2205 nsresult EditorBase::InsertTextAt(
   2206    const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert,
   2207    DeleteSelectedContent aDeleteSelectedContent) {
   2208  MOZ_ASSERT(IsEditActionDataAvailable());
   2209  MOZ_ASSERT(aPointToInsert.IsSet());
   2210 
   2211  nsresult rv = PrepareToInsertContent(aPointToInsert, aDeleteSelectedContent);
   2212  if (NS_FAILED(rv)) {
   2213    NS_WARNING("EditorBase::PrepareToInsertContent() failed");
   2214    return rv;
   2215  }
   2216 
   2217  rv = InsertTextAsSubAction(aStringToInsert, InsertTextFor::NormalText);
   2218  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2219                       "EditorBase::InsertTextAsSubAction() failed");
   2220  return rv;
   2221 }
   2222 
   2223 EditorBase::SafeToInsertData EditorBase::IsSafeToInsertData(
   2224    nsIPrincipal* aSourcePrincipal) const {
   2225  // Try to determine whether we should use a sanitizing fragment sink
   2226  RefPtr<Document> destdoc = GetDocument();
   2227  NS_ASSERTION(destdoc, "Where is our destination doc?");
   2228 
   2229  nsIDocShell* docShell = nullptr;
   2230  if (RefPtr<BrowsingContext> bc = destdoc->GetBrowsingContext()) {
   2231    RefPtr<BrowsingContext> root = bc->Top();
   2232    MOZ_ASSERT(root, "root should not be null");
   2233 
   2234    docShell = root->GetDocShell();
   2235  }
   2236 
   2237  bool isSafe =
   2238      docShell && docShell->GetAppType() == nsIDocShell::APP_TYPE_EDITOR;
   2239 
   2240  if (!isSafe && aSourcePrincipal) {
   2241    nsIPrincipal* destPrincipal = destdoc->NodePrincipal();
   2242    NS_ASSERTION(destPrincipal, "How come we don't have a principal?");
   2243    DebugOnly<nsresult> rvIgnored =
   2244        aSourcePrincipal->Subsumes(destPrincipal, &isSafe);
   2245    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2246                         "nsIPrincipal::Subsumes() failed, but ignored");
   2247  }
   2248 
   2249  return isSafe ? SafeToInsertData::Yes : SafeToInsertData::No;
   2250 }
   2251 
   2252 NS_IMETHODIMP EditorBase::PasteTransferable(nsITransferable* aTransferable) {
   2253  nsresult rv =
   2254      PasteTransferableAsAction(aTransferable, DispatchPasteEvent::Yes);
   2255  NS_WARNING_ASSERTION(
   2256      NS_SUCCEEDED(rv),
   2257      "EditorBase::PasteTransferableAsAction(DispatchPasteEvent::Yes) failed");
   2258  return rv;
   2259 }
   2260 
   2261 NS_IMETHODIMP EditorBase::CanPaste(nsIClipboard::ClipboardType aClipboardType,
   2262                                   bool* aCanPaste) {
   2263  if (uint32_t(aClipboardType) >= nsIClipboard::kClipboardTypeCount) {
   2264    return NS_ERROR_INVALID_ARG;
   2265  }
   2266  if (NS_WARN_IF(!aCanPaste)) {
   2267    return NS_ERROR_INVALID_ARG;
   2268  }
   2269  *aCanPaste = CanPaste(aClipboardType);
   2270  return NS_OK;
   2271 }
   2272 
   2273 NS_IMETHODIMP EditorBase::SetAttribute(Element* aElement,
   2274                                       const nsAString& aAttribute,
   2275                                       const nsAString& aValue) {
   2276  if (NS_WARN_IF(aAttribute.IsEmpty()) || NS_WARN_IF(!aElement)) {
   2277    return NS_ERROR_INVALID_ARG;
   2278  }
   2279 
   2280  AutoEditActionDataSetter editActionData(*this, EditAction::eSetAttribute);
   2281  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   2282  if (NS_FAILED(rv)) {
   2283    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2284                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   2285    return EditorBase::ToGenericNSResult(rv);
   2286  }
   2287 
   2288  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
   2289  rv = SetAttributeWithTransaction(*aElement, *attribute, aValue);
   2290  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2291                       "EditorBase::SetAttributeWithTransaction() failed");
   2292  return EditorBase::ToGenericNSResult(rv);
   2293 }
   2294 
   2295 nsresult EditorBase::SetAttributeWithTransaction(Element& aElement,
   2296                                                 nsAtom& aAttribute,
   2297                                                 const nsAString& aValue) {
   2298  const RefPtr<ChangeAttributeTransaction> transaction =
   2299      ChangeAttributeTransaction::Create(*this, aElement, aAttribute, aValue);
   2300  nsresult rv = DoTransactionInternal(transaction);
   2301  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2302                       "EditorBase::DoTransactionInternal() failed");
   2303  return rv;
   2304 }
   2305 
   2306 NS_IMETHODIMP EditorBase::RemoveAttribute(Element* aElement,
   2307                                          const nsAString& aAttribute) {
   2308  if (NS_WARN_IF(aAttribute.IsEmpty()) || NS_WARN_IF(!aElement)) {
   2309    return NS_ERROR_INVALID_ARG;
   2310  }
   2311 
   2312  AutoEditActionDataSetter editActionData(*this, EditAction::eRemoveAttribute);
   2313  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   2314  if (NS_FAILED(rv)) {
   2315    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2316                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   2317    return EditorBase::ToGenericNSResult(rv);
   2318  }
   2319 
   2320  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
   2321  rv = RemoveAttributeWithTransaction(*aElement, *attribute);
   2322  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2323                       "EditorBase::RemoveAttributeWithTransaction() failed");
   2324  return EditorBase::ToGenericNSResult(rv);
   2325 }
   2326 
   2327 nsresult EditorBase::RemoveAttributeWithTransaction(Element& aElement,
   2328                                                    nsAtom& aAttribute) {
   2329  if (!aElement.HasAttr(&aAttribute)) {
   2330    return NS_OK;
   2331  }
   2332  const RefPtr<ChangeAttributeTransaction> transaction =
   2333      ChangeAttributeTransaction::CreateToRemove(*this, aElement, aAttribute);
   2334  nsresult rv = DoTransactionInternal(transaction);
   2335  if (NS_WARN_IF(Destroyed())) {
   2336    return NS_ERROR_EDITOR_DESTROYED;
   2337  }
   2338  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2339                       "EditorBase::DoTransactionInternal() failed");
   2340  return rv;
   2341 }
   2342 
   2343 nsresult EditorBase::MarkElementDirty(Element& aElement) {
   2344  // Mark the node dirty, but not for webpages (bug 599983)
   2345  if (!OutputsMozDirty()) {
   2346    return NS_OK;
   2347  }
   2348  nsresult rv = AutoElementAttrAPIWrapper(*this, aElement)
   2349                    .SetAttr(nsGkAtoms::mozdirty, EmptyString(), false);
   2350  NS_WARNING_ASSERTION(
   2351      NS_SUCCEEDED(rv),
   2352      "AutoElementAttrAPIWrapper::SetAttr() failed, but ignored");
   2353  return rv;
   2354 }
   2355 
   2356 NS_IMETHODIMP EditorBase::GetInlineSpellChecker(
   2357    bool aAutoCreate, nsIInlineSpellChecker** aInlineSpellChecker) {
   2358  if (NS_WARN_IF(!aInlineSpellChecker)) {
   2359    return NS_ERROR_INVALID_ARG;
   2360  }
   2361 
   2362  if (mDidPreDestroy) {
   2363    // Don't allow people to get or create the spell checker once the editor
   2364    // is going away.
   2365    *aInlineSpellChecker = nullptr;
   2366    return aAutoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   2367  }
   2368 
   2369  // We don't want to show the spell checking UI if there are no spell check
   2370  // dictionaries available.
   2371  if (!mozInlineSpellChecker::CanEnableInlineSpellChecking()) {
   2372    *aInlineSpellChecker = nullptr;
   2373    return NS_ERROR_FAILURE;
   2374  }
   2375 
   2376  if (!mInlineSpellChecker && aAutoCreate) {
   2377    mInlineSpellChecker = new mozInlineSpellChecker();
   2378  }
   2379 
   2380  if (mInlineSpellChecker) {
   2381    nsresult rv = mInlineSpellChecker->Init(this);
   2382    if (NS_FAILED(rv)) {
   2383      NS_WARNING("mozInlineSpellChecker::Init() failed");
   2384      mInlineSpellChecker = nullptr;
   2385      return rv;
   2386    }
   2387  }
   2388 
   2389  *aInlineSpellChecker = do_AddRef(mInlineSpellChecker).take();
   2390  return NS_OK;
   2391 }
   2392 
   2393 void EditorBase::SyncRealTimeSpell() {
   2394  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   2395  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2396    return;
   2397  }
   2398 
   2399  bool enable = GetDesiredSpellCheckState();
   2400 
   2401  // Initializes mInlineSpellChecker
   2402  nsCOMPtr<nsIInlineSpellChecker> spellChecker;
   2403  DebugOnly<nsresult> rvIgnored =
   2404      GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
   2405  NS_WARNING_ASSERTION(
   2406      NS_SUCCEEDED(rvIgnored),
   2407      "EditorBase::GetInlineSpellChecker() failed, but ignored");
   2408 
   2409  if (mInlineSpellChecker) {
   2410    if (!mSpellCheckerDictionaryUpdated && enable) {
   2411      DebugOnly<nsresult> rvIgnored =
   2412          mInlineSpellChecker->UpdateCurrentDictionary();
   2413      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2414                           "mozInlineSpellChecker::UpdateCurrentDictionary() "
   2415                           "failed, but ignored");
   2416      mSpellCheckerDictionaryUpdated = true;
   2417    }
   2418 
   2419    // We might have a mInlineSpellChecker even if there are no dictionaries
   2420    // available since we don't destroy the mInlineSpellChecker when the last
   2421    // dictionariy is removed, but in that case spellChecker is null
   2422    DebugOnly<nsresult> rvIgnored =
   2423        mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
   2424    NS_WARNING_ASSERTION(
   2425        NS_SUCCEEDED(rvIgnored),
   2426        "mozInlineSpellChecker::SetEnableRealTimeSpell() failed, but ignored");
   2427  }
   2428 }
   2429 
   2430 NS_IMETHODIMP EditorBase::SetSpellcheckUserOverride(bool enable) {
   2431  mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
   2432  SyncRealTimeSpell();
   2433  return NS_OK;
   2434 }
   2435 
   2436 NS_IMETHODIMP EditorBase::InsertNode(nsINode* aNodeToInsert,
   2437                                     nsINode* aContainer, uint32_t aOffset,
   2438                                     bool aPreserveSelection,
   2439                                     uint8_t aOptionalArgCount) {
   2440  MOZ_DIAGNOSTIC_ASSERT(IsHTMLEditor());
   2441 
   2442  nsCOMPtr<nsIContent> contentToInsert =
   2443      nsIContent::FromNodeOrNull(aNodeToInsert);
   2444  if (NS_WARN_IF(!contentToInsert) || NS_WARN_IF(!aContainer)) {
   2445    return NS_ERROR_NULL_POINTER;
   2446  }
   2447 
   2448  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertNode);
   2449  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   2450  if (NS_FAILED(rv)) {
   2451    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2452                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   2453    return EditorBase::ToGenericNSResult(rv);
   2454  }
   2455 
   2456  // Make dispatch `input` event after stopping preserving selection.
   2457  AutoPlaceholderBatch treatAsOneTransaction(
   2458      *this,
   2459      ScrollSelectionIntoView::No,  // not a user interaction
   2460      __FUNCTION__);
   2461 
   2462  Maybe<AutoTransactionsConserveSelection> preseveSelection;
   2463  if (aOptionalArgCount && aPreserveSelection) {
   2464    preseveSelection.emplace(*this);
   2465  }
   2466 
   2467  const uint32_t offset = std::min(aOffset, aContainer->Length());
   2468  Result<CreateContentResult, nsresult> insertContentResult =
   2469      InsertNodeWithTransaction(*contentToInsert,
   2470                                EditorDOMPoint(aContainer, offset));
   2471  if (MOZ_UNLIKELY(insertContentResult.isErr())) {
   2472    NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   2473    return EditorBase::ToGenericNSResult(insertContentResult.unwrapErr());
   2474  }
   2475  rv = insertContentResult.inspect().SuggestCaretPointTo(
   2476      *this, {SuggestCaret::OnlyIfHasSuggestion,
   2477              SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
   2478              SuggestCaret::AndIgnoreTrivialError});
   2479  if (NS_FAILED(rv)) {
   2480    NS_WARNING("CreateContentResult::SuggestCaretPointTo() failed");
   2481    return EditorBase::ToGenericNSResult(rv);
   2482  }
   2483  NS_WARNING_ASSERTION(
   2484      rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
   2485      "CreateContentResult::SuggestCaretPointTo() failed, but ignored");
   2486  return NS_OK;
   2487 }
   2488 
   2489 template <typename ContentNodeType>
   2490 Result<CreateNodeResultBase<ContentNodeType>, nsresult>
   2491 EditorBase::InsertNodeWithTransaction(ContentNodeType& aContentToInsert,
   2492                                      const EditorDOMPoint& aPointToInsert) {
   2493  MOZ_ASSERT(IsEditActionDataAvailable());
   2494  MOZ_ASSERT_IF(IsTextEditor(), !aContentToInsert.IsText());
   2495 
   2496  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
   2497    return Err(NS_ERROR_INVALID_ARG);
   2498  }
   2499  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   2500 
   2501  IgnoredErrorResult ignoredError;
   2502  AutoEditSubActionNotifier startToHandleEditSubAction(
   2503      *this, EditSubAction::eInsertNode, nsIEditor::eNext, ignoredError);
   2504  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   2505    return Err(ignoredError.StealNSResult());
   2506  }
   2507  NS_WARNING_ASSERTION(
   2508      !ignoredError.Failed(),
   2509      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   2510 
   2511  RefPtr<InsertNodeTransaction> transaction =
   2512      InsertNodeTransaction::Create(*this, aContentToInsert, aPointToInsert);
   2513  nsresult rv = DoTransactionInternal(transaction);
   2514  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2515                       "EditorBase::DoTransactionInternal() failed");
   2516 
   2517  DebugOnly<nsresult> rvIgnored =
   2518      RangeUpdaterRef().SelAdjInsertNode(aPointToInsert);
   2519  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2520                       "RangeUpdater::SelAdjInsertNode() failed, but ignored");
   2521 
   2522  if (NS_WARN_IF(Destroyed())) {
   2523    return Err(NS_ERROR_EDITOR_DESTROYED);
   2524  }
   2525  if (NS_WARN_IF(aContentToInsert.GetParentNode() !=
   2526                 aPointToInsert.GetContainer())) {
   2527    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   2528  }
   2529  if (NS_FAILED(rv)) {
   2530    return Err(rv);
   2531  }
   2532 
   2533  if (IsHTMLEditor()) {
   2534    TopLevelEditSubActionDataRef().DidInsertContent(*this, aContentToInsert);
   2535  }
   2536 
   2537  return CreateNodeResultBase<ContentNodeType>(
   2538      aContentToInsert, transaction->SuggestPointToPutCaret<EditorDOMPoint>());
   2539 }
   2540 
   2541 Result<CreateElementResult, nsresult>
   2542 EditorBase::InsertPaddingBRElementForEmptyLastLineWithTransaction(
   2543    const EditorDOMPoint& aPointToInsert) {
   2544  MOZ_ASSERT(IsEditActionDataAvailable());
   2545  MOZ_ASSERT(IsHTMLEditor() || !aPointToInsert.IsInTextNode());
   2546 
   2547  if (MOZ_UNLIKELY(!aPointToInsert.IsSet())) {
   2548    return Err(NS_ERROR_FAILURE);
   2549  }
   2550 
   2551  EditorDOMPoint pointToInsert;
   2552  if (IsTextEditor()) {
   2553    pointToInsert = aPointToInsert;
   2554  } else {
   2555    Result<EditorDOMPoint, nsresult> maybePointToInsert =
   2556        MOZ_KnownLive(AsHTMLEditor())
   2557            ->PrepareToInsertLineBreak(HTMLEditor::LineBreakType::BRElement,
   2558                                       aPointToInsert);
   2559    if (maybePointToInsert.isErr()) {
   2560      return maybePointToInsert.propagateErr();
   2561    }
   2562    MOZ_ASSERT(maybePointToInsert.inspect().IsSetAndValid());
   2563    pointToInsert = maybePointToInsert.unwrap();
   2564  }
   2565 
   2566  Result<CreateElementResult, nsresult> insertBRElementResultOrError =
   2567      InsertBRElement(WithTransaction::Yes,
   2568                      BRElementType::PaddingForEmptyLastLine, pointToInsert);
   2569  NS_WARNING_ASSERTION(insertBRElementResultOrError.isOk(),
   2570                       "EditorBase::InsertBRElement(WithTransaction::Yes, "
   2571                       "BRElementType::PaddingForEmptyLastLine) failed");
   2572  return insertBRElementResultOrError;
   2573 }
   2574 
   2575 nsresult EditorBase::UpdateBRElementType(HTMLBRElement& aBRElement,
   2576                                         BRElementType aNewType) {
   2577  const bool brElementIsHidden = aBRElement.IsPaddingForEmptyEditor() ||
   2578                                 aBRElement.IsPaddingForEmptyLastLine();
   2579  const bool brElementWillBeHidden = aNewType != BRElementType::Normal;
   2580  const auto SetBRElementFlags = [&]() {
   2581    switch (aNewType) {
   2582      case BRElementType::Normal:
   2583        if (brElementIsHidden) {
   2584          aBRElement.UnsetFlags(NS_PADDING_FOR_EMPTY_EDITOR |
   2585                                NS_PADDING_FOR_EMPTY_LAST_LINE);
   2586        }
   2587        break;
   2588      case BRElementType::PaddingForEmptyEditor:
   2589        if (brElementIsHidden) {
   2590          aBRElement.UnsetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
   2591        }
   2592        aBRElement.SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
   2593        break;
   2594      case BRElementType::PaddingForEmptyLastLine:
   2595        if (brElementIsHidden) {
   2596          aBRElement.UnsetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
   2597        }
   2598        aBRElement.SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
   2599        break;
   2600    }
   2601  };
   2602  // If the <br> element is in the composed doc, it must be observed by
   2603  // IMEContentObserver.  However, IMEContentObserver cannot observe the state
   2604  // change, but changing the <br> type may make the <br> element visible or
   2605  // invisible for ContentEventHandler.  Therefore, IMEContentObserver needs to
   2606  // notify IME of the state change as a text change notification of adding or
   2607  // removing a line break.  Therefore, we need to reconnect the <br> element
   2608  // temporarily for making IMEContentObserver observable this change.
   2609  if (!aBRElement.IsInComposedDoc() ||
   2610      brElementIsHidden == brElementWillBeHidden) {
   2611    SetBRElementFlags();
   2612    return NS_OK;
   2613  }
   2614  EditorDOMPoint pointToInsert(&aBRElement);
   2615  {
   2616    AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
   2617    nsresult rv = DeleteNodeWithTransaction(aBRElement);
   2618    if (NS_FAILED(rv)) {
   2619      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   2620      return rv;
   2621    }
   2622  }
   2623  if (NS_WARN_IF(!pointToInsert.IsSetAndValid())) {
   2624    return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
   2625  }
   2626  SetBRElementFlags();
   2627  Result<CreateElementResult, nsresult> result =
   2628      InsertNodeWithTransaction<Element>(aBRElement, pointToInsert);
   2629  if (MOZ_UNLIKELY(result.isErr())) {
   2630    NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   2631    return result.unwrapErr();
   2632  }
   2633  result.inspect().IgnoreCaretPointSuggestion();
   2634  return NS_OK;
   2635 }
   2636 
   2637 Result<CreateElementResult, nsresult> EditorBase::InsertBRElement(
   2638    WithTransaction aWithTransaction, BRElementType aBRElementType,
   2639    const EditorDOMPoint& aPointToInsert) {
   2640  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   2641 
   2642  const RefPtr<HTMLBRElement> newBRElement =
   2643      HTMLBRElement::FromNodeOrNull(RefPtr{CreateHTMLContent(nsGkAtoms::br)});
   2644  if (MOZ_UNLIKELY(!newBRElement)) {
   2645    NS_WARNING("EditorBase::CreateHTMLContent() failed");
   2646    return Err(NS_ERROR_FAILURE);
   2647  }
   2648  nsresult rv = MarkElementDirty(*newBRElement);
   2649  if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
   2650    NS_WARNING("EditorBase::MarkElementDirty() caused destroying the editor");
   2651    return Err(NS_ERROR_EDITOR_DESTROYED);
   2652  }
   2653  if (aBRElementType != BRElementType::Normal) {
   2654    nsresult rv = UpdateBRElementType(*newBRElement, aBRElementType);
   2655    if (NS_FAILED(rv)) {
   2656      NS_WARNING("EditorBase::UpdateBRElementType() failed");
   2657      return Err(rv);
   2658    }
   2659  }
   2660  if (aWithTransaction == WithTransaction::Yes) {
   2661    Result<CreateElementResult, nsresult> insertBRElementResultOrError =
   2662        InsertNodeWithTransaction<Element>(*newBRElement, aPointToInsert);
   2663    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   2664      NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   2665      return insertBRElementResultOrError.propagateErr();
   2666    }
   2667    CreateElementResult insertBRElementResult =
   2668        insertBRElementResultOrError.unwrap();
   2669    insertBRElementResult.IgnoreCaretPointSuggestion();
   2670  } else {
   2671    (void)aPointToInsert.Offset();
   2672    RefPtr<InsertNodeTransaction> transaction =
   2673        InsertNodeTransaction::Create(*this, *newBRElement, aPointToInsert);
   2674    nsresult rv = transaction->DoTransaction();
   2675    if (NS_WARN_IF(Destroyed())) {
   2676      return Err(NS_ERROR_EDITOR_DESTROYED);
   2677    }
   2678    if (NS_FAILED(rv)) {
   2679      NS_WARNING("InsertNodeTransaction::DoTransaction() failed");
   2680      return Err(rv);
   2681    }
   2682    RangeUpdaterRef().SelAdjInsertNode(EditorRawDOMPoint(
   2683        aPointToInsert.GetContainer(), aPointToInsert.Offset()));
   2684  }
   2685  if (NS_WARN_IF(newBRElement->GetParentNode() !=
   2686                 aPointToInsert.GetContainer())) {
   2687    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   2688  }
   2689  return CreateElementResult(
   2690      *newBRElement,
   2691      EditorDOMPoint(newBRElement, aBRElementType == BRElementType::Normal
   2692                                       ? InterlinePosition::StartOfNextLine
   2693                                       : InterlinePosition::EndOfLine));
   2694 }
   2695 
   2696 NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode, bool aPreserveSelection,
   2697                                     uint8_t aOptionalArgCount) {
   2698  MOZ_ASSERT_UNREACHABLE("Do not use this API with TextEditor");
   2699  return NS_ERROR_NOT_IMPLEMENTED;
   2700 }
   2701 
   2702 nsresult EditorBase::DeleteNodeWithTransaction(nsIContent& aContent) {
   2703  MOZ_ASSERT(IsEditActionDataAvailable());
   2704  MOZ_ASSERT_IF(IsTextEditor(), !aContent.IsText());
   2705 
   2706  // Do nothing if the node is read-only.
   2707  if (IsHTMLEditor() && NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(aContent))) {
   2708    return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
   2709  }
   2710 
   2711  IgnoredErrorResult ignoredError;
   2712  AutoEditSubActionNotifier startToHandleEditSubAction(
   2713      *this, EditSubAction::eDeleteNode, nsIEditor::ePrevious, ignoredError);
   2714  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   2715    return ignoredError.StealNSResult();
   2716  }
   2717  NS_WARNING_ASSERTION(
   2718      !ignoredError.Failed(),
   2719      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   2720 
   2721  if (IsHTMLEditor()) {
   2722    TopLevelEditSubActionDataRef().WillDeleteContent(*this, aContent);
   2723  }
   2724 
   2725  // FYI: DeleteNodeTransaction grabs aContent while it's alive.  So, it's safe
   2726  //      to refer aContent even after calling DoTransaction().
   2727  RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
   2728      DeleteNodeTransaction::MaybeCreate(*this, aContent);
   2729  NS_WARNING_ASSERTION(deleteNodeTransaction,
   2730                       "DeleteNodeTransaction::MaybeCreate() failed");
   2731  nsresult rv;
   2732  if (deleteNodeTransaction) {
   2733    rv = DoTransactionInternal(deleteNodeTransaction);
   2734    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2735                         "EditorBase::DoTransactionInternal() failed");
   2736 
   2737    if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
   2738      RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
   2739      textServicesDocument->DidDeleteContent(aContent);
   2740    }
   2741  } else {
   2742    rv = NS_ERROR_FAILURE;
   2743  }
   2744 
   2745  if (!mActionListeners.IsEmpty()) {
   2746    for (auto& listener : mActionListeners.Clone()) {
   2747      DebugOnly<nsresult> rvIgnored = listener->DidDeleteNode(&aContent, rv);
   2748      NS_WARNING_ASSERTION(
   2749          NS_SUCCEEDED(rvIgnored),
   2750          "nsIEditActionListener::DidDeleteNode() failed, but ignored");
   2751    }
   2752  }
   2753 
   2754  return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : rv;
   2755 }
   2756 
   2757 NS_IMETHODIMP EditorBase::NotifySelectionChanged(Document* aDocument,
   2758                                                 Selection* aSelection,
   2759                                                 int16_t aReason,
   2760                                                 int32_t aAmount) {
   2761  if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
   2762    return NS_ERROR_INVALID_ARG;
   2763  }
   2764 
   2765  if (mTextInputListener) {
   2766    RefPtr<TextInputListener> textInputListener = mTextInputListener;
   2767    textInputListener->OnSelectionChange(*aSelection, aReason);
   2768  }
   2769 
   2770  if (mIMEContentObserver) {
   2771    RefPtr<IMEContentObserver> observer = mIMEContentObserver;
   2772    observer->OnSelectionChange(*aSelection);
   2773  }
   2774 
   2775  return NS_OK;
   2776 }
   2777 
   2778 void EditorBase::NotifyEditorObservers(
   2779    NotificationForEditorObservers aNotification) {
   2780  MOZ_ASSERT(IsEditActionDataAvailable());
   2781 
   2782  switch (aNotification) {
   2783    case eNotifyEditorObserversOfEnd:
   2784      mIsInEditSubAction = false;
   2785 
   2786      if (mEditActionData) {
   2787        mEditActionData->MarkAsHandled();
   2788      }
   2789 
   2790      if (mTextInputListener) {
   2791        // TODO: TextInputListener::OnEditActionHandled() may return
   2792        //       NS_ERROR_OUT_OF_MEMORY.  If so and if
   2793        //       TextControlState::SetValue() setting value with us, we should
   2794        //       return the result to EditorBase::ReplaceTextAsAction(),
   2795        //       EditorBase::DeleteSelectionAsAction() and
   2796        //       TextEditor::InsertTextAsAction().  However, it requires a lot
   2797        //       of changes in editor classes, but it's not so important since
   2798        //       editor does not use fallible allocation.  Therefore, normally,
   2799        //       the process must be crashed anyway.
   2800        RefPtr<TextInputListener> listener = mTextInputListener;
   2801        nsresult rv =
   2802            listener->OnEditActionHandled(MOZ_KnownLive(*AsTextEditor()));
   2803        MOZ_RELEASE_ASSERT(rv != NS_ERROR_OUT_OF_MEMORY,
   2804                           "Setting value failed due to out of memory");
   2805        NS_WARNING_ASSERTION(
   2806            NS_SUCCEEDED(rv),
   2807            "TextInputListener::OnEditActionHandled() failed, but ignored");
   2808      }
   2809 
   2810      if (mIMEContentObserver) {
   2811        RefPtr<IMEContentObserver> observer = mIMEContentObserver;
   2812        observer->OnEditActionHandled();
   2813      }
   2814 
   2815      if (!mDispatchInputEvent || IsEditActionAborted() ||
   2816          IsEditActionCanceled()) {
   2817        MOZ_LOG(
   2818            gEventLog, LogLevel::Warning,
   2819            ("%p %s: Not dispatching \"input\" event (mDispatchInputEvent=%s, "
   2820             "IsEditActionAborted()=%s, IsEditActionCanceled()=%s",
   2821             this, mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   2822             mDispatchInputEvent ? "true" : "false",
   2823             IsEditActionAborted() ? "true" : "false",
   2824             IsEditActionCanceled() ? "true" : "false"));
   2825        break;
   2826      }
   2827 
   2828      DispatchInputEvent();
   2829      break;
   2830    case eNotifyEditorObserversOfBefore:
   2831      if (NS_WARN_IF(mIsInEditSubAction)) {
   2832        return;
   2833      }
   2834 
   2835      mIsInEditSubAction = true;
   2836 
   2837      if (mIMEContentObserver) {
   2838        RefPtr<IMEContentObserver> observer = mIMEContentObserver;
   2839        observer->BeforeEditAction();
   2840      }
   2841      return;
   2842    case eNotifyEditorObserversOfCancel:
   2843      mIsInEditSubAction = false;
   2844 
   2845      if (mEditActionData) {
   2846        mEditActionData->MarkAsHandled();
   2847      }
   2848 
   2849      if (mIMEContentObserver) {
   2850        RefPtr<IMEContentObserver> observer = mIMEContentObserver;
   2851        observer->CancelEditAction();
   2852      }
   2853      break;
   2854    default:
   2855      MOZ_CRASH("Handle all notifications here");
   2856      break;
   2857  }
   2858 
   2859  if (IsHTMLEditor() && !Destroyed()) {
   2860    // We may need to show resizing handles or update existing ones after
   2861    // all transactions are done. This way of doing is preferred to DOM
   2862    // mutation events listeners because all the changes the user can apply
   2863    // to a document may result in multiple events, some of them quite hard
   2864    // to listen too (in particular when an ancestor of the selection is
   2865    // changed but the selection itself is not changed).
   2866    DebugOnly<nsresult> rvIgnored =
   2867        MOZ_KnownLive(AsHTMLEditor())->RefreshEditingUI();
   2868    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2869                         "HTMLEditor::RefreshEditingUI() failed, but ignored");
   2870  }
   2871 }
   2872 
   2873 void EditorBase::DispatchInputEvent() {
   2874  MOZ_ASSERT(IsEditActionDataAvailable());
   2875  MOZ_ASSERT(!IsEditActionCanceled(),
   2876             "If preceding beforeinput event is canceled, we shouldn't "
   2877             "dispatch input event");
   2878  MOZ_ASSERT(
   2879      !ShouldAlreadyHaveHandledBeforeInputEventDispatching(),
   2880      "We've not handled beforeinput event but trying to dispatch input event");
   2881 
   2882  // We don't need to dispatch multiple input events if there is a pending
   2883  // input event.  However, it may have different event target.  If we resolved
   2884  // this issue, we need to manage the pending events in an array.  But it's
   2885  // overwork.  We don't need to do it for the very rare case.
   2886  // TODO: However, we start to set InputEvent.inputType.  So, each "input"
   2887  //       event now notifies web app each change.  So, perhaps, we should
   2888  //       not omit input events.
   2889 
   2890  RefPtr<Element> targetElement = GetInputEventTargetElement();
   2891  if (NS_WARN_IF(!targetElement)) {
   2892    MOZ_LOG(gEventLog, LogLevel::Error,
   2893            ("%p %s: Failed dispatching \"input\" event due to no target", this,
   2894             mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor"));
   2895    return;
   2896  }
   2897  RefPtr<DataTransfer> dataTransfer = GetInputEventDataTransfer();
   2898  mEditActionData->WillDispatchInputEvent();
   2899  MOZ_LOG(gEventLog, LogLevel::Info,
   2900          ("%p %s: Dispatching \"input\" event: { inputType=\"%s\" }...", this,
   2901           mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   2902           ToString(ToInputType(GetEditAction())).c_str()));
   2903  DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(
   2904      targetElement, eEditorInput, ToInputType(GetEditAction()), this,
   2905      dataTransfer ? InputEventOptions(dataTransfer,
   2906                                       InputEventOptions::NeverCancelable::No)
   2907                   : InputEventOptions(GetInputEventData(),
   2908                                       InputEventOptions::NeverCancelable::No));
   2909  MOZ_LOG(gEventLog, LogLevel::Debug,
   2910          ("%p %s: Dispatched \"input\" event: { inputType=\"%s\" }", this,
   2911           mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   2912           ToString(ToInputType(GetEditAction())).c_str()));
   2913  mEditActionData->DidDispatchInputEvent();
   2914  NS_WARNING_ASSERTION(
   2915      NS_SUCCEEDED(rvIgnored),
   2916      "nsContentUtils::DispatchInputEvent() failed, but ignored");
   2917 }
   2918 
   2919 NS_IMETHODIMP EditorBase::AddEditActionListener(
   2920    nsIEditActionListener* aListener) {
   2921  if (NS_WARN_IF(!aListener)) {
   2922    return NS_ERROR_INVALID_ARG;
   2923  }
   2924 
   2925  // If given edit action listener is text services document for the inline
   2926  // spell checker, store it as reference of concrete class for performance
   2927  // reason.
   2928  if (mInlineSpellChecker) {
   2929    EditorSpellCheck* editorSpellCheck =
   2930        mInlineSpellChecker->GetEditorSpellCheck();
   2931    if (editorSpellCheck) {
   2932      mozSpellChecker* spellChecker = editorSpellCheck->GetSpellChecker();
   2933      if (spellChecker) {
   2934        TextServicesDocument* textServicesDocument =
   2935            spellChecker->GetTextServicesDocument();
   2936        if (static_cast<nsIEditActionListener*>(textServicesDocument) ==
   2937            aListener) {
   2938          mTextServicesDocument = textServicesDocument;
   2939          return NS_OK;
   2940        }
   2941      }
   2942    }
   2943  }
   2944 
   2945  // Make sure the listener isn't already on the list
   2946  if (!mActionListeners.Contains(aListener)) {
   2947    mActionListeners.AppendElement(*aListener);
   2948    NS_WARNING_ASSERTION(
   2949        mActionListeners.Length() != 1,
   2950        "nsIEditActionListener installed, this editor becomes slower");
   2951  }
   2952 
   2953  return NS_OK;
   2954 }
   2955 
   2956 NS_IMETHODIMP EditorBase::RemoveEditActionListener(
   2957    nsIEditActionListener* aListener) {
   2958  if (NS_WARN_IF(!aListener)) {
   2959    return NS_ERROR_INVALID_ARG;
   2960  }
   2961 
   2962  if (static_cast<nsIEditActionListener*>(mTextServicesDocument) == aListener) {
   2963    mTextServicesDocument = nullptr;
   2964    return NS_OK;
   2965  }
   2966 
   2967  NS_WARNING_ASSERTION(mActionListeners.Length() != 1,
   2968                       "All nsIEditActionListeners have been removed, this "
   2969                       "editor becomes faster");
   2970  mActionListeners.RemoveElement(aListener);
   2971 
   2972  return NS_OK;
   2973 }
   2974 
   2975 NS_IMETHODIMP EditorBase::AddDocumentStateListener(
   2976    nsIDocumentStateListener* aListener) {
   2977  if (NS_WARN_IF(!aListener)) {
   2978    return NS_ERROR_INVALID_ARG;
   2979  }
   2980 
   2981  if (!mDocStateListeners.Contains(aListener)) {
   2982    mDocStateListeners.AppendElement(*aListener);
   2983  }
   2984 
   2985  return NS_OK;
   2986 }
   2987 
   2988 NS_IMETHODIMP EditorBase::RemoveDocumentStateListener(
   2989    nsIDocumentStateListener* aListener) {
   2990  if (NS_WARN_IF(!aListener)) {
   2991    return NS_ERROR_INVALID_ARG;
   2992  }
   2993 
   2994  mDocStateListeners.RemoveElement(aListener);
   2995 
   2996  return NS_OK;
   2997 }
   2998 
   2999 NS_IMETHODIMP EditorBase::ForceCompositionEnd() {
   3000  nsresult rv = CommitComposition();
   3001  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3002                       "EditorBase::CommitComposition() failed");
   3003  return rv;
   3004 }
   3005 
   3006 nsresult EditorBase::CommitComposition() {
   3007  nsPresContext* presContext = GetPresContext();
   3008  if (NS_WARN_IF(!presContext)) {
   3009    return NS_ERROR_NOT_AVAILABLE;
   3010  }
   3011 
   3012  if (!mComposition) {
   3013    return NS_OK;
   3014  }
   3015  nsresult rv =
   3016      IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, presContext);
   3017  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "IMEStateManager::NotifyIME() failed");
   3018  return rv;
   3019 }
   3020 
   3021 NS_IMETHODIMP EditorBase::GetComposing(bool* aResult) {
   3022  if (NS_WARN_IF(!aResult)) {
   3023    return NS_ERROR_INVALID_ARG;
   3024  }
   3025  *aResult = IsIMEComposing();
   3026  return NS_OK;
   3027 }
   3028 
   3029 NS_IMETHODIMP EditorBase::GetRootElement(Element** aRootElement) {
   3030  if (NS_WARN_IF(!aRootElement)) {
   3031    return NS_ERROR_INVALID_ARG;
   3032  }
   3033  *aRootElement = do_AddRef(mRootElement).take();
   3034  return NS_WARN_IF(!*aRootElement) ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   3035 }
   3036 
   3037 void EditorBase::OnStartToHandleTopLevelEditSubAction(
   3038    EditSubAction aTopLevelEditSubAction,
   3039    nsIEditor::EDirection aDirectionOfTopLevelEditSubAction, ErrorResult& aRv) {
   3040  MOZ_ASSERT(IsEditActionDataAvailable());
   3041  MOZ_ASSERT(!aRv.Failed());
   3042  mEditActionData->SetTopLevelEditSubAction(aTopLevelEditSubAction,
   3043                                            aDirectionOfTopLevelEditSubAction);
   3044 }
   3045 
   3046 nsresult EditorBase::OnEndHandlingTopLevelEditSubAction() {
   3047  MOZ_ASSERT(IsEditActionDataAvailable());
   3048  mEditActionData->SetTopLevelEditSubAction(EditSubAction::eNone, eNone);
   3049  return NS_OK;
   3050 }
   3051 
   3052 void EditorBase::DoInsertText(Text& aText, uint32_t aOffset,
   3053                              const nsAString& aStringToInsert,
   3054                              ErrorResult& aRv) {
   3055  {
   3056    AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
   3057    aRv = charDataWrapper.InsertData(aOffset, aStringToInsert);
   3058    if (MOZ_UNLIKELY(aRv.Failed())) {
   3059      NS_WARNING("AutoCharacterDataAPIWrapper::InsertData() failed");
   3060      return;
   3061    }
   3062    NS_WARNING_ASSERTION(charDataWrapper.IsExpectedResult(aStringToInsert),
   3063                         "Inserting data caused other mutations, but ignored");
   3064  }
   3065  if (IsTextEditor() && !aStringToInsert.IsEmpty()) {
   3066    aRv = MOZ_KnownLive(AsTextEditor())
   3067              ->DidInsertText(aText.TextLength(), aOffset,
   3068                              aStringToInsert.Length());
   3069    NS_WARNING_ASSERTION(!aRv.Failed(), "TextEditor::DidInsertText() failed");
   3070  }
   3071 }
   3072 
   3073 void EditorBase::DoDeleteText(Text& aText, uint32_t aOffset, uint32_t aCount,
   3074                              ErrorResult& aRv) {
   3075  if (IsTextEditor() && aCount > 0) {
   3076    AsTextEditor()->WillDeleteText(aText.TextLength(), aOffset, aCount);
   3077  }
   3078  AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
   3079  aRv = charDataWrapper.DeleteData(aOffset, aCount);
   3080  if (MOZ_UNLIKELY(aRv.Failed())) {
   3081    NS_WARNING("AutoCharacterDataAPIWrapper::DeleteData() failed");
   3082    return;
   3083  }
   3084  NS_WARNING_ASSERTION(charDataWrapper.IsExpectedResult(EmptyString()),
   3085                       "Deleting data caused other mutations, but ignored");
   3086 }
   3087 
   3088 void EditorBase::DoReplaceText(Text& aText, uint32_t aOffset, uint32_t aCount,
   3089                               const nsAString& aStringToInsert,
   3090                               ErrorResult& aRv) {
   3091  if (IsTextEditor() && aCount > 0) {
   3092    AsTextEditor()->WillDeleteText(aText.TextLength(), aOffset, aCount);
   3093  }
   3094  {
   3095    AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
   3096    aRv = charDataWrapper.ReplaceData(aOffset, aCount, aStringToInsert);
   3097    if (MOZ_UNLIKELY(aRv.Failed())) {
   3098      NS_WARNING("AutoCharacterDataAPIWrapper::ReplaceData() failed");
   3099      return;
   3100    }
   3101    NS_WARNING_ASSERTION(charDataWrapper.IsExpectedResult(aStringToInsert),
   3102                         "Replacing data caused other mutations, but ignored");
   3103  }
   3104  if (IsTextEditor() && !aStringToInsert.IsEmpty()) {
   3105    aRv = MOZ_KnownLive(AsTextEditor())
   3106              ->DidInsertText(aText.TextLength(), aOffset,
   3107                              aStringToInsert.Length());
   3108    NS_WARNING_ASSERTION(!aRv.Failed(), "TextEditor::DidInsertText() failed");
   3109  }
   3110 }
   3111 
   3112 void EditorBase::DoSetText(Text& aText, const nsAString& aStringToSet,
   3113                           ErrorResult& aRv) {
   3114  if (IsTextEditor()) {
   3115    uint32_t length = aText.TextLength();
   3116    if (length > 0) {
   3117      AsTextEditor()->WillDeleteText(length, 0, length);
   3118    }
   3119  }
   3120  {
   3121    AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
   3122    aRv = charDataWrapper.SetData(aStringToSet);
   3123    if (MOZ_UNLIKELY(aRv.Failed())) {
   3124      NS_WARNING("AutoCharacterDataAPIWrapper::SetData() failed");
   3125      return;
   3126    }
   3127    NS_WARNING_ASSERTION(charDataWrapper.IsExpectedResult(aStringToSet),
   3128                         "Setting data caused other mutations, but ignored");
   3129  }
   3130  if (IsTextEditor() && !aStringToSet.IsEmpty()) {
   3131    aRv = MOZ_KnownLive(AsTextEditor())
   3132              ->DidInsertText(aText.Length(), 0, aStringToSet.Length());
   3133    NS_WARNING_ASSERTION(!aRv.Failed(), "TextEditor::DidInsertText() failed");
   3134  }
   3135 }
   3136 
   3137 nsresult EditorBase::CloneAttributeWithTransaction(nsAtom& aAttribute,
   3138                                                   Element& aDestElement,
   3139                                                   Element& aSourceElement) {
   3140  nsAutoString attrValue;
   3141  if (aSourceElement.GetAttr(&aAttribute, attrValue)) {
   3142    nsresult rv =
   3143        SetAttributeWithTransaction(aDestElement, aAttribute, attrValue);
   3144    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3145                         "EditorBase::SetAttributeWithTransaction() failed");
   3146    return rv;
   3147  }
   3148  nsresult rv = RemoveAttributeWithTransaction(aDestElement, aAttribute);
   3149  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3150                       "EditorBase::RemoveAttributeWithTransaction() failed");
   3151  return rv;
   3152 }
   3153 
   3154 NS_IMETHODIMP EditorBase::CloneAttributes(Element* aDestElement,
   3155                                          Element* aSourceElement) {
   3156  if (NS_WARN_IF(!aDestElement) || NS_WARN_IF(!aSourceElement)) {
   3157    return NS_ERROR_INVALID_ARG;
   3158  }
   3159 
   3160  AutoEditActionDataSetter editActionData(*this, EditAction::eSetAttribute);
   3161  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   3162  if (NS_FAILED(rv)) {
   3163    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   3164                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   3165    return EditorBase::ToGenericNSResult(rv);
   3166  }
   3167 
   3168  CloneAttributesWithTransaction(*aDestElement, *aSourceElement);
   3169 
   3170  return NS_OK;
   3171 }
   3172 
   3173 void EditorBase::CloneAttributesWithTransaction(Element& aDestElement,
   3174                                                Element& aSourceElement) {
   3175  AutoPlaceholderBatch treatAsOneTransaction(
   3176      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   3177 
   3178  // Use transaction system for undo only if destination is already in the
   3179  // document
   3180  Element* rootElement = GetRoot();
   3181  if (NS_WARN_IF(!rootElement)) {
   3182    return;
   3183  }
   3184 
   3185  const OwningNonNull<Element> destElement(aDestElement);
   3186  const OwningNonNull<Element> sourceElement(aSourceElement);
   3187  bool isDestElementInBody = rootElement->Contains(destElement);
   3188 
   3189  // Clear existing attributes
   3190  AutoTArray<OwningNonNull<nsAtom>, 16> destElementAttributes;
   3191  if (const uint32_t attrCount = destElement->GetAttrCount()) {
   3192    destElementAttributes.SetCapacity(attrCount);
   3193    for (const uint32_t i : IntegerRange(attrCount)) {
   3194      if (const nsAttrName* attrName = destElement->GetUnsafeAttrNameAt(i)) {
   3195        MOZ_ASSERT(attrName->LocalName());
   3196        destElementAttributes.AppendElement(*attrName->LocalName());
   3197      }
   3198    }
   3199  }
   3200  for (const OwningNonNull<nsAtom>& attr : destElementAttributes) {
   3201    if (isDestElementInBody) {
   3202      DebugOnly<nsresult> rvIgnored =
   3203          RemoveAttributeWithTransaction(destElement, MOZ_KnownLive(*attr));
   3204      NS_WARNING_ASSERTION(
   3205          NS_SUCCEEDED(rvIgnored),
   3206          "EditorBase::RemoveAttributeWithTransaction() failed, but ignored");
   3207    } else {
   3208      AutoElementAttrAPIWrapper elementWrapper(*this, destElement);
   3209      if (NS_FAILED(elementWrapper.UnsetAttr(MOZ_KnownLive(attr), true))) {
   3210        NS_WARNING(
   3211            "AutoElementAttrAPIWrapper::UnsetAttr() failed, but ignored");
   3212      } else {
   3213        NS_WARNING_ASSERTION(
   3214            elementWrapper.IsExpectedResult(EmptyString()),
   3215            "Removing attribute caused other mutations, but ignored");
   3216      }
   3217    }
   3218  }
   3219 
   3220  // Set just the attributes that the source element has
   3221  AutoTArray<std::pair<OwningNonNull<nsAtom>, nsString>, 16>
   3222      sourceElementAttributes;
   3223  if (const uint32_t attrCount = sourceElement->GetAttrCount()) {
   3224    sourceElementAttributes.SetCapacity(attrCount);
   3225    for (const uint32_t i : IntegerRange(attrCount)) {
   3226      const BorrowedAttrInfo attrInfo = sourceElement->GetAttrInfoAt(i);
   3227      if (const nsAttrName* attrName = attrInfo.mName) {
   3228        MOZ_ASSERT(attrName->LocalName());
   3229        MOZ_ASSERT(attrInfo.mValue);
   3230        nsString value;
   3231        attrInfo.mValue->ToString(value);
   3232        sourceElementAttributes.AppendElement(std::make_pair(
   3233            OwningNonNull<nsAtom>(*attrName->LocalName()), std::move(value)));
   3234      }
   3235    }
   3236  }
   3237  for (const auto& attr : sourceElementAttributes) {
   3238    if (isDestElementInBody) {
   3239      DebugOnly<nsresult> rvIgnored = SetAttributeOrEquivalent(
   3240          destElement, MOZ_KnownLive(attr.first), attr.second, false);
   3241      NS_WARNING_ASSERTION(
   3242          NS_SUCCEEDED(rvIgnored),
   3243          "EditorBase::SetAttributeOrEquivalent() failed, but ignored");
   3244    } else {
   3245      // The element is not inserted in the document yet, we don't want to put
   3246      // a transaction on the UndoStack
   3247      DebugOnly<nsresult> rvIgnored = SetAttributeOrEquivalent(
   3248          destElement, MOZ_KnownLive(attr.first), attr.second, true);
   3249      NS_WARNING_ASSERTION(
   3250          NS_SUCCEEDED(rvIgnored),
   3251          "EditorBase::SetAttributeOrEquivalent() failed, but ignored");
   3252    }
   3253  }
   3254 }
   3255 
   3256 nsresult EditorBase::ScrollSelectionFocusIntoView() const {
   3257  nsISelectionController* selectionController = GetSelectionController();
   3258  if (!selectionController) {
   3259    return NS_OK;
   3260  }
   3261 
   3262  DebugOnly<nsresult> rvIgnored = selectionController->ScrollSelectionIntoView(
   3263      SelectionType::eNormal, nsISelectionController::SELECTION_FOCUS_REGION,
   3264      ScrollAxis(), ScrollAxis(), ScrollFlags::ScrollOverflowHidden);
   3265  NS_WARNING_ASSERTION(
   3266      NS_SUCCEEDED(rvIgnored),
   3267      "nsISelectionController::ScrollSelectionIntoView() failed, but ignored");
   3268  return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
   3269 }
   3270 
   3271 EditorDOMPoint EditorBase::ComputePointToInsertText(
   3272    const EditorDOMPoint& aPoint, InsertTextTo aInsertTextTo) const {
   3273  if (aInsertTextTo == InsertTextTo::SpecifiedPoint) {
   3274    return aPoint;
   3275  }
   3276 
   3277  if (IsTextEditor()) {
   3278    // In some cases, the node may be the anonymous div element or a padding
   3279    // <br> element for empty last line.  Let's try to look for better insertion
   3280    // point in the nearest text node if there is.
   3281    return AsTextEditor()->FindBetterInsertionPoint(aPoint);
   3282  }
   3283  auto pointToInsert =
   3284      aPoint.GetPointInTextNodeIfPointingAroundTextNode<EditorDOMPoint>();
   3285  // If the candidate point is in a Text node which has only a preformatted
   3286  // linefeed, we should not insert text into the node because it may have
   3287  // been inserted by us and that's compatible behavior with Chrome.
   3288  if (pointToInsert.IsInTextNode() &&
   3289      HTMLEditUtils::TextHasOnlyOnePreformattedLinefeed(
   3290          *pointToInsert.ContainerAs<Text>())) {
   3291    if (pointToInsert.IsStartOfContainer()) {
   3292      if (Text* const previousText = Text::FromNodeOrNull(
   3293              pointToInsert.ContainerAs<Text>()->GetPreviousSibling())) {
   3294        pointToInsert = EditorDOMPoint::AtEndOf(*previousText);
   3295      } else {
   3296        pointToInsert = pointToInsert.ParentPoint();
   3297      }
   3298    } else {
   3299      MOZ_ASSERT(pointToInsert.IsEndOfContainer());
   3300      if (Text* const nextText = Text::FromNodeOrNull(
   3301              pointToInsert.ContainerAs<Text>()->GetNextSibling())) {
   3302        pointToInsert = EditorDOMPoint(nextText, 0u);
   3303      } else {
   3304        pointToInsert = pointToInsert.AfterContainer();
   3305      }
   3306    }
   3307  }
   3308  if (aInsertTextTo == InsertTextTo::AlwaysCreateNewTextNode) {
   3309    NS_WARNING_ASSERTION(!pointToInsert.IsInTextNode() ||
   3310                             pointToInsert.IsStartOfContainer() ||
   3311                             pointToInsert.IsEndOfContainer(),
   3312                         "aPointToInsert is \"AlwaysCreateNewTextNode\", but "
   3313                         "specified point middle of a `Text`");
   3314    if (!pointToInsert.IsInTextNode()) {
   3315      return pointToInsert;
   3316    }
   3317    return pointToInsert.IsStartOfContainer()
   3318               ? EditorDOMPoint(pointToInsert.ContainerAs<Text>())
   3319               : (pointToInsert.IsEndOfContainer()
   3320                      ? EditorDOMPoint::After(
   3321                            *pointToInsert.ContainerAs<Text>())
   3322                      : pointToInsert);
   3323  }
   3324  if (aInsertTextTo == InsertTextTo::ExistingTextNodeIfAvailableAndNotStart) {
   3325    return !(pointToInsert.IsInTextNode() && pointToInsert.IsStartOfContainer())
   3326               ? pointToInsert
   3327               : EditorDOMPoint(pointToInsert.ContainerAs<Text>());
   3328  }
   3329  return pointToInsert;
   3330 }
   3331 
   3332 Result<InsertTextResult, nsresult> EditorBase::InsertTextWithTransaction(
   3333    const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert,
   3334    InsertTextTo aInsertTextTo) {
   3335  MOZ_ASSERT_IF(IsTextEditor(),
   3336                aInsertTextTo == InsertTextTo::ExistingTextNodeIfAvailable);
   3337 
   3338  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
   3339    return Err(NS_ERROR_INVALID_ARG);
   3340  }
   3341 
   3342  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   3343 
   3344  if (!ShouldHandleIMEComposition() && aStringToInsert.IsEmpty()) {
   3345    return InsertTextResult();
   3346  }
   3347 
   3348  EditorDOMPoint pointToInsert =
   3349      ComputePointToInsertText(aPointToInsert, aInsertTextTo);
   3350  if (ShouldHandleIMEComposition()) {
   3351    if (!pointToInsert.IsInTextNode()) {
   3352      // create a text node
   3353      RefPtr<nsTextNode> newTextNode = CreateTextNode(u""_ns);
   3354      if (NS_WARN_IF(!newTextNode)) {
   3355        return Err(NS_ERROR_FAILURE);
   3356      }
   3357      // then we insert it into the dom tree
   3358      Result<CreateTextResult, nsresult> insertTextNodeResult =
   3359          InsertNodeWithTransaction<Text>(*newTextNode, pointToInsert);
   3360      if (MOZ_UNLIKELY(insertTextNodeResult.isErr())) {
   3361        NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   3362        return insertTextNodeResult.propagateErr();
   3363      }
   3364      insertTextNodeResult.unwrap().IgnoreCaretPointSuggestion();
   3365      pointToInsert.Set(newTextNode, 0u);
   3366    }
   3367    Result<InsertTextResult, nsresult> insertTextResult =
   3368        InsertTextIntoTextNodeWithTransaction(aStringToInsert,
   3369                                              pointToInsert.AsInText());
   3370    NS_WARNING_ASSERTION(
   3371        insertTextResult.isOk(),
   3372        "EditorBase::InsertTextIntoTextNodeWithTransaction() failed");
   3373    return insertTextResult;
   3374  }
   3375 
   3376  if (pointToInsert.IsInTextNode()) {
   3377    // we are inserting text into an existing text node.
   3378    Result<InsertTextResult, nsresult> insertTextResult =
   3379        InsertTextIntoTextNodeWithTransaction(aStringToInsert,
   3380                                              pointToInsert.AsInText());
   3381    NS_WARNING_ASSERTION(
   3382        insertTextResult.isOk(),
   3383        "EditorBase::InsertTextIntoTextNodeWithTransaction() failed");
   3384    return insertTextResult;
   3385  }
   3386 
   3387  // we are inserting text into a non-text node.  first we have to create a
   3388  // textnode (this also populates it with the text)
   3389  RefPtr<nsTextNode> newTextNode = CreateTextNode(aStringToInsert);
   3390  if (NS_WARN_IF(!newTextNode)) {
   3391    return Err(NS_ERROR_FAILURE);
   3392  }
   3393  // then we insert it into the dom tree
   3394  Result<CreateTextResult, nsresult> insertTextNodeResult =
   3395      InsertNodeWithTransaction<Text>(*newTextNode, pointToInsert);
   3396  if (MOZ_UNLIKELY(insertTextNodeResult.isErr())) {
   3397    NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   3398    return Err(insertTextNodeResult.unwrapErr());
   3399  }
   3400  insertTextNodeResult.unwrap().IgnoreCaretPointSuggestion();
   3401  if (NS_WARN_IF(!newTextNode->IsInComposedDoc())) {
   3402    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   3403  }
   3404  return InsertTextResult(EditorDOMPoint::AtEndOf(*newTextNode),
   3405                          EditorDOMPoint::AtEndOf(*newTextNode));
   3406 }
   3407 
   3408 std::tuple<EditorDOMPointInText, EditorDOMPointInText>
   3409 EditorBase::ComputeInsertedRange(const EditorDOMPointInText& aInsertedPoint,
   3410                                 const nsAString& aInsertedString) const {
   3411  MOZ_ASSERT(aInsertedPoint.IsSet());
   3412 
   3413  EditorDOMPointInText endOfInsertion(
   3414      aInsertedPoint.ContainerAs<Text>(),
   3415      aInsertedPoint.Offset() + aInsertedString.Length());
   3416  return {aInsertedPoint, endOfInsertion};
   3417 }
   3418 
   3419 Result<InsertTextResult, nsresult>
   3420 EditorBase::InsertTextIntoTextNodeWithTransaction(
   3421    const nsAString& aStringToInsert,
   3422    const EditorDOMPointInText& aPointToInsert) {
   3423  MOZ_ASSERT(IsEditActionDataAvailable());
   3424  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   3425 
   3426  RefPtr<EditTransactionBase> transaction;
   3427  bool isIMETransaction = false;
   3428  if (ShouldHandleIMEComposition()) {
   3429    transaction =
   3430        CompositionTransaction::Create(*this, aStringToInsert, aPointToInsert);
   3431    isIMETransaction = true;
   3432  } else {
   3433    transaction =
   3434        InsertTextTransaction::Create(*this, aStringToInsert, aPointToInsert);
   3435  }
   3436 
   3437  // XXX We may not need these view batches anymore.  This is handled at a
   3438  // higher level now I believe.
   3439  BeginUpdateViewBatch(__FUNCTION__);
   3440  nsresult rv = DoTransactionInternal(transaction);
   3441  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3442                       "EditorBase::DoTransactionInternal() failed");
   3443  EndUpdateViewBatch(__FUNCTION__);
   3444 
   3445  // Don't check whether we've been destroyed here because we need to notify
   3446  // listeners and observers below even if we've already destroyed.
   3447 
   3448  auto pointToInsert = [&]() -> EditorDOMPointInText {
   3449    if (!isIMETransaction) {
   3450      return aPointToInsert;
   3451    }
   3452    if (NS_WARN_IF(!mComposition->GetContainerTextNode())) {
   3453      return aPointToInsert;
   3454    }
   3455    return EditorDOMPointInText(
   3456        mComposition->GetContainerTextNode(),
   3457        std::min(mComposition->XPOffsetInTextNode(),
   3458                 mComposition->GetContainerTextNode()->TextDataLength()));
   3459  }();
   3460 
   3461  EditorDOMPoint endOfInsertedText(
   3462      pointToInsert.ContainerAs<Text>(),
   3463      pointToInsert.Offset() + aStringToInsert.Length());
   3464 
   3465  if (IsHTMLEditor()) {
   3466    auto [begin, end] = ComputeInsertedRange(pointToInsert, aStringToInsert);
   3467    if (begin.IsSet() && end.IsSet()) {
   3468      TopLevelEditSubActionDataRef().DidInsertText(
   3469          *this, begin.RefOrTo<EditorRawDOMPoint>(),
   3470          end.RefOrTo<EditorRawDOMPoint>());
   3471    }
   3472    if (isIMETransaction) {
   3473      // Let's mark the text node as "modified frequently" if it interact with
   3474      // IME since non-ASCII character may be inserted into it in most cases.
   3475      pointToInsert.ContainerAs<Text>()->MarkAsMaybeModifiedFrequently();
   3476    }
   3477    // XXX Should we update endOfInsertedText here?
   3478  }
   3479 
   3480  // let listeners know what happened
   3481  if (!mActionListeners.IsEmpty()) {
   3482    for (auto& listener : mActionListeners.Clone()) {
   3483      // TODO: might need adaptation because of mutation event listeners called
   3484      // during `DoTransactionInternal`.
   3485      DebugOnly<nsresult> rvIgnored = listener->DidInsertText(
   3486          pointToInsert.ContainerAs<Text>(),
   3487          static_cast<int32_t>(pointToInsert.Offset()), aStringToInsert, rv);
   3488      NS_WARNING_ASSERTION(
   3489          NS_SUCCEEDED(rvIgnored),
   3490          "nsIEditActionListener::DidInsertText() failed, but ignored");
   3491    }
   3492  }
   3493 
   3494  // Added some cruft here for bug 43366.  Layout was crashing because we left
   3495  // an empty text node lying around in the document.  So I delete empty text
   3496  // nodes caused by IME.  I have to mark the IME transaction as "fixed", which
   3497  // means that furure IME txns won't merge with it.  This is because we don't
   3498  // want future IME txns trying to put their text into a node that is no
   3499  // longer in the document.  This does not break undo/redo, because all these
   3500  // txns are wrapped in a parent PlaceHolder txn, and placeholder txns are
   3501  // already savvy to having multiple ime txns inside them.
   3502 
   3503  // Delete empty IME text node if there is one
   3504  if (IsHTMLEditor() && isIMETransaction && mComposition) {
   3505    RefPtr<Text> textNode = mComposition->GetContainerTextNode();
   3506    if (textNode && !textNode->Length()) {
   3507      endOfInsertedText.Set(textNode);
   3508      AutoEditorDOMPointChildInvalidator lockIndex(endOfInsertedText);
   3509      rv = DeleteNodeWithTransaction(*textNode);
   3510      if (MOZ_LIKELY(!textNode->IsInComposedDoc())) {
   3511        mComposition->OnTextNodeRemoved();
   3512      }
   3513      static_cast<CompositionTransaction*>(transaction.get())->MarkFixed();
   3514      if (NS_FAILED(rv)) {
   3515        NS_WARNING("EditorBase::DeleteNodeTransaction() failed");
   3516        return Err(rv);
   3517      }
   3518      if (NS_WARN_IF(!endOfInsertedText.IsSetAndValidInComposedDoc())) {
   3519        return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   3520      }
   3521    }
   3522  }
   3523 
   3524  if (NS_WARN_IF(Destroyed())) {
   3525    return Err(NS_ERROR_EDITOR_DESTROYED);
   3526  }
   3527 
   3528  InsertTextTransaction* const insertTextTransaction =
   3529      transaction->GetAsInsertTextTransaction();
   3530  return insertTextTransaction
   3531             ? InsertTextResult(std::move(endOfInsertedText),
   3532                                insertTextTransaction
   3533                                    ->SuggestPointToPutCaret<EditorDOMPoint>())
   3534             : InsertTextResult(std::move(endOfInsertedText));
   3535 }
   3536 
   3537 nsresult EditorBase::NotifyDocumentListeners(
   3538    TDocumentListenerNotification aNotificationType) {
   3539  switch (aNotificationType) {
   3540    case eDocumentCreated:
   3541      if (IsTextEditor()) {
   3542        return NS_OK;
   3543      }
   3544      if (RefPtr<ComposerCommandsUpdater> composerCommandsUpdate =
   3545              AsHTMLEditor()->mComposerCommandsUpdater) {
   3546        composerCommandsUpdate->OnHTMLEditorCreated();
   3547      }
   3548      return NS_OK;
   3549 
   3550    case eDocumentToBeDestroyed: {
   3551      RefPtr<ComposerCommandsUpdater> composerCommandsUpdate =
   3552          IsHTMLEditor() ? AsHTMLEditor()->mComposerCommandsUpdater : nullptr;
   3553      if (!mDocStateListeners.Length() && !composerCommandsUpdate) {
   3554        return NS_OK;
   3555      }
   3556      // Needs to store all listeners before notifying ComposerCommandsUpdate
   3557      // since notifying it might change mDocStateListeners.
   3558      const AutoDocumentStateListenerArray listeners(
   3559          mDocStateListeners.Clone());
   3560      if (composerCommandsUpdate) {
   3561        composerCommandsUpdate->OnBeforeHTMLEditorDestroyed();
   3562      }
   3563      for (auto& listener : listeners) {
   3564        // MOZ_KnownLive because 'listeners' is guaranteed to
   3565        // keep it alive.
   3566        //
   3567        // This can go away once
   3568        // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
   3569        nsresult rv = MOZ_KnownLive(listener)->NotifyDocumentWillBeDestroyed();
   3570        if (NS_FAILED(rv)) {
   3571          NS_WARNING(
   3572              "nsIDocumentStateListener::NotifyDocumentWillBeDestroyed() "
   3573              "failed");
   3574          return rv;
   3575        }
   3576      }
   3577      return NS_OK;
   3578    }
   3579    case eDocumentStateChanged: {
   3580      bool docIsDirty;
   3581      nsresult rv = GetDocumentModified(&docIsDirty);
   3582      if (NS_FAILED(rv)) {
   3583        NS_WARNING("EditorBase::GetDocumentModified() failed");
   3584        return rv;
   3585      }
   3586 
   3587      if (static_cast<int8_t>(docIsDirty) == mDocDirtyState) {
   3588        return NS_OK;
   3589      }
   3590 
   3591      mDocDirtyState = docIsDirty;
   3592 
   3593      RefPtr<ComposerCommandsUpdater> composerCommandsUpdate =
   3594          IsHTMLEditor() ? AsHTMLEditor()->mComposerCommandsUpdater : nullptr;
   3595      if (!mDocStateListeners.Length() && !composerCommandsUpdate) {
   3596        return NS_OK;
   3597      }
   3598      // Needs to store all listeners before notifying ComposerCommandsUpdate
   3599      // since notifying it might change mDocStateListeners.
   3600      const AutoDocumentStateListenerArray listeners(
   3601          mDocStateListeners.Clone());
   3602      if (composerCommandsUpdate) {
   3603        composerCommandsUpdate->OnHTMLEditorDirtyStateChanged(mDocDirtyState);
   3604      }
   3605      for (auto& listener : listeners) {
   3606        // MOZ_KnownLive because 'listeners' is guaranteed to
   3607        // keep it alive.
   3608        //
   3609        // This can go away once
   3610        // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
   3611        nsresult rv =
   3612            MOZ_KnownLive(listener)->NotifyDocumentStateChanged(mDocDirtyState);
   3613        if (NS_FAILED(rv)) {
   3614          NS_WARNING(
   3615              "nsIDocumentStateListener::NotifyDocumentStateChanged() failed");
   3616          return rv;
   3617        }
   3618      }
   3619      return NS_OK;
   3620    }
   3621    default:
   3622      MOZ_ASSERT_UNREACHABLE("Unknown notification");
   3623      return NS_ERROR_FAILURE;
   3624  }
   3625 }
   3626 
   3627 nsresult EditorBase::SetTextNodeWithoutTransaction(const nsAString& aString,
   3628                                                   Text& aTextNode) {
   3629  MOZ_ASSERT(IsEditActionDataAvailable());
   3630  MOZ_ASSERT(IsTextEditor());
   3631  MOZ_ASSERT(!IsUndoRedoEnabled());
   3632 
   3633  const uint32_t length = aTextNode.Length();
   3634 
   3635  // Let listeners know what's up
   3636  if (!mActionListeners.IsEmpty() && length) {
   3637    for (auto& listener : mActionListeners.Clone()) {
   3638      DebugOnly<nsresult> rvIgnored =
   3639          listener->WillDeleteText(MOZ_KnownLive(&aTextNode), 0, length);
   3640      if (NS_WARN_IF(Destroyed())) {
   3641        NS_WARNING(
   3642            "nsIEditActionListener::WillDeleteText() failed, but ignored");
   3643        return NS_ERROR_EDITOR_DESTROYED;
   3644      }
   3645    }
   3646  }
   3647 
   3648  // We don't support undo here, so we don't really need all of the transaction
   3649  // machinery, therefore we can run our transaction directly, breaking all of
   3650  // the rules!
   3651  IgnoredErrorResult error;
   3652  DoSetText(aTextNode, aString, error);
   3653  if (MOZ_UNLIKELY(error.Failed())) {
   3654    NS_WARNING("EditorBase::DoSetText() failed");
   3655    return error.StealNSResult();
   3656  }
   3657 
   3658  CollapseSelectionTo(EditorRawDOMPoint(&aTextNode, aString.Length()), error);
   3659  if (MOZ_UNLIKELY(error.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   3660    NS_WARNING("EditorBase::CollapseSelection() caused destroying the editor");
   3661    return NS_ERROR_EDITOR_DESTROYED;
   3662  }
   3663  NS_ASSERTION(!error.Failed(),
   3664               "EditorBase::CollapseSelectionTo() failed, but ignored");
   3665 
   3666  RangeUpdaterRef().SelAdjReplaceText(aTextNode, 0, length, aString.Length());
   3667 
   3668  // Let listeners know what happened
   3669  if (!mActionListeners.IsEmpty() && !aString.IsEmpty()) {
   3670    for (auto& listener : mActionListeners.Clone()) {
   3671      DebugOnly<nsresult> rvIgnored =
   3672          listener->DidInsertText(&aTextNode, 0, aString, NS_OK);
   3673      if (NS_WARN_IF(Destroyed())) {
   3674        return NS_ERROR_EDITOR_DESTROYED;
   3675      }
   3676      NS_WARNING_ASSERTION(
   3677          NS_SUCCEEDED(rvIgnored),
   3678          "nsIEditActionListener::DidInsertText() failed, but ignored");
   3679    }
   3680  }
   3681 
   3682  return NS_OK;
   3683 }
   3684 
   3685 Result<CaretPoint, nsresult> EditorBase::DeleteTextWithTransaction(
   3686    Text& aTextNode, uint32_t aOffset, uint32_t aLength) {
   3687  MOZ_ASSERT(IsEditActionDataAvailable());
   3688 
   3689  RefPtr<DeleteTextTransaction> transaction =
   3690      DeleteTextTransaction::MaybeCreate(*this, aTextNode, aOffset, aLength);
   3691  if (MOZ_UNLIKELY(!transaction)) {
   3692    NS_WARNING("DeleteTextTransaction::MaybeCreate() failed");
   3693    return Err(NS_ERROR_FAILURE);
   3694  }
   3695 
   3696  IgnoredErrorResult ignoredError;
   3697  AutoEditSubActionNotifier startToHandleEditSubAction(
   3698      *this, EditSubAction::eDeleteText, nsIEditor::ePrevious, ignoredError);
   3699  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   3700    return Err(ignoredError.StealNSResult());
   3701  }
   3702  NS_WARNING_ASSERTION(
   3703      !ignoredError.Failed(),
   3704      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   3705 
   3706  // Let listeners know what's up
   3707  if (!mActionListeners.IsEmpty()) {
   3708    for (auto& listener : mActionListeners.Clone()) {
   3709      DebugOnly<nsresult> rvIgnored =
   3710          listener->WillDeleteText(&aTextNode, aOffset, aLength);
   3711      NS_WARNING_ASSERTION(
   3712          NS_SUCCEEDED(rvIgnored),
   3713          "nsIEditActionListener::WillDeleteText() failed, but ignored");
   3714    }
   3715  }
   3716 
   3717  nsresult rv = DoTransactionInternal(transaction);
   3718  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3719                       "EditorBase::DoTransactionInternal() failed");
   3720 
   3721  if (IsHTMLEditor()) {
   3722    TopLevelEditSubActionDataRef().DidDeleteText(
   3723        *this, EditorRawDOMPoint(&aTextNode, aOffset));
   3724  }
   3725 
   3726  if (NS_WARN_IF(Destroyed())) {
   3727    return Err(NS_ERROR_EDITOR_DESTROYED);
   3728  }
   3729  if (NS_FAILED(rv)) {
   3730    return Err(rv);
   3731  }
   3732 
   3733  return CaretPoint(transaction->SuggestPointToPutCaret());
   3734 }
   3735 
   3736 bool EditorBase::IsRoot(const nsINode* inNode) const {
   3737  if (NS_WARN_IF(!inNode)) {
   3738    return false;
   3739  }
   3740  nsINode* rootNode = GetRoot();
   3741  return inNode == rootNode;
   3742 }
   3743 
   3744 bool EditorBase::IsDescendantOfRoot(const nsINode* inNode) const {
   3745  if (NS_WARN_IF(!inNode)) {
   3746    return false;
   3747  }
   3748  nsIContent* root = GetRoot();
   3749  if (NS_WARN_IF(!root)) {
   3750    return false;
   3751  }
   3752 
   3753  return inNode->IsInclusiveDescendantOf(root);
   3754 }
   3755 
   3756 NS_IMETHODIMP EditorBase::IncrementModificationCount(int32_t inNumMods) {
   3757  uint32_t oldModCount = mModCount;
   3758 
   3759  mModCount += inNumMods;
   3760 
   3761  if ((!oldModCount && mModCount) || (oldModCount && !mModCount)) {
   3762    DebugOnly<nsresult> rvIgnored =
   3763        NotifyDocumentListeners(eDocumentStateChanged);
   3764    NS_WARNING_ASSERTION(
   3765        NS_SUCCEEDED(rvIgnored),
   3766        "EditorBase::NotifyDocumentListeners() failed, but ignored");
   3767  }
   3768  return NS_OK;
   3769 }
   3770 
   3771 NS_IMETHODIMP EditorBase::GetModificationCount(int32_t* aOutModCount) {
   3772  if (NS_WARN_IF(!aOutModCount)) {
   3773    return NS_ERROR_INVALID_ARG;
   3774  }
   3775  *aOutModCount = mModCount;
   3776  return NS_OK;
   3777 }
   3778 
   3779 NS_IMETHODIMP EditorBase::ResetModificationCount() {
   3780  bool doNotify = (mModCount != 0);
   3781 
   3782  mModCount = 0;
   3783 
   3784  if (!doNotify) {
   3785    return NS_OK;
   3786  }
   3787 
   3788  DebugOnly<nsresult> rvIgnored =
   3789      NotifyDocumentListeners(eDocumentStateChanged);
   3790  NS_WARNING_ASSERTION(
   3791      NS_SUCCEEDED(rvIgnored),
   3792      "EditorBase::NotifyDocumentListeners() failed, but ignored");
   3793  return NS_OK;
   3794 }
   3795 
   3796 template <typename EditorDOMPointType>
   3797 EditorDOMPointType EditorBase::GetFirstSelectionStartPoint() const {
   3798  MOZ_ASSERT(IsEditActionDataAvailable());
   3799  if (MOZ_UNLIKELY(!SelectionRef().RangeCount())) {
   3800    return EditorDOMPointType();
   3801  }
   3802 
   3803  const nsRange* range = SelectionRef().GetRangeAt(0);
   3804  if (MOZ_UNLIKELY(NS_WARN_IF(!range) || NS_WARN_IF(!range->IsPositioned()))) {
   3805    return EditorDOMPointType();
   3806  }
   3807 
   3808  return EditorDOMPointType(range->StartRef());
   3809 }
   3810 
   3811 template <typename EditorDOMPointType>
   3812 EditorDOMPointType EditorBase::GetFirstSelectionEndPoint() const {
   3813  MOZ_ASSERT(IsEditActionDataAvailable());
   3814  if (MOZ_UNLIKELY(!SelectionRef().RangeCount())) {
   3815    return EditorDOMPointType();
   3816  }
   3817 
   3818  const nsRange* range = SelectionRef().GetRangeAt(0);
   3819  if (MOZ_UNLIKELY(NS_WARN_IF(!range) || NS_WARN_IF(!range->IsPositioned()))) {
   3820    return EditorDOMPointType();
   3821  }
   3822 
   3823  return EditorDOMPointType(range->EndRef());
   3824 }
   3825 
   3826 // static
   3827 nsresult EditorBase::GetEndChildNode(const Selection& aSelection,
   3828                                     nsIContent** aEndNode) {
   3829  MOZ_ASSERT(aEndNode);
   3830 
   3831  *aEndNode = nullptr;
   3832 
   3833  if (NS_WARN_IF(!aSelection.RangeCount())) {
   3834    return NS_ERROR_FAILURE;
   3835  }
   3836 
   3837  const nsRange* range = aSelection.GetRangeAt(0);
   3838  if (NS_WARN_IF(!range)) {
   3839    return NS_ERROR_FAILURE;
   3840  }
   3841 
   3842  if (NS_WARN_IF(!range->IsPositioned())) {
   3843    return NS_ERROR_FAILURE;
   3844  }
   3845 
   3846  NS_IF_ADDREF(*aEndNode = range->GetChildAtEndOffset());
   3847  return NS_OK;
   3848 }
   3849 
   3850 nsresult EditorBase::EnsurePaddingBRElementInMultilineEditor() {
   3851  MOZ_ASSERT(IsEditActionDataAvailable());
   3852  MOZ_ASSERT(IsTextEditor() || AsHTMLEditor()->IsPlaintextMailComposer());
   3853  MOZ_ASSERT(!IsSingleLineEditor());
   3854 
   3855  Element* anonymousDivOrBodyElement = GetRoot();
   3856  if (NS_WARN_IF(!anonymousDivOrBodyElement)) {
   3857    return NS_ERROR_FAILURE;
   3858  }
   3859 
   3860  // Assuming EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() has been
   3861  // called first.
   3862  // XXX This assumption is wrong.  This method may be called alone.  Actually,
   3863  //     we see this warning in mochitest log.  So, we should fix this bug
   3864  //     later.
   3865  if (NS_WARN_IF(!anonymousDivOrBodyElement->GetLastChild())) {
   3866    return NS_ERROR_FAILURE;
   3867  }
   3868 
   3869  RefPtr<HTMLBRElement> brElement =
   3870      HTMLBRElement::FromNode(anonymousDivOrBodyElement->GetLastChild());
   3871  if (!brElement) {
   3872    // TODO: Remove AutoTransactionsConserveSelection here.  It's not necessary
   3873    //       in normal cases.  However, it may be required for nested edit
   3874    //       actions which may be caused by legacy mutation event listeners or
   3875    //       chrome script.
   3876    AutoTransactionsConserveSelection dontChangeMySelection(*this);
   3877    EditorDOMPoint endOfAnonymousDiv(
   3878        EditorDOMPoint::AtEndOf(*anonymousDivOrBodyElement));
   3879    Result<CreateElementResult, nsresult> insertPaddingBRElementResult =
   3880        InsertPaddingBRElementForEmptyLastLineWithTransaction(
   3881            endOfAnonymousDiv);
   3882    if (MOZ_UNLIKELY(insertPaddingBRElementResult.isErr())) {
   3883      NS_WARNING(
   3884          "EditorBase::InsertPaddingBRElementForEmptyLastLineWithTransaction() "
   3885          "failed");
   3886      return insertPaddingBRElementResult.unwrapErr();
   3887    }
   3888    insertPaddingBRElementResult.inspect().IgnoreCaretPointSuggestion();
   3889    return NS_OK;
   3890  }
   3891 
   3892  // Check to see if the trailing BR is a former padding <br> element for empty
   3893  // editor - this will have stuck around if we previously morphed a trailing
   3894  // node into a padding <br> element.
   3895  if (!brElement->IsPaddingForEmptyEditor()) {
   3896    return NS_OK;
   3897  }
   3898 
   3899  // Morph it back to a padding <br> element for empty last line.
   3900  nsresult rv =
   3901      UpdateBRElementType(*brElement, BRElementType::PaddingForEmptyLastLine);
   3902  if (NS_FAILED(rv)) {
   3903    NS_WARNING("EditorBase::UpdateBRElementType() failed");
   3904    return rv;
   3905  }
   3906 
   3907  return NS_OK;
   3908 }
   3909 
   3910 void EditorBase::BeginUpdateViewBatch(const char* aRequesterFuncName) {
   3911  MOZ_ASSERT(IsEditActionDataAvailable());
   3912  MOZ_ASSERT(mUpdateCount >= 0, "bad state");
   3913 
   3914  if (!mUpdateCount) {
   3915    // Turn off selection updates and notifications.
   3916    SelectionRef().StartBatchChanges(aRequesterFuncName);
   3917  }
   3918 
   3919  mUpdateCount++;
   3920 }
   3921 
   3922 void EditorBase::EndUpdateViewBatch(const char* aRequesterFuncName) {
   3923  MOZ_ASSERT(IsEditActionDataAvailable());
   3924  MOZ_ASSERT(mUpdateCount > 0, "bad state");
   3925 
   3926  if (NS_WARN_IF(mUpdateCount <= 0)) {
   3927    mUpdateCount = 0;
   3928    return;
   3929  }
   3930 
   3931  if (--mUpdateCount) {
   3932    return;
   3933  }
   3934 
   3935  // Turn selection updating and notifications back on.
   3936  SelectionRef().EndBatchChanges(aRequesterFuncName);
   3937 }
   3938 
   3939 TextComposition* EditorBase::GetComposition() const { return mComposition; }
   3940 
   3941 template <typename EditorDOMPointType>
   3942 EditorDOMPointType EditorBase::GetFirstIMESelectionStartPoint() const {
   3943  return mComposition
   3944             ? EditorDOMPointType(mComposition->FirstIMESelectionStartRef())
   3945             : EditorDOMPointType();
   3946 }
   3947 
   3948 template <typename EditorDOMPointType>
   3949 EditorDOMPointType EditorBase::GetLastIMESelectionEndPoint() const {
   3950  return mComposition
   3951             ? EditorDOMPointType(mComposition->LastIMESelectionEndRef())
   3952             : EditorDOMPointType();
   3953 }
   3954 
   3955 bool EditorBase::IsIMEComposing() const {
   3956  return mComposition && mComposition->IsComposing();
   3957 }
   3958 
   3959 bool EditorBase::ShouldHandleIMEComposition() const {
   3960  // When the editor is being reframed, the old value may be restored with
   3961  // InsertText().  In this time, the text should be inserted as not a part
   3962  // of the composition.
   3963  return mComposition && mDidPostCreate;
   3964 }
   3965 
   3966 bool EditorBase::EnsureComposition(WidgetCompositionEvent& aCompositionEvent) {
   3967  if (mComposition) {
   3968    return true;
   3969  }
   3970  // The compositionstart event must cause creating new TextComposition
   3971  // instance at being dispatched by IMEStateManager.
   3972  mComposition = IMEStateManager::GetTextCompositionFor(&aCompositionEvent);
   3973  if (!mComposition) {
   3974    // However, TextComposition may be committed before the composition
   3975    // event comes here.
   3976    return false;
   3977  }
   3978  mComposition->StartHandlingComposition(this);
   3979  return true;
   3980 }
   3981 
   3982 nsresult EditorBase::OnCompositionStart(
   3983    WidgetCompositionEvent& aCompositionStartEvent) {
   3984  MOZ_LOG(gTextInputLog, LogLevel::Info,
   3985          ("%p %s::OnCompositionStart(aCompositionStartEvent={ mData=\"%s\"}), "
   3986           "mComposition=%p",
   3987           this, mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   3988           NS_ConvertUTF16toUTF8(aCompositionStartEvent.mData).get(),
   3989           mComposition.get()));
   3990 
   3991  if (mComposition) {
   3992    NS_WARNING("There was a composition at receiving compositionstart event");
   3993    return NS_OK;
   3994  }
   3995 
   3996  // "beforeinput" event shouldn't be fired before "compositionstart".
   3997  AutoEditActionDataSetter editActionData(*this, EditAction::eStartComposition);
   3998  if (NS_WARN_IF(!editActionData.CanHandle())) {
   3999    return NS_ERROR_NOT_INITIALIZED;
   4000  }
   4001 
   4002  EnsureComposition(aCompositionStartEvent);
   4003  NS_WARNING_ASSERTION(mComposition, "Failed to get TextComposition instance?");
   4004  return NS_OK;
   4005 }
   4006 
   4007 nsresult EditorBase::OnCompositionChange(
   4008    WidgetCompositionEvent& aCompositionChangeEvent) {
   4009  MOZ_ASSERT(aCompositionChangeEvent.mMessage == eCompositionChange,
   4010             "The event should be eCompositionChange");
   4011 
   4012  MOZ_LOG(
   4013      gTextInputLog, LogLevel::Info,
   4014      ("%p %s::OnCompositionChange(aCompositionChangeEvent={ mData=\"%s\", "
   4015       "IsFollowedByCompositionEnd()=%s }), mComposition=%p",
   4016       this, mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   4017       NS_ConvertUTF16toUTF8(aCompositionChangeEvent.mData).get(),
   4018       aCompositionChangeEvent.IsFollowedByCompositionEnd() ? "true" : "false",
   4019       mComposition.get()));
   4020 
   4021  if (!mComposition) {
   4022    NS_WARNING(
   4023        "There is no composition, but receiving compositionchange event");
   4024    return NS_ERROR_FAILURE;
   4025  }
   4026 
   4027  AutoEditActionDataSetter editActionData(
   4028      *this,
   4029      // We need to distinguish whether the composition change is followed by
   4030      // compositionend or not (i.e., wether IME has already ended the
   4031      // composition or still has the composition) because we need to dispatch
   4032      // `textInput` event only for the last composition change.
   4033      aCompositionChangeEvent.IsFollowedByCompositionEnd()
   4034          ? EditAction::eUpdateCompositionToCommit
   4035          : EditAction::eUpdateComposition);
   4036  if (NS_WARN_IF(!editActionData.CanHandle())) {
   4037    return NS_ERROR_NOT_INITIALIZED;
   4038  }
   4039  MOZ_ASSERT(!aCompositionChangeEvent.mData.IsVoid());
   4040  editActionData.SetData(aCompositionChangeEvent.mData);
   4041 
   4042  // If we're an `HTMLEditor` and this is second or later composition change,
   4043  // we should set target range to the range of composition string.
   4044  // Otherwise, set target ranges to selection ranges (will be done by
   4045  // editActionData itself before dispatching `beforeinput` event).
   4046  if (IsHTMLEditor() && mComposition->GetContainerTextNode()) {
   4047    RefPtr<StaticRange> targetRange = StaticRange::Create(
   4048        mComposition->GetContainerTextNode(),
   4049        mComposition->XPOffsetInTextNode(),
   4050        mComposition->GetContainerTextNode(),
   4051        mComposition->XPEndOffsetInTextNode(), IgnoreErrors());
   4052    NS_WARNING_ASSERTION(targetRange && targetRange->IsPositioned(),
   4053                         "StaticRange::Create() failed");
   4054    if (targetRange && targetRange->IsPositioned()) {
   4055      editActionData.AppendTargetRange(*targetRange);
   4056    }
   4057  }
   4058 
   4059  // TODO: We need to use different EditAction value for beforeinput event
   4060  //       if the event is followed by "compositionend" because corresponding
   4061  //       "input" event will be fired from OnCompositionEnd() later with
   4062  //       different EditAction value.
   4063  // TODO: If Input Events Level 2 is enabled, "beforeinput" event may be
   4064  //       actually canceled if edit action is eDeleteByComposition. In such
   4065  //       case, we might need to keep selected text, but insert composition
   4066  //       string before or after the selection.  However, the spec is still
   4067  //       unstable.  We should keep handling the composition since other
   4068  //       parts including widget may not be ready for such complicated
   4069  //       behavior.
   4070  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   4071  if (rv != NS_ERROR_EDITOR_ACTION_CANCELED && NS_FAILED(rv)) {
   4072    NS_WARNING("MaybeDispatchBeforeInputEvent() failed");
   4073    return EditorBase::ToGenericNSResult(rv);
   4074  }
   4075 
   4076  if (!EnsureComposition(aCompositionChangeEvent)) {
   4077    NS_WARNING("EditorBase::EnsureComposition() failed");
   4078    return NS_OK;
   4079  }
   4080 
   4081  if (NS_WARN_IF(!GetPresShell())) {
   4082    return NS_ERROR_NOT_INITIALIZED;
   4083  }
   4084 
   4085  // NOTE: TextComposition should receive selection change notification before
   4086  //       CompositionChangeEventHandlingMarker notifies TextComposition of the
   4087  //       end of handling compositionchange event because TextComposition may
   4088  //       need to ignore selection changes caused by composition.  Therefore,
   4089  //       CompositionChangeEventHandlingMarker must be destroyed after a call
   4090  //       of NotifiyEditorObservers(eNotifyEditorObserversOfEnd) or
   4091  //       NotifiyEditorObservers(eNotifyEditorObserversOfCancel) which notifies
   4092  //       TextComposition of a selection change.
   4093  MOZ_ASSERT(
   4094      !mPlaceholderBatch,
   4095      "UpdateIMEComposition() must be called without place holder batch");
   4096  nsString data(aCompositionChangeEvent.mData);
   4097  if (IsHTMLEditor()) {
   4098    nsContentUtils::PlatformToDOMLineBreaks(data);
   4099  }
   4100 
   4101  {
   4102    // This needs to be destroyed before dispatching "input" event from
   4103    // the following call of `NotifyEditorObservers`.  Therefore, we need to
   4104    // put this in this block rather than outside of this.
   4105    const bool wasComposing = mComposition->IsComposing();
   4106    TextComposition::CompositionChangeEventHandlingMarker
   4107        compositionChangeEventHandlingMarker(mComposition,
   4108                                             &aCompositionChangeEvent);
   4109    AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::IMETxnName,
   4110                                               ScrollSelectionIntoView::Yes,
   4111                                               __FUNCTION__);
   4112 
   4113    // XXX Why don't we get caret after the DOM mutation?
   4114    RefPtr<nsCaret> caret = GetCaret();
   4115 
   4116    MOZ_ASSERT(
   4117        mIsInEditSubAction,
   4118        "AutoPlaceholderBatch should've notified the observes of before-edit");
   4119    // If we're updating composition, we need to ignore normal selection
   4120    // which may be updated by the web content.
   4121    const auto purpose = [&]() -> InsertTextFor {
   4122      if (!wasComposing) {
   4123        return !aCompositionChangeEvent.IsFollowedByCompositionEnd()
   4124                   ? InsertTextFor::CompositionStart
   4125                   : InsertTextFor::CompositionStartAndEnd;
   4126      }
   4127      return !aCompositionChangeEvent.IsFollowedByCompositionEnd()
   4128                 ? InsertTextFor::CompositionUpdate
   4129                 : InsertTextFor::CompositionEnd;
   4130    }();
   4131    rv = InsertTextAsSubAction(data, purpose);
   4132    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4133                         "EditorBase::InsertTextAsSubAction() failed");
   4134 
   4135    if (caret) {
   4136      caret->SetSelection(&SelectionRef());
   4137    }
   4138  }
   4139 
   4140  // If still composing, we should fire input event via observer.
   4141  // Note that if the composition will be committed by the following
   4142  // compositionend event, we don't need to notify editor observes of this
   4143  // change even if it's preferred by the pref.
   4144  // NOTE: We must notify after the auto batch will be gone.
   4145  if (!aCompositionChangeEvent.IsFollowedByCompositionEnd()) {
   4146    // If we're a TextEditor, we'll be initialized with a new anonymous subtree,
   4147    // which can be caused by reframing from a "input" event listener.  At that
   4148    // time, we'll move composition from current text node to the new text node
   4149    // with using mComposition's data.  Therefore, it's important that
   4150    // mComposition already has the latest information here.
   4151    MOZ_ASSERT_IF(mComposition, mComposition->String() == data);
   4152    NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   4153  }
   4154  // NOTE: When the pref is enabled, the last `input` event which will be fired
   4155  // after `compositionend` won't be paired with corresponding `beforeinput`
   4156  // event.
   4157  else if (StaticPrefs::dom_input_events_dispatch_before_compositionend() &&
   4158           mDispatchInputEvent && !IsEditActionAborted()) {
   4159    DispatchInputEvent();
   4160  }
   4161 
   4162  return EditorBase::ToGenericNSResult(rv);
   4163 }
   4164 
   4165 void EditorBase::OnCompositionEnd(
   4166    WidgetCompositionEvent& aCompositionEndEvent) {
   4167  MOZ_LOG(gTextInputLog, LogLevel::Info,
   4168          ("%p %s::OnCompositionEnd(aCompositionEndEvent={ mData=\"%s\"}), "
   4169           "mComposition=%p",
   4170           this, mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   4171           NS_ConvertUTF16toUTF8(aCompositionEndEvent.mData).get(),
   4172           mComposition.get()));
   4173 
   4174  if (!mComposition) {
   4175    NS_WARNING("There is no composition, but receiving compositionend event");
   4176    return;
   4177  }
   4178 
   4179  const EditAction editAction = aCompositionEndEvent.mData.IsEmpty()
   4180                                    ? EditAction::eCancelComposition
   4181                                    : EditAction::eCommitComposition;
   4182  AutoEditActionDataSetter editActionData(*this, editAction);
   4183  // If Input Events Level 2 is enabled, EditAction::eCancelComposition is
   4184  // mapped to EditorInputType::eDeleteCompositionText and it requires null
   4185  // for InputEvent.data.  Therefore, only otherwise, we should set data.
   4186  if (ToInputType(editAction) != EditorInputType::eDeleteCompositionText) {
   4187    MOZ_ASSERT(
   4188        ToInputType(editAction) == EditorInputType::eInsertCompositionText ||
   4189        ToInputType(editAction) == EditorInputType::eInsertFromComposition);
   4190    MOZ_ASSERT(!aCompositionEndEvent.mData.IsVoid());
   4191    editActionData.SetData(aCompositionEndEvent.mData);
   4192  }
   4193 
   4194  const RefPtr<PlaceholderTransaction> placeholderTransaction =
   4195      [&]() -> PlaceholderTransaction* {
   4196    if (!mTransactionManager) {
   4197      return nullptr;
   4198    }
   4199    const nsCOMPtr<nsITransaction> transaction =
   4200        mTransactionManager->PeekUndoStack();
   4201    if (MOZ_UNLIKELY(!transaction)) {
   4202      return nullptr;
   4203    }
   4204    const RefPtr<EditTransactionBase> transactionBase =
   4205        transaction->GetAsEditTransactionBase();
   4206    if (MOZ_UNLIKELY(!transactionBase)) {
   4207      return nullptr;
   4208    }
   4209    return transactionBase->GetAsPlaceholderTransaction();
   4210  }();
   4211  // commit the IME transaction..we can get at it via the transaction mgr.
   4212  // Note that this means IME won't work without an undo stack!
   4213  if (placeholderTransaction) {
   4214    placeholderTransaction->Commit();
   4215  }
   4216 
   4217  // If the composition is canceled and the composition hasn't remove any
   4218  // content, we should remove the transaction from the undo stack because
   4219  // user "canceled" it, so, undoing the canceled composition is odd.  That
   4220  // would appear as a noop undo transaction.
   4221  if (editAction == EditAction::eCancelComposition && placeholderTransaction) {
   4222    const nsTArray<OwningNonNull<EditTransactionBase>>& childTransactions =
   4223        placeholderTransaction->ChildTransactions();
   4224    MOZ_ASSERT(!childTransactions.IsEmpty());
   4225    // If the first transaction is inserting composition string, we didn't
   4226    // replace selection with the composition string.  Then, all of the
   4227    // operations during the composition is canceled by the user.  So, we should
   4228    // not record it as an undo transaction.
   4229    if (childTransactions[0]->GetAsCompositionTransaction()) {
   4230      nsCOMPtr<nsITransaction> transaction =
   4231          mTransactionManager->PopUndoStack();
   4232      MOZ_DIAGNOSTIC_ASSERT(transaction == placeholderTransaction);
   4233    }
   4234  }
   4235 
   4236  // Note that this just marks as that we've already handled "beforeinput" for
   4237  // preventing assertions in FireInputEvent().  Note that corresponding
   4238  // "beforeinput" event for the following "input" event should've already
   4239  // been dispatched from `OnCompositionChange()`.
   4240  DebugOnly<nsresult> rvIgnored =
   4241      editActionData.MaybeDispatchBeforeInputEvent();
   4242  MOZ_ASSERT(rvIgnored != NS_ERROR_EDITOR_ACTION_CANCELED,
   4243             "Why beforeinput event was canceled in this case?");
   4244  MOZ_ASSERT(NS_SUCCEEDED(rvIgnored),
   4245             "MaybeDispatchBeforeInputEvent() should just mark the instance as "
   4246             "handled it");
   4247 
   4248  // Composition string may have hidden the caret.  Therefore, we need to
   4249  // cancel it here.
   4250  HideCaret(false);
   4251 
   4252  // FYI: mComposition still keeps storing container text node of committed
   4253  //      string, its offset and length.  However, they will be invalidated
   4254  //      soon since its Destroy() will be called by IMEStateManager.
   4255  mComposition->EndHandlingComposition(this);
   4256  mComposition = nullptr;
   4257 
   4258  // notify editor observers of action
   4259  // FYI: With current draft, "input" event should be fired from
   4260  //      OnCompositionChange(), however, it requires a lot of our UI code
   4261  //      change and does not make sense.  See spec issue:
   4262  //      https://github.com/w3c/uievents/issues/202
   4263  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   4264 }
   4265 
   4266 bool EditorBase::WillHandleMouseButtonEvent(WidgetMouseEvent& aMouseEvent) {
   4267  MOZ_ASSERT(aMouseEvent.mMessage == eMouseDown ||
   4268             aMouseEvent.mMessage == eMouseUp);
   4269  if (!mEventListener) {
   4270    return false;
   4271  }
   4272  OwningNonNull<EditorEventListener> editorEventListener(*mEventListener);
   4273  return editorEventListener->WillHandleMouseButtonEvent(aMouseEvent);
   4274 }
   4275 
   4276 void EditorBase::DoAfterDoTransaction(nsITransaction* aTransaction) {
   4277  bool isTransientTransaction;
   4278  MOZ_ALWAYS_SUCCEEDS(aTransaction->GetIsTransient(&isTransientTransaction));
   4279 
   4280  if (!isTransientTransaction) {
   4281    // we need to deal here with the case where the user saved after some
   4282    // edits, then undid one or more times. Then, the undo count is -ve,
   4283    // but we can't let a do take it back to zero. So we flip it up to
   4284    // a +ve number.
   4285    int32_t modCount;
   4286    DebugOnly<nsresult> rvIgnored = GetModificationCount(&modCount);
   4287    NS_WARNING_ASSERTION(
   4288        NS_SUCCEEDED(rvIgnored),
   4289        "EditorBase::GetModificationCount() failed, but ignored");
   4290    if (modCount < 0) {
   4291      modCount = -modCount;
   4292    }
   4293 
   4294    // don't count transient transactions
   4295    MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(1));
   4296  }
   4297 }
   4298 
   4299 void EditorBase::DoAfterUndoTransaction() {
   4300  // all undoable transactions are non-transient
   4301  MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(-1));
   4302 }
   4303 
   4304 void EditorBase::DoAfterRedoTransaction() {
   4305  // all redoable transactions are non-transient
   4306  MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(1));
   4307 }
   4308 
   4309 already_AddRefed<DeleteMultipleRangesTransaction>
   4310 EditorBase::CreateTransactionForDeleteSelection(
   4311    HowToHandleCollapsedRange aHowToHandleCollapsedRange,
   4312    const AutoClonedRangeArray& aRangesToDelete) {
   4313  MOZ_ASSERT(IsEditActionDataAvailable());
   4314  MOZ_ASSERT(!aRangesToDelete.Ranges().IsEmpty());
   4315 
   4316  // Check whether the selection is collapsed and we should do nothing:
   4317  if (NS_WARN_IF(aRangesToDelete.IsCollapsed() &&
   4318                 aHowToHandleCollapsedRange ==
   4319                     HowToHandleCollapsedRange::Ignore)) {
   4320    return nullptr;
   4321  }
   4322 
   4323  // allocate the out-param transaction
   4324  RefPtr<DeleteMultipleRangesTransaction> transaction =
   4325      DeleteMultipleRangesTransaction::Create();
   4326  for (const OwningNonNull<nsRange>& range : aRangesToDelete.Ranges()) {
   4327    // Same with range as with selection; if it is collapsed and action
   4328    // is eNone, do nothing.
   4329    if (!range->Collapsed()) {
   4330      RefPtr<DeleteRangeTransaction> deleteRangeTransaction =
   4331          DeleteRangeTransaction::Create(*this, range);
   4332      // XXX Oh, not checking if deleteRangeTransaction can modify the range...
   4333      transaction->AppendChild(*deleteRangeTransaction);
   4334      continue;
   4335    }
   4336 
   4337    if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::Ignore) {
   4338      continue;
   4339    }
   4340 
   4341    // Let's extend the collapsed range to delete content around it.
   4342    RefPtr<DeleteContentTransactionBase> deleteNodeOrTextTransaction =
   4343        CreateTransactionForCollapsedRange(range, aHowToHandleCollapsedRange);
   4344    // XXX When there are two or more ranges and at least one of them is
   4345    //     not editable, deleteNodeOrTextTransaction may be nullptr.
   4346    //     In such case, should we stop removing other ranges too?
   4347    if (!deleteNodeOrTextTransaction) {
   4348      NS_WARNING("EditorBase::CreateTransactionForCollapsedRange() failed");
   4349      return nullptr;
   4350    }
   4351    transaction->AppendChild(*deleteNodeOrTextTransaction);
   4352  }
   4353 
   4354  return transaction.forget();
   4355 }
   4356 
   4357 // XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
   4358 // are not implemented
   4359 already_AddRefed<DeleteContentTransactionBase>
   4360 EditorBase::CreateTransactionForCollapsedRange(
   4361    const nsRange& aCollapsedRange,
   4362    HowToHandleCollapsedRange aHowToHandleCollapsedRange) {
   4363  MOZ_ASSERT(aCollapsedRange.Collapsed());
   4364  MOZ_ASSERT(
   4365      aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward ||
   4366      aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendForward);
   4367 
   4368  EditorRawDOMPoint point(aCollapsedRange.StartRef());
   4369  if (NS_WARN_IF(!point.IsSet())) {
   4370    return nullptr;
   4371  }
   4372  if (IsTextEditor()) {
   4373    // There should be only one text node in the anonymous `<div>` (but may
   4374    // be followed by a padding `<br>`).  We should adjust the point into
   4375    // the text node (or return nullptr if there is no text to delete) for
   4376    // avoiding finding the text node with complicated API.
   4377    if (!point.IsInTextNode()) {
   4378      const Element* anonymousDiv = GetRoot();
   4379      if (NS_WARN_IF(!anonymousDiv)) {
   4380        return nullptr;
   4381      }
   4382      if (!anonymousDiv->GetFirstChild() ||
   4383          !anonymousDiv->GetFirstChild()->IsText()) {
   4384        return nullptr;  // The value is empty.
   4385      }
   4386      if (point.GetContainer() == anonymousDiv) {
   4387        if (point.IsStartOfContainer()) {
   4388          point.Set(anonymousDiv->GetFirstChild(), 0);
   4389        } else {
   4390          point.SetToEndOf(anonymousDiv->GetFirstChild());
   4391        }
   4392      } else {
   4393        // Must be referring a padding `<br>` element or after the text node.
   4394        point.SetToEndOf(anonymousDiv->GetFirstChild());
   4395      }
   4396    }
   4397    MOZ_ASSERT(!point.ContainerAs<Text>()->GetPreviousSibling());
   4398    MOZ_ASSERT(!point.ContainerAs<Text>()->GetNextSibling() ||
   4399               !point.ContainerAs<Text>()->GetNextSibling()->IsText());
   4400    if (aHowToHandleCollapsedRange ==
   4401            HowToHandleCollapsedRange::ExtendBackward &&
   4402        point.IsStartOfContainer()) {
   4403      return nullptr;
   4404    }
   4405    if (aHowToHandleCollapsedRange ==
   4406            HowToHandleCollapsedRange::ExtendForward &&
   4407        point.IsEndOfContainer()) {
   4408      return nullptr;
   4409    }
   4410  }
   4411 
   4412  // XXX: if the container of point is empty, then we'll need to delete the node
   4413  //      as well as the 1 child
   4414 
   4415  // build a transaction for deleting the appropriate data
   4416  // XXX: this has to come from rule section
   4417  const Element* const anonymousDivOrEditingHost =
   4418      IsTextEditor() ? GetRoot() : AsHTMLEditor()->ComputeEditingHost();
   4419  if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward &&
   4420      point.IsStartOfContainer()) {
   4421    MOZ_ASSERT(IsHTMLEditor());
   4422    // We're backspacing from the beginning of a node.  Delete the last thing
   4423    // of previous editable content.
   4424    nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
   4425        *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
   4426        IsTextEditor() ? BlockInlineCheck::UseHTMLDefaultStyle
   4427                       : BlockInlineCheck::UseComputedDisplayOutsideStyle,
   4428        anonymousDivOrEditingHost);
   4429    if (!previousEditableContent) {
   4430      NS_WARNING("There was no editable content before the collapsed range");
   4431      return nullptr;
   4432    }
   4433 
   4434    // There is an editable content, so delete its last child (if a text node,
   4435    // delete the last char).  If it has no children, delete it.
   4436    if (previousEditableContent->IsText()) {
   4437      uint32_t length = previousEditableContent->Length();
   4438      // Bail out for empty text node.
   4439      // XXX Do we want to do something else?
   4440      // XXX If other browsers delete empty text node, we should follow it.
   4441      if (NS_WARN_IF(!length)) {
   4442        NS_WARNING("Previous editable content was an empty text node");
   4443        return nullptr;
   4444      }
   4445      RefPtr<DeleteTextTransaction> deleteTextTransaction =
   4446          DeleteTextTransaction::MaybeCreateForPreviousCharacter(
   4447              *this, *previousEditableContent->AsText(), length);
   4448      if (!deleteTextTransaction) {
   4449        NS_WARNING(
   4450            "DeleteTextTransaction::MaybeCreateForPreviousCharacter() failed");
   4451        return nullptr;
   4452      }
   4453      return deleteTextTransaction.forget();
   4454    }
   4455 
   4456    if (IsHTMLEditor() &&
   4457        NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(*previousEditableContent))) {
   4458      return nullptr;
   4459    }
   4460    RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
   4461        DeleteNodeTransaction::MaybeCreate(*this, *previousEditableContent);
   4462    if (!deleteNodeTransaction) {
   4463      NS_WARNING("DeleteNodeTransaction::MaybeCreate() failed");
   4464      return nullptr;
   4465    }
   4466    return deleteNodeTransaction.forget();
   4467  }
   4468 
   4469  if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendForward &&
   4470      point.IsEndOfContainer()) {
   4471    MOZ_ASSERT(IsHTMLEditor());
   4472    // We're deleting from the end of a node.  Delete the first thing of
   4473    // next editable content.
   4474    nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
   4475        *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
   4476        IsTextEditor() ? BlockInlineCheck::UseHTMLDefaultStyle
   4477                       : BlockInlineCheck::UseComputedDisplayOutsideStyle,
   4478        anonymousDivOrEditingHost);
   4479    if (!nextEditableContent) {
   4480      NS_WARNING("There was no editable content after the collapsed range");
   4481      return nullptr;
   4482    }
   4483 
   4484    // There is an editable content, so delete its first child (if a text node,
   4485    // delete the first char).  If it has no children, delete it.
   4486    if (nextEditableContent->IsText()) {
   4487      uint32_t length = nextEditableContent->Length();
   4488      // Bail out for empty text node.
   4489      // XXX Do we want to do something else?
   4490      // XXX If other browsers delete empty text node, we should follow it.
   4491      if (!length) {
   4492        NS_WARNING("Next editable content was an empty text node");
   4493        return nullptr;
   4494      }
   4495      RefPtr<DeleteTextTransaction> deleteTextTransaction =
   4496          DeleteTextTransaction::MaybeCreateForNextCharacter(
   4497              *this, *nextEditableContent->AsText(), 0);
   4498      if (!deleteTextTransaction) {
   4499        NS_WARNING(
   4500            "DeleteTextTransaction::MaybeCreateForNextCharacter() failed");
   4501        return nullptr;
   4502      }
   4503      return deleteTextTransaction.forget();
   4504    }
   4505 
   4506    if (IsHTMLEditor() &&
   4507        NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(*nextEditableContent))) {
   4508      return nullptr;
   4509    }
   4510    RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
   4511        DeleteNodeTransaction::MaybeCreate(*this, *nextEditableContent);
   4512    if (!deleteNodeTransaction) {
   4513      NS_WARNING("DeleteNodeTransaction::MaybeCreate() failed");
   4514      return nullptr;
   4515    }
   4516    return deleteNodeTransaction.forget();
   4517  }
   4518 
   4519  if (point.IsInTextNode()) {
   4520    if (aHowToHandleCollapsedRange ==
   4521        HowToHandleCollapsedRange::ExtendBackward) {
   4522      RefPtr<DeleteTextTransaction> deleteTextTransaction =
   4523          DeleteTextTransaction::MaybeCreateForPreviousCharacter(
   4524              *this, *point.ContainerAs<Text>(), point.Offset());
   4525      NS_WARNING_ASSERTION(
   4526          deleteTextTransaction,
   4527          "DeleteTextTransaction::MaybeCreateForPreviousCharacter() failed");
   4528      return deleteTextTransaction.forget();
   4529    }
   4530    RefPtr<DeleteTextTransaction> deleteTextTransaction =
   4531        DeleteTextTransaction::MaybeCreateForNextCharacter(
   4532            *this, *point.ContainerAs<Text>(), point.Offset());
   4533    NS_WARNING_ASSERTION(
   4534        deleteTextTransaction,
   4535        "DeleteTextTransaction::MaybeCreateForNextCharacter() failed");
   4536    return deleteTextTransaction.forget();
   4537  }
   4538 
   4539  nsIContent* editableContent = nullptr;
   4540  if (IsHTMLEditor()) {
   4541    editableContent =
   4542        aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward
   4543            ? HTMLEditUtils::GetPreviousContent(
   4544                  point, {WalkTreeOption::IgnoreNonEditableNode},
   4545                  BlockInlineCheck::UseComputedDisplayOutsideStyle,
   4546                  anonymousDivOrEditingHost)
   4547            : HTMLEditUtils::GetNextContent(
   4548                  point, {WalkTreeOption::IgnoreNonEditableNode},
   4549                  BlockInlineCheck::UseComputedDisplayOutsideStyle,
   4550                  anonymousDivOrEditingHost);
   4551    if (!editableContent) {
   4552      NS_WARNING("There was no editable content around the collapsed range");
   4553      return nullptr;
   4554    }
   4555    while (editableContent && editableContent->IsCharacterData() &&
   4556           !editableContent->Length()) {
   4557      // Can't delete an empty text node (bug 762183)
   4558      editableContent =
   4559          aHowToHandleCollapsedRange ==
   4560                  HowToHandleCollapsedRange::ExtendBackward
   4561              ? HTMLEditUtils::GetPreviousContent(
   4562                    *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
   4563                    BlockInlineCheck::UseComputedDisplayOutsideStyle,
   4564                    anonymousDivOrEditingHost)
   4565              : HTMLEditUtils::GetNextContent(
   4566                    *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
   4567                    BlockInlineCheck::UseComputedDisplayOutsideStyle,
   4568                    anonymousDivOrEditingHost);
   4569    }
   4570    if (!editableContent) {
   4571      NS_WARNING(
   4572          "There was no editable content which is not empty around the "
   4573          "collapsed range");
   4574      return nullptr;
   4575    }
   4576  } else {
   4577    MOZ_ASSERT(point.IsInTextNode());
   4578    editableContent = point.GetContainerAs<nsIContent>();
   4579    if (!editableContent) {
   4580      NS_WARNING("If there was no text node, should've been handled first");
   4581      return nullptr;
   4582    }
   4583  }
   4584 
   4585  if (editableContent->IsText()) {
   4586    if (aHowToHandleCollapsedRange ==
   4587        HowToHandleCollapsedRange::ExtendBackward) {
   4588      RefPtr<DeleteTextTransaction> deleteTextTransaction =
   4589          DeleteTextTransaction::MaybeCreateForPreviousCharacter(
   4590              *this, *editableContent->AsText(), editableContent->Length());
   4591      NS_WARNING_ASSERTION(
   4592          deleteTextTransaction,
   4593          "DeleteTextTransaction::MaybeCreateForPreviousCharacter() failed");
   4594      return deleteTextTransaction.forget();
   4595    }
   4596 
   4597    RefPtr<DeleteTextTransaction> deleteTextTransaction =
   4598        DeleteTextTransaction::MaybeCreateForNextCharacter(
   4599            *this, *editableContent->AsText(), 0);
   4600    NS_WARNING_ASSERTION(
   4601        deleteTextTransaction,
   4602        "DeleteTextTransaction::MaybeCreateForNextCharacter() failed");
   4603    return deleteTextTransaction.forget();
   4604  }
   4605 
   4606  MOZ_ASSERT(IsHTMLEditor());
   4607  if (NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(*editableContent))) {
   4608    return nullptr;
   4609  }
   4610  RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
   4611      DeleteNodeTransaction::MaybeCreate(*this, *editableContent);
   4612  NS_WARNING_ASSERTION(deleteNodeTransaction,
   4613                       "DeleteNodeTransaction::MaybeCreate() failed");
   4614  return deleteNodeTransaction.forget();
   4615 }
   4616 
   4617 bool EditorBase::FlushPendingNotificationsIfToHandleDeletionWithFrameSelection(
   4618    nsIEditor::EDirection aDirectionAndAmount) const {
   4619  MOZ_ASSERT(IsEditActionDataAvailable());
   4620 
   4621  if (NS_WARN_IF(Destroyed())) {
   4622    return false;
   4623  }
   4624  if (!EditorUtils::IsFrameSelectionRequiredToExtendSelection(
   4625          aDirectionAndAmount, SelectionRef())) {
   4626    return true;
   4627  }
   4628  // Although AutoClonedSelectionRangeArray::ExtendAnchorFocusRangeFor() will
   4629  // use nsFrameSelection, if it still has dirty frame, nsFrameSelection doesn't
   4630  // extend selection since we block script.
   4631  if (RefPtr<PresShell> presShell = GetPresShell()) {
   4632    presShell->FlushPendingNotifications(FlushType::Layout);
   4633    if (NS_WARN_IF(Destroyed())) {
   4634      return false;
   4635    }
   4636  }
   4637  return true;
   4638 }
   4639 
   4640 nsresult EditorBase::DeleteSelectionAsAction(
   4641    nsIEditor::EDirection aDirectionAndAmount,
   4642    nsIEditor::EStripWrappers aStripWrappers, nsIPrincipal* aPrincipal) {
   4643  MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   4644  // Showing this assertion is fine if this method is called by outside via
   4645  // mutation event listener or something.  Otherwise, this is called by
   4646  // wrong method.
   4647  NS_ASSERTION(
   4648      !mPlaceholderBatch,
   4649      "Should be called only when this is the only edit action of the "
   4650      "operation unless mutation event listener nests some operations");
   4651 
   4652  // If we're a TextEditor instance, we don't need to treat parent elements
   4653  // so that we can ignore aStripWrappers for skipping unnecessary cost.
   4654  if (IsTextEditor()) {
   4655    aStripWrappers = nsIEditor::eNoStrip;
   4656  }
   4657 
   4658  EditAction editAction = EditAction::eDeleteSelection;
   4659  switch (aDirectionAndAmount) {
   4660    case nsIEditor::ePrevious:
   4661      editAction = EditAction::eDeleteBackward;
   4662      break;
   4663    case nsIEditor::eNext:
   4664      editAction = EditAction::eDeleteForward;
   4665      break;
   4666    case nsIEditor::ePreviousWord:
   4667      editAction = EditAction::eDeleteWordBackward;
   4668      break;
   4669    case nsIEditor::eNextWord:
   4670      editAction = EditAction::eDeleteWordForward;
   4671      break;
   4672    case nsIEditor::eToBeginningOfLine:
   4673      editAction = EditAction::eDeleteToBeginningOfSoftLine;
   4674      break;
   4675    case nsIEditor::eToEndOfLine:
   4676      editAction = EditAction::eDeleteToEndOfSoftLine;
   4677      break;
   4678  }
   4679 
   4680  AutoEditActionDataSetter editActionData(*this, editAction, aPrincipal);
   4681  if (NS_WARN_IF(!editActionData.CanHandle())) {
   4682    return NS_ERROR_NOT_INITIALIZED;
   4683  }
   4684 
   4685  // If there is an existing selection when an extended delete is requested,
   4686  // platforms that use "caret-style" caret positioning collapse the
   4687  // selection to the  start and then create a new selection.
   4688  // Platforms that use "selection-style" caret positioning just delete the
   4689  // existing selection without extending it.
   4690  if (!SelectionRef().IsCollapsed()) {
   4691    switch (aDirectionAndAmount) {
   4692      case eNextWord:
   4693      case ePreviousWord:
   4694      case eToBeginningOfLine:
   4695      case eToEndOfLine: {
   4696        if (mCaretStyle != 1) {
   4697          aDirectionAndAmount = eNone;
   4698          break;
   4699        }
   4700        ErrorResult error;
   4701        SelectionRef().CollapseToStart(error);
   4702        if (NS_WARN_IF(Destroyed())) {
   4703          error.SuppressException();
   4704          return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   4705        }
   4706        if (error.Failed()) {
   4707          NS_WARNING("Selection::CollapseToStart() failed");
   4708          editActionData.Abort();
   4709          return EditorBase::ToGenericNSResult(error.StealNSResult());
   4710        }
   4711        break;
   4712      }
   4713      default:
   4714        break;
   4715    }
   4716  }
   4717 
   4718  // If Selection is still NOT collapsed, it does not important removing
   4719  // range of the operation since we'll remove the selected content.  However,
   4720  // information of direction (backward or forward) may be important for
   4721  // web apps.  E.g., web apps may want to mark selected range as "deleted"
   4722  // and move caret before or after the range.  Therefore, we should forget
   4723  // only the range information but keep range information.  See discussion
   4724  // of the spec issue for the detail:
   4725  // https://github.com/w3c/input-events/issues/82
   4726  if (!SelectionRef().IsCollapsed()) {
   4727    switch (editAction) {
   4728      case EditAction::eDeleteWordBackward:
   4729      case EditAction::eDeleteToBeginningOfSoftLine:
   4730        editActionData.UpdateEditAction(EditAction::eDeleteBackward);
   4731        break;
   4732      case EditAction::eDeleteWordForward:
   4733      case EditAction::eDeleteToEndOfSoftLine:
   4734        editActionData.UpdateEditAction(EditAction::eDeleteForward);
   4735        break;
   4736      default:
   4737        break;
   4738    }
   4739  }
   4740 
   4741  editActionData.SetSelectionCreatedByDoubleclick(
   4742      SelectionRef().GetFrameSelection() &&
   4743      SelectionRef().GetFrameSelection()->IsDoubleClickSelection());
   4744 
   4745  if (!FlushPendingNotificationsIfToHandleDeletionWithFrameSelection(
   4746          aDirectionAndAmount)) {
   4747    NS_WARNING("Flusing pending notifications caused destroying the editor");
   4748    editActionData.Abort();
   4749    return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   4750  }
   4751 
   4752  nsresult rv =
   4753      editActionData.MaybeDispatchBeforeInputEvent(aDirectionAndAmount);
   4754  if (NS_FAILED(rv)) {
   4755    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   4756                         "MaybeDispatchBeforeInputEvent() failed");
   4757    return EditorBase::ToGenericNSResult(rv);
   4758  }
   4759 
   4760  // delete placeholder txns merge.
   4761  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName,
   4762                                             ScrollSelectionIntoView::Yes,
   4763                                             __FUNCTION__);
   4764  rv = DeleteSelectionAsSubAction(aDirectionAndAmount, aStripWrappers);
   4765  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4766                       "EditorBase::DeleteSelectionAsSubAction() failed");
   4767  return EditorBase::ToGenericNSResult(rv);
   4768 }
   4769 
   4770 nsresult EditorBase::DeleteSelectionAsSubAction(
   4771    nsIEditor::EDirection aDirectionAndAmount,
   4772    nsIEditor::EStripWrappers aStripWrappers) {
   4773  MOZ_ASSERT(IsEditActionDataAvailable());
   4774  // If handling edit action is for table editing, this may be called with
   4775  // selecting an any table element by the caller, but it's not usual work of
   4776  // this so that `MayEditActionDeleteSelection()` returns false.
   4777  MOZ_ASSERT(MayEditActionDeleteSelection(GetEditAction()) ||
   4778             IsEditActionTableEditing(GetEditAction()));
   4779  MOZ_ASSERT(mPlaceholderBatch);
   4780  MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   4781  NS_ASSERTION(IsHTMLEditor() || aStripWrappers == nsIEditor::eNoStrip,
   4782               "TextEditor does not support strip wrappers");
   4783 
   4784  if (NS_WARN_IF(!mInitSucceeded)) {
   4785    return NS_ERROR_NOT_INITIALIZED;
   4786  }
   4787 
   4788  IgnoredErrorResult ignoredError;
   4789  AutoEditSubActionNotifier startToHandleEditSubAction(
   4790      *this, EditSubAction::eDeleteSelectedContent, aDirectionAndAmount,
   4791      ignoredError);
   4792  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   4793    return ignoredError.StealNSResult();
   4794  }
   4795  NS_WARNING_ASSERTION(
   4796      !ignoredError.Failed(),
   4797      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   4798 
   4799  {
   4800    Result<EditActionResult, nsresult> result =
   4801        HandleDeleteSelection(aDirectionAndAmount, aStripWrappers);
   4802    if (MOZ_UNLIKELY(result.isErr())) {
   4803      // If HTMLEditor::HandleDeleteSelection() returns "no editable range"
   4804      // error and the range is collapsed and the deletion is a preparation for
   4805      // inserting something, we wan't to keep handling the insertion without
   4806      // error.
   4807      if (result.inspectErr() == NS_ERROR_EDITOR_NO_DELETABLE_RANGE &&
   4808          GetTopLevelEditSubAction() != EditSubAction::eDeleteSelectedContent) {
   4809        return NS_OK;
   4810      }
   4811      NS_WARNING(nsPrintfCString("%s::HandleDeleteSelection() failed",
   4812                                 IsTextEditor() ? "TextEditor" : "HTMLEditor")
   4813                     .get());
   4814      return result.unwrapErr();
   4815    }
   4816    if (result.inspect().Canceled()) {
   4817      return NS_OK;
   4818    }
   4819  }
   4820 
   4821  // XXX This is odd.  We just tries to remove empty text node here but we
   4822  //     refer `Selection`.  It may be modified by mutation event listeners
   4823  //     so that we should remove the empty text node when we make it empty.
   4824  const auto atNewStartOfSelection =
   4825      GetFirstSelectionStartPoint<EditorDOMPoint>();
   4826  if (NS_WARN_IF(!atNewStartOfSelection.IsSet())) {
   4827    // XXX And also it seems that we don't need to return error here.
   4828    //     Why don't we just ignore?  `Selection::RemoveAllRanges()` may
   4829    //     have been called by mutation event listeners.
   4830    return NS_ERROR_FAILURE;
   4831  }
   4832  if (IsHTMLEditor() && atNewStartOfSelection.IsInTextNode() &&
   4833      !atNewStartOfSelection.GetContainer()->Length()) {
   4834    nsresult rv = DeleteNodeWithTransaction(
   4835        MOZ_KnownLive(*atNewStartOfSelection.ContainerAs<Text>()));
   4836    if (NS_FAILED(rv)) {
   4837      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   4838      return rv;
   4839    }
   4840  }
   4841 
   4842  // XXX I don't think that this is necessary in anonymous `<div>` element of
   4843  //     TextEditor since there should be at most one text node and at most
   4844  //     one padding `<br>` element so that `<br>` element won't be before
   4845  //     caret.
   4846  if (!TopLevelEditSubActionDataRef().mDidExplicitlySetInterLine) {
   4847    // We prevent the caret from sticking on the left of previous `<br>`
   4848    // element (i.e. the end of previous line) after this deletion. Bug 92124.
   4849    if (MOZ_UNLIKELY(NS_FAILED(SelectionRef().SetInterlinePosition(
   4850            InterlinePosition::StartOfNextLine)))) {
   4851      NS_WARNING(
   4852          "Selection::SetInterlinePosition(InterlinePosition::StartOfNextLine) "
   4853          "failed");
   4854      return NS_ERROR_FAILURE;  // Don't need to return NS_ERROR_NOT_INITIALIZED
   4855    }
   4856  }
   4857 
   4858  return NS_OK;
   4859 }
   4860 
   4861 nsresult EditorBase::HandleDropEvent(DragEvent* aDropEvent) {
   4862  if (NS_WARN_IF(!aDropEvent)) {
   4863    return NS_ERROR_INVALID_ARG;
   4864  }
   4865 
   4866  DebugOnly<nsresult> rvIgnored = CommitComposition();
   4867  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   4868                       "EditorBase::CommitComposition() failed, but ignored");
   4869 
   4870  AutoEditActionDataSetter editActionData(*this, EditAction::eDrop);
   4871  // We need to initialize data or dataTransfer later.  Therefore, we cannot
   4872  // dispatch "beforeinput" event until then.
   4873  if (NS_WARN_IF(!editActionData.CanHandle())) {
   4874    return NS_ERROR_NOT_INITIALIZED;
   4875  }
   4876 
   4877  RefPtr<DataTransfer> dataTransfer = aDropEvent->GetDataTransfer();
   4878  if (NS_WARN_IF(!dataTransfer)) {
   4879    return NS_ERROR_FAILURE;
   4880  }
   4881 
   4882  RefPtr<nsIWidget> widget = GetWidget();
   4883  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(widget);
   4884  if (NS_WARN_IF(!dragSession)) {
   4885    return NS_ERROR_FAILURE;
   4886  }
   4887 
   4888  nsCOMPtr<nsINode> sourceNode = dataTransfer->GetMozSourceNode();
   4889 
   4890  // If there is no source document, then the drag was from another application
   4891  // or another process (such as an out of process subframe). The latter case is
   4892  // not currently handled below when checking for a move/copy and deleting the
   4893  // existing text.
   4894  RefPtr<Document> srcdoc;
   4895  if (sourceNode) {
   4896    srcdoc = sourceNode->OwnerDoc();
   4897  }
   4898 
   4899  nsCOMPtr<nsIPrincipal> sourcePrincipal;
   4900  dragSession->GetTriggeringPrincipal(getter_AddRefs(sourcePrincipal));
   4901 
   4902  if (nsContentUtils::CheckForSubFrameDrop(
   4903          dragSession, aDropEvent->WidgetEventPtr()->AsDragEvent())) {
   4904    // Don't allow drags from subframe documents with different origins than
   4905    // the drop destination.
   4906    if (IsSafeToInsertData(sourcePrincipal) == SafeToInsertData::No) {
   4907      return NS_OK;
   4908    }
   4909  }
   4910 
   4911  // Current doc is destination
   4912  RefPtr<Document> document = GetDocument();
   4913  if (NS_WARN_IF(!document)) {
   4914    return NS_ERROR_NOT_INITIALIZED;
   4915  }
   4916 
   4917  const uint32_t numItems = dataTransfer->MozItemCount();
   4918  if (NS_WARN_IF(!numItems)) {
   4919    return NS_ERROR_FAILURE;  // Nothing to drop?
   4920  }
   4921 
   4922  // We have to figure out whether to delete and relocate caret only once
   4923  // Parent and offset are under the mouse cursor.
   4924  int32_t dropOffset = -1;
   4925  nsCOMPtr<nsIContent> dropParentContent =
   4926      aDropEvent->GetRangeParentContentAndOffset(&dropOffset);
   4927  if (dropOffset < 0) {
   4928    NS_WARNING(
   4929        "DropEvent::GetRangeParentContentAndOffset() returned negative offset");
   4930    return NS_ERROR_FAILURE;
   4931  }
   4932  EditorDOMPoint droppedAt(dropParentContent,
   4933                           AssertedCast<uint32_t>(dropOffset));
   4934  if (NS_WARN_IF(!droppedAt.IsInContentNode())) {
   4935    return NS_ERROR_FAILURE;
   4936  }
   4937 
   4938  // Check if dropping into a selected range.  If so and the source comes from
   4939  // same document, jump through some hoops to determine if mouse is over
   4940  // selection (bail) and whether user wants to copy selection or delete it.
   4941  if (sourceNode && sourceNode->IsEditable() && srcdoc == document) {
   4942    bool isPointInSelection = nsContentUtils::IsPointInSelection(
   4943        SelectionRef(), *droppedAt.GetContainer(), droppedAt.Offset());
   4944    if (isPointInSelection) {
   4945      // If source document and destination document is same and dropping
   4946      // into one of selected ranges, we don't need to do nothing.
   4947      // XXX If the source comes from outside of this editor, this check
   4948      //     means that we don't allow to drop the item in the selected
   4949      //     range.  However, the selection is hidden until the <input> or
   4950      //     <textarea> gets focus, therefore, this looks odd.
   4951      return NS_OK;
   4952    }
   4953  }
   4954 
   4955  // Delete if user doesn't want to copy when user moves selected content
   4956  // to different place in same editor.
   4957  // XXX Do we need the check whether it's in same document or not?
   4958  RefPtr<EditorBase> editorToDeleteSelection;
   4959  if (sourceNode && sourceNode->IsEditable() && srcdoc == document) {
   4960    if ((dataTransfer->DropEffectInt() &
   4961         nsIDragService::DRAGDROP_ACTION_MOVE) &&
   4962        !(dataTransfer->DropEffectInt() &
   4963          nsIDragService::DRAGDROP_ACTION_COPY)) {
   4964      // If the source node is in native anonymous tree, it must be in
   4965      // <input> or <textarea> element.  If so, its TextEditor can remove it.
   4966      if (sourceNode->IsInNativeAnonymousSubtree()) {
   4967        if (RefPtr textControlElement = TextControlElement::FromNodeOrNull(
   4968                sourceNode
   4969                    ->GetClosestNativeAnonymousSubtreeRootParentOrHost())) {
   4970          editorToDeleteSelection = textControlElement->GetTextEditor();
   4971        }
   4972      }
   4973      // Otherwise, must be the content is in HTMLEditor.
   4974      else if (IsHTMLEditor()) {
   4975        editorToDeleteSelection = this;
   4976      } else {
   4977        editorToDeleteSelection =
   4978            nsContentUtils::GetHTMLEditor(srcdoc->GetPresContext());
   4979      }
   4980    }
   4981    // If the found editor isn't modifiable, we should not try to delete
   4982    // selection.
   4983    if (editorToDeleteSelection && !editorToDeleteSelection->IsModifiable()) {
   4984      editorToDeleteSelection = nullptr;
   4985    }
   4986    // If the found editor has collapsed selection, we need to delete nothing
   4987    // in the editor.
   4988    if (editorToDeleteSelection) {
   4989      if (Selection* selection = editorToDeleteSelection->GetSelection()) {
   4990        if (selection->IsCollapsed()) {
   4991          editorToDeleteSelection = nullptr;
   4992        }
   4993      }
   4994    }
   4995  }
   4996 
   4997  // Combine any deletion and drop insertion into one transaction.
   4998  AutoPlaceholderBatch treatAsOneTransaction(
   4999      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   5000 
   5001  // Don't dispatch "selectionchange" event until inserting all contents.
   5002  SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__);
   5003 
   5004  // Track dropped point with nsRange because we shouldn't insert the
   5005  // dropped content into different position even if some event listeners
   5006  // modify selection.  Note that Chrome's behavior is really odd.  So,
   5007  // we don't need to worry about web-compat about this.
   5008  IgnoredErrorResult ignoredError;
   5009  RefPtr<nsRange> rangeAtDropPoint =
   5010      nsRange::Create(droppedAt.ToRawRangeBoundary(),
   5011                      droppedAt.ToRawRangeBoundary(), ignoredError);
   5012  if (NS_WARN_IF(ignoredError.Failed()) ||
   5013      NS_WARN_IF(!rangeAtDropPoint->IsPositioned())) {
   5014    editActionData.Abort();
   5015    return NS_ERROR_FAILURE;
   5016  }
   5017 
   5018  // Remove selected contents first here because we need to fire a pair of
   5019  // "beforeinput" and "input" for deletion and web apps can cancel only
   5020  // this deletion.  Note that callee may handle insertion asynchronously.
   5021  // Therefore, it is the best to remove selected content here.
   5022  if (editorToDeleteSelection) {
   5023    nsresult rv = editorToDeleteSelection->DeleteSelectionByDragAsAction(
   5024        mDispatchInputEvent);
   5025    if (NS_WARN_IF(Destroyed())) {
   5026      editActionData.Abort();
   5027      return NS_OK;
   5028    }
   5029    // Ignore the editor instance specific error if it's another editor.
   5030    if (this != editorToDeleteSelection &&
   5031        (rv == NS_ERROR_NOT_INITIALIZED || rv == NS_ERROR_EDITOR_DESTROYED)) {
   5032      rv = NS_OK;
   5033    }
   5034    // Don't cancel "insertFromDrop" even if "deleteByDrag" is canceled.
   5035    if (rv != NS_ERROR_EDITOR_ACTION_CANCELED && NS_FAILED(rv)) {
   5036      NS_WARNING("EditorBase::DeleteSelectionByDragAsAction() failed");
   5037      editActionData.Abort();
   5038      return EditorBase::ToGenericNSResult(rv);
   5039    }
   5040    if (NS_WARN_IF(!rangeAtDropPoint->IsPositioned()) ||
   5041        NS_WARN_IF(!rangeAtDropPoint->GetStartContainer()->IsContent())) {
   5042      editActionData.Abort();
   5043      return NS_ERROR_FAILURE;
   5044    }
   5045    droppedAt = rangeAtDropPoint->StartRef();
   5046    MOZ_ASSERT(droppedAt.IsSetAndValid());
   5047    MOZ_ASSERT(droppedAt.IsInContentNode());
   5048  }
   5049 
   5050  // Before inserting dropping content, we need to move focus for compatibility
   5051  // with Chrome and firing "beforeinput" event on new editing host.
   5052  RefPtr<Element> focusedElement, newFocusedElement;
   5053  if (IsTextEditor()) {
   5054    newFocusedElement = GetExposedRoot();
   5055    focusedElement = IsActiveInDOMWindow() ? newFocusedElement : nullptr;
   5056  }
   5057  // TODO: We need to add automated tests when dropping something into an
   5058  //       editing host for contenteditable which is in a shadow DOM tree
   5059  //       and its host which is in design mode.
   5060  else if (!droppedAt.ContainerAs<nsIContent>()->IsInDesignMode()) {
   5061    focusedElement = AsHTMLEditor()->ComputeEditingHost();
   5062    if (focusedElement &&
   5063        droppedAt.ContainerAs<nsIContent>()->IsInclusiveDescendantOf(
   5064            focusedElement)) {
   5065      newFocusedElement = focusedElement;
   5066    } else {
   5067      newFocusedElement = droppedAt.ContainerAs<nsIContent>()->GetEditingHost();
   5068    }
   5069  }
   5070  // Move selection right now.  Note that this does not move focus because
   5071  // `Selection` moves focus with selection change only when the API caller is
   5072  // JS.  And also this does not notify selection listeners (nor
   5073  // "selectionchange") since we created SelectionBatcher above.
   5074  ErrorResult error;
   5075  SelectionRef().SetStartAndEnd(droppedAt.ToRawRangeBoundary(),
   5076                                droppedAt.ToRawRangeBoundary(), error);
   5077  if (error.Failed()) {
   5078    NS_WARNING("Selection::SetStartAndEnd() failed");
   5079    editActionData.Abort();
   5080    return error.StealNSResult();
   5081  }
   5082  if (NS_WARN_IF(Destroyed())) {
   5083    editActionData.Abort();
   5084    return NS_OK;
   5085  }
   5086  // Then, move focus if necessary.  This must cause dispatching "blur" event
   5087  // and "focus" event.
   5088  if (newFocusedElement && focusedElement != newFocusedElement) {
   5089    RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
   5090    DebugOnly<nsresult> rvIgnored = fm->SetFocus(newFocusedElement, 0);
   5091    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   5092                         "nsFocusManager::SetFocus() failed to set focus "
   5093                         "to the element, but ignored");
   5094    if (NS_WARN_IF(Destroyed())) {
   5095      editActionData.Abort();
   5096      return NS_OK;
   5097    }
   5098    // "blur" or "focus" event listener may have changed the value.
   5099    // Let's keep using the original point.
   5100    if (NS_WARN_IF(!rangeAtDropPoint->IsPositioned()) ||
   5101        NS_WARN_IF(!rangeAtDropPoint->GetStartContainer()->IsContent())) {
   5102      return NS_ERROR_FAILURE;
   5103    }
   5104    droppedAt = rangeAtDropPoint->StartRef();
   5105    MOZ_ASSERT(droppedAt.IsSetAndValid());
   5106 
   5107    // If focus is changed to different element and we're handling drop in
   5108    // contenteditable, we cannot handle it without focus.  So, we should give
   5109    // it up.
   5110    if (IsHTMLEditor() && !AsHTMLEditor()->IsInDesignMode() &&
   5111        NS_WARN_IF(newFocusedElement != AsHTMLEditor()->ComputeEditingHost())) {
   5112      editActionData.Abort();
   5113      return NS_OK;
   5114    }
   5115  }
   5116 
   5117  nsresult rv = InsertDroppedDataTransferAsAction(editActionData, *dataTransfer,
   5118                                                  droppedAt, sourcePrincipal);
   5119  if (rv == NS_ERROR_EDITOR_DESTROYED ||
   5120      rv == NS_ERROR_EDITOR_ACTION_CANCELED) {
   5121    return EditorBase::ToGenericNSResult(rv);
   5122  }
   5123  NS_WARNING_ASSERTION(
   5124      NS_SUCCEEDED(rv),
   5125      "EditorBase::InsertDroppedDataTransferAsAction() failed, but ignored");
   5126 
   5127  rv = ScrollSelectionFocusIntoView();
   5128  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5129                       "EditorBase::ScrollSelectionFocusIntoView() failed");
   5130  return rv;
   5131 }
   5132 
   5133 nsresult EditorBase::DeleteSelectionByDragAsAction(bool aDispatchInputEvent) {
   5134  // TODO: Move this method to `EditorBase`.
   5135  AutoRestore<bool> saveDispatchInputEvent(mDispatchInputEvent);
   5136  mDispatchInputEvent = aDispatchInputEvent;
   5137  // Even if we're handling "deleteByDrag" in same editor as "insertFromDrop",
   5138  // we need to recreate edit action data here because
   5139  // `AutoEditActionDataSetter` needs to manage event state separately.
   5140  bool requestedByAnotherEditor = GetEditAction() != EditAction::eDrop;
   5141  AutoEditActionDataSetter editActionData(*this, EditAction::eDeleteByDrag);
   5142  MOZ_ASSERT(!SelectionRef().IsCollapsed());
   5143  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   5144  if (NS_FAILED(rv)) {
   5145    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5146                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   5147    return rv;
   5148  }
   5149  // But keep using placeholder transaction for "insertFromDrop" if there is.
   5150  Maybe<AutoPlaceholderBatch> treatAsOneTransaction;
   5151  if (requestedByAnotherEditor) {
   5152    treatAsOneTransaction.emplace(*this, ScrollSelectionIntoView::Yes,
   5153                                  __FUNCTION__);
   5154  }
   5155 
   5156  // We may need to update the source node to dispatch "dragend" below.
   5157  // Chrome restricts the new target under the <body> here.  Therefore, we
   5158  // should follow it here.
   5159  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/editing/editing_utilities.cc;l=254;drc=da35f4ed6398ae287d5adc828b9546eec95f668a
   5160  const RefPtr<Element> editingHost =
   5161      IsHTMLEditor() ? AsHTMLEditor()->ComputeEditingHost(
   5162                           HTMLEditor::LimitInBodyElement::Yes)
   5163                     : nullptr;
   5164 
   5165  rv = DeleteSelectionAsSubAction(nsIEditor::eNone, IsTextEditor()
   5166                                                        ? nsIEditor::eNoStrip
   5167                                                        : nsIEditor::eStrip);
   5168  if (NS_FAILED(rv)) {
   5169    NS_WARNING("EditorBase::DeleteSelectionAsSubAction(eNone) failed");
   5170    return rv;
   5171  }
   5172 
   5173  if (!mDispatchInputEvent) {
   5174    return NS_OK;
   5175  }
   5176 
   5177  if (treatAsOneTransaction.isNothing()) {
   5178    DispatchInputEvent();
   5179  }
   5180 
   5181  if (NS_WARN_IF(Destroyed())) {
   5182    return NS_ERROR_EDITOR_DESTROYED;
   5183  }
   5184 
   5185  // If we success everything here, we may need to retarget "dragend" event
   5186  // target for compatibility with the other browsers.  They do this only when
   5187  // their builtin editor delete the source node from the document.  Then,
   5188  // they retarget the source node to the editing host.
   5189  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/page/drag_controller.cc;l=724;drc=d9ba13b8cd8ac0faed7afc3d1f7e4b67ebac2a0b
   5190  if (editingHost) {
   5191    RefPtr<nsIWidget> widget = GetWidget();
   5192    if (nsCOMPtr<nsIDragSession> dragSession =
   5193            nsContentUtils::GetDragSession(widget)) {
   5194      dragSession->MaybeEditorDeletedSourceNode(editingHost);
   5195    }
   5196  }
   5197  return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
   5198 }
   5199 
   5200 Result<CaretPoint, nsresult> EditorBase::DeleteRangeWithTransaction(
   5201    nsIEditor::EDirection aDirectionAndAmount,
   5202    nsIEditor::EStripWrappers aStripWrappers, nsRange& aRangeToDelete) {
   5203  MOZ_ASSERT(IsEditActionDataAvailable());
   5204  MOZ_ASSERT(!Destroyed());
   5205  MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   5206 
   5207  HowToHandleCollapsedRange howToHandleCollapsedRange =
   5208      EditorBase::HowToHandleCollapsedRangeFor(aDirectionAndAmount);
   5209  if (MOZ_UNLIKELY(aRangeToDelete.Collapsed() &&
   5210                   howToHandleCollapsedRange ==
   5211                       HowToHandleCollapsedRange::Ignore)) {
   5212    return CaretPoint(EditorDOMPoint(aRangeToDelete.StartRef()));
   5213  }
   5214 
   5215  AutoClonedRangeArray rangesToDelete(aRangeToDelete);
   5216  Result<CaretPoint, nsresult> result = DeleteRangesWithTransaction(
   5217      aDirectionAndAmount, aStripWrappers, rangesToDelete);
   5218  NS_WARNING_ASSERTION(result.isOk(),
   5219                       "EditorBase::DeleteRangesWithTransaction() failed");
   5220  return result;
   5221 }
   5222 
   5223 Result<CaretPoint, nsresult> EditorBase::DeleteRangesWithTransaction(
   5224    nsIEditor::EDirection aDirectionAndAmount,
   5225    nsIEditor::EStripWrappers aStripWrappers,
   5226    AutoClonedRangeArray& aRangesToDelete) {
   5227  MOZ_ASSERT(IsEditActionDataAvailable());
   5228  MOZ_ASSERT(!Destroyed());
   5229  MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   5230  MOZ_ASSERT(!aRangesToDelete.Ranges().IsEmpty());
   5231 
   5232  HowToHandleCollapsedRange howToHandleCollapsedRange =
   5233      EditorBase::HowToHandleCollapsedRangeFor(aDirectionAndAmount);
   5234  if (NS_WARN_IF(aRangesToDelete.IsCollapsed() &&
   5235                 howToHandleCollapsedRange ==
   5236                     HowToHandleCollapsedRange::Ignore)) {
   5237    NS_ASSERTION(
   5238        false,
   5239        "For avoiding to throw incompatible exception for `execCommand`, fix "
   5240        "the caller");
   5241    return Err(NS_ERROR_FAILURE);
   5242  }
   5243 
   5244  RefPtr<DeleteMultipleRangesTransaction> deleteSelectionTransaction =
   5245      CreateTransactionForDeleteSelection(howToHandleCollapsedRange,
   5246                                          aRangesToDelete);
   5247  if (MOZ_UNLIKELY(!deleteSelectionTransaction)) {
   5248    NS_WARNING("EditorBase::CreateTransactionForDeleteSelection() failed");
   5249    return Err(NS_ERROR_FAILURE);
   5250  }
   5251 
   5252  // XXX This is odd, this assumes that there are no multiple collapsed
   5253  //     ranges in `Selection`, but it's possible scenario.
   5254  // XXX This loop looks slow, but it's rarely so because of multiple
   5255  //     selection is not used so many times.
   5256  nsCOMPtr<nsIContent> deleteContent;
   5257  uint32_t deleteCharOffset = 0;
   5258  for (const OwningNonNull<EditTransactionBase>& transactionBase :
   5259       Reversed(deleteSelectionTransaction->ChildTransactions())) {
   5260    if (DeleteTextTransaction* deleteTextTransaction =
   5261            transactionBase->GetAsDeleteTextTransaction()) {
   5262      deleteContent = deleteTextTransaction->GetTextNode();
   5263      deleteCharOffset = deleteTextTransaction->Offset();
   5264      break;
   5265    }
   5266    if (DeleteNodeTransaction* deleteNodeTransaction =
   5267            transactionBase->GetAsDeleteNodeTransaction()) {
   5268      deleteContent = deleteNodeTransaction->GetContent();
   5269      break;
   5270    }
   5271  }
   5272 
   5273  RefPtr<CharacterData> deleteCharData =
   5274      CharacterData::FromNodeOrNull(deleteContent);
   5275  IgnoredErrorResult ignoredError;
   5276  AutoEditSubActionNotifier startToHandleEditSubAction(
   5277      *this, EditSubAction::eDeleteSelectedContent, aDirectionAndAmount,
   5278      ignoredError);
   5279  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   5280    return Err(ignoredError.StealNSResult());
   5281  }
   5282  NS_WARNING_ASSERTION(
   5283      !ignoredError.Failed(),
   5284      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   5285 
   5286  if (IsHTMLEditor()) {
   5287    if (!deleteContent) {
   5288      // XXX We may remove multiple ranges in the following.  Therefore,
   5289      //     this must have a bug since we only add the first range into
   5290      //     the changed range.
   5291      TopLevelEditSubActionDataRef().WillDeleteRange(
   5292          *this, aRangesToDelete.GetFirstRangeStartPoint<EditorRawDOMPoint>(),
   5293          aRangesToDelete.GetFirstRangeEndPoint<EditorRawDOMPoint>());
   5294    } else if (!deleteCharData) {
   5295      TopLevelEditSubActionDataRef().WillDeleteContent(*this, *deleteContent);
   5296    }
   5297  }
   5298 
   5299  // Notify nsIEditActionListener::WillDelete[Selection|Text]
   5300  if (!mActionListeners.IsEmpty()) {
   5301    if (!deleteContent) {
   5302      MOZ_ASSERT(!aRangesToDelete.Ranges().IsEmpty());
   5303      AutoTArray<RefPtr<nsRange>, 8> rangesToDelete(
   5304          aRangesToDelete.CloneRanges<RefPtr>());
   5305      AutoActionListenerArray listeners(mActionListeners.Clone());
   5306      for (auto& listener : listeners) {
   5307        DebugOnly<nsresult> rvIgnored =
   5308            listener->WillDeleteRanges(rangesToDelete);
   5309        NS_WARNING_ASSERTION(
   5310            NS_SUCCEEDED(rvIgnored),
   5311            "nsIEditActionListener::WillDeleteRanges() failed, but ignored");
   5312        MOZ_DIAGNOSTIC_ASSERT(!Destroyed(),
   5313                              "nsIEditActionListener::WillDeleteRanges() "
   5314                              "must not destroy the editor");
   5315      }
   5316    } else if (deleteCharData) {
   5317      AutoActionListenerArray listeners(mActionListeners.Clone());
   5318      for (auto& listener : listeners) {
   5319        // XXX Why don't we notify listeners of actual length?
   5320        DebugOnly<nsresult> rvIgnored =
   5321            listener->WillDeleteText(deleteCharData, deleteCharOffset, 1);
   5322        NS_WARNING_ASSERTION(
   5323            NS_SUCCEEDED(rvIgnored),
   5324            "nsIEditActionListener::WillDeleteText() failed, but ignored");
   5325        MOZ_DIAGNOSTIC_ASSERT(!Destroyed(),
   5326                              "nsIEditActionListener::WillDeleteText() must "
   5327                              "not destroy the editor");
   5328      }
   5329    }
   5330  }
   5331 
   5332  // Delete the specified amount
   5333  nsresult rv = DoTransactionInternal(deleteSelectionTransaction);
   5334  // I'm not sure whether we should keep notifying edit action listeners or
   5335  // stop doing it.  For now, just keep traditional behavior.
   5336  bool destroyedByTransaction = Destroyed();
   5337  NS_WARNING_ASSERTION(destroyedByTransaction || NS_SUCCEEDED(rv),
   5338                       "EditorBase::DoTransactionInternal() failed");
   5339 
   5340  if (IsHTMLEditor() && deleteCharData) {
   5341    MOZ_ASSERT(deleteContent);
   5342    TopLevelEditSubActionDataRef().DidDeleteText(
   5343        *this, EditorRawDOMPoint(deleteContent));
   5344  }
   5345 
   5346  if (mTextServicesDocument && NS_SUCCEEDED(rv) && deleteContent &&
   5347      !deleteCharData) {
   5348    RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
   5349    textServicesDocument->DidDeleteContent(*deleteContent);
   5350    MOZ_ASSERT(
   5351        destroyedByTransaction || !Destroyed(),
   5352        "TextServicesDocument::DidDeleteContent() must not destroy the editor");
   5353  }
   5354 
   5355  if (!mActionListeners.IsEmpty() && deleteContent && !deleteCharData) {
   5356    for (auto& listener : mActionListeners.Clone()) {
   5357      DebugOnly<nsresult> rvIgnored =
   5358          listener->DidDeleteNode(deleteContent, rv);
   5359      NS_WARNING_ASSERTION(
   5360          NS_SUCCEEDED(rvIgnored),
   5361          "nsIEditActionListener::DidDeleteNode() failed, but ignored");
   5362      MOZ_DIAGNOSTIC_ASSERT(
   5363          destroyedByTransaction || !Destroyed(),
   5364          "nsIEditActionListener::DidDeleteNode() must not destroy the editor");
   5365    }
   5366  }
   5367 
   5368  if (NS_WARN_IF(destroyedByTransaction)) {
   5369    return Err(NS_ERROR_EDITOR_DESTROYED);
   5370  }
   5371  if (NS_FAILED(rv)) {
   5372    return Err(rv);
   5373  }
   5374 
   5375  return CaretPoint(deleteSelectionTransaction->SuggestPointToPutCaret());
   5376 }
   5377 
   5378 already_AddRefed<Element> EditorBase::CreateHTMLContent(
   5379    const nsAtom* aTag) const {
   5380  MOZ_ASSERT(aTag);
   5381 
   5382  RefPtr<Document> document = GetDocument();
   5383  if (NS_WARN_IF(!document)) {
   5384    return nullptr;
   5385  }
   5386 
   5387  // XXX Wallpaper over editor bug (editor tries to create elements with an
   5388  //     empty nodename).
   5389  if (aTag == nsGkAtoms::_empty) {
   5390    NS_ERROR(
   5391        "Don't pass an empty tag to EditorBase::CreateHTMLContent, "
   5392        "check caller.");
   5393    return nullptr;
   5394  }
   5395 
   5396  return document->CreateElem(nsDependentAtomString(aTag), nullptr,
   5397                              kNameSpaceID_XHTML);
   5398 }
   5399 
   5400 already_AddRefed<nsTextNode> EditorBase::CreateTextNode(
   5401    const nsAString& aData) const {
   5402  MOZ_ASSERT(IsEditActionDataAvailable());
   5403 
   5404  Document* document = GetDocument();
   5405  if (NS_WARN_IF(!document)) {
   5406    return nullptr;
   5407  }
   5408  RefPtr<nsTextNode> text = document->CreateEmptyTextNode();
   5409  text->MarkAsMaybeModifiedFrequently();
   5410  if (IsPasswordEditor()) {
   5411    text->MarkAsMaybeMasked();
   5412  }
   5413  // Don't notify; this node is still being created.
   5414  DebugOnly<nsresult> rvIgnored = text->SetText(aData, false);
   5415  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   5416                       "Text::SetText() failed, but ignored");
   5417  return text.forget();
   5418 }
   5419 
   5420 NS_IMETHODIMP EditorBase::SetAttributeOrEquivalent(Element* aElement,
   5421                                                   const nsAString& aAttribute,
   5422                                                   const nsAString& aValue,
   5423                                                   bool aSuppressTransaction) {
   5424  if (NS_WARN_IF(!aElement)) {
   5425    return NS_ERROR_NULL_POINTER;
   5426  }
   5427 
   5428  AutoEditActionDataSetter editActionData(*this, EditAction::eSetAttribute);
   5429  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   5430  if (NS_FAILED(rv)) {
   5431    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5432                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   5433    return EditorBase::ToGenericNSResult(rv);
   5434  }
   5435 
   5436  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
   5437  rv = SetAttributeOrEquivalent(aElement, attribute, aValue,
   5438                                aSuppressTransaction);
   5439  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5440                       "EditorBase::SetAttributeOrEquivalent() failed");
   5441  return EditorBase::ToGenericNSResult(rv);
   5442 }
   5443 
   5444 NS_IMETHODIMP EditorBase::RemoveAttributeOrEquivalent(
   5445    Element* aElement, const nsAString& aAttribute, bool aSuppressTransaction) {
   5446  if (NS_WARN_IF(!aElement)) {
   5447    return NS_ERROR_NULL_POINTER;
   5448  }
   5449 
   5450  AutoEditActionDataSetter editActionData(*this, EditAction::eRemoveAttribute);
   5451  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   5452  if (NS_FAILED(rv)) {
   5453    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5454                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   5455    return EditorBase::ToGenericNSResult(rv);
   5456  }
   5457 
   5458  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
   5459  rv = RemoveAttributeOrEquivalent(aElement, attribute, aSuppressTransaction);
   5460  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5461                       "EditorBase::RemoveAttributeOrEquivalent() failed");
   5462  return EditorBase::ToGenericNSResult(rv);
   5463 }
   5464 
   5465 void EditorBase::HandleKeyPressEventInReadOnlyMode(
   5466    WidgetKeyboardEvent& aKeyboardEvent) const {
   5467  MOZ_ASSERT(IsReadonly());
   5468  MOZ_ASSERT(aKeyboardEvent.mMessage == eKeyPress);
   5469 
   5470  switch (aKeyboardEvent.mKeyCode) {
   5471    case NS_VK_BACK:
   5472      // If it's a `Backspace` key, let's consume it because it may be mapped
   5473      // to "Back" of the history navigation.  So, it's possible that user
   5474      // tries to delete a character with `Backspace` even in the read-only
   5475      // editor.
   5476      aKeyboardEvent.PreventDefault();
   5477      break;
   5478  }
   5479  // XXX How about space key (page up and page down in browser navigation)?
   5480 }
   5481 
   5482 nsresult EditorBase::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
   5483  MOZ_ASSERT(!IsReadonly());
   5484  MOZ_ASSERT(aKeyboardEvent);
   5485  MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress);
   5486 
   5487  // NOTE: When you change this method, you should also change:
   5488  //   * editor/libeditor/tests/test_texteditor_keyevent_handling.html
   5489  //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
   5490  //
   5491  // And also when you add new key handling, you need to change the subclass's
   5492  // HandleKeyPressEvent()'s switch statement.
   5493 
   5494  switch (aKeyboardEvent->mKeyCode) {
   5495    case NS_VK_META:
   5496    case NS_VK_WIN:
   5497    case NS_VK_SHIFT:
   5498    case NS_VK_CONTROL:
   5499    case NS_VK_ALT:
   5500      MOZ_ASSERT_UNREACHABLE(
   5501          "eKeyPress event shouldn't be fired for modifier keys");
   5502      return NS_ERROR_UNEXPECTED;
   5503 
   5504    case NS_VK_BACK: {
   5505      if (aKeyboardEvent->IsControl() || aKeyboardEvent->IsAlt() ||
   5506          aKeyboardEvent->IsMeta()) {
   5507        return NS_OK;
   5508      }
   5509      DebugOnly<nsresult> rvIgnored =
   5510          DeleteSelectionAsAction(nsIEditor::ePrevious, nsIEditor::eStrip);
   5511      aKeyboardEvent->PreventDefault();
   5512      NS_WARNING_ASSERTION(
   5513          NS_SUCCEEDED(rvIgnored),
   5514          "EditorBase::DeleteSelectionAsAction() failed, but ignored");
   5515      return NS_OK;
   5516    }
   5517    case NS_VK_DELETE: {
   5518      // on certain platforms (such as windows) the shift key
   5519      // modifies what delete does (cmd_cut in this case).
   5520      // bailing here to allow the keybindings to do the cut.
   5521      if (aKeyboardEvent->IsShift() || aKeyboardEvent->IsControl() ||
   5522          aKeyboardEvent->IsAlt() || aKeyboardEvent->IsMeta()) {
   5523        return NS_OK;
   5524      }
   5525      DebugOnly<nsresult> rvIgnored =
   5526          DeleteSelectionAsAction(nsIEditor::eNext, nsIEditor::eStrip);
   5527      aKeyboardEvent->PreventDefault();
   5528      NS_WARNING_ASSERTION(
   5529          NS_SUCCEEDED(rvIgnored),
   5530          "EditorBase::DeleteSelectionAsAction() failed, but ignored");
   5531      return NS_OK;
   5532    }
   5533  }
   5534  return NS_OK;
   5535 }
   5536 
   5537 nsresult EditorBase::OnInputText(const nsAString& aStringToInsert) {
   5538  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertText);
   5539  MOZ_ASSERT(!aStringToInsert.IsVoid());
   5540 
   5541  MOZ_LOG(gTextInputLog, LogLevel::Info,
   5542          ("%p %s::OnInputText(aStringToInsert=\"%s\")", this,
   5543           mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   5544           NS_ConvertUTF16toUTF8(aStringToInsert).get()));
   5545 
   5546  editActionData.SetData(aStringToInsert);
   5547  // FYI: For conforming to current UI Events spec, we should dispatch
   5548  //      "beforeinput" event before "keypress" event, but here is in a
   5549  //      "keypress" event listener.  However, the other browsers dispatch
   5550  //      "beforeinput" event after "keypress" event.  Therefore, it makes
   5551  //      sense to follow the other browsers.  Spec issue:
   5552  //      https://github.com/w3c/uievents/issues/220
   5553  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   5554  if (NS_FAILED(rv)) {
   5555    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5556                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   5557    return EditorBase::ToGenericNSResult(rv);
   5558  }
   5559 
   5560  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName,
   5561                                             ScrollSelectionIntoView::Yes,
   5562                                             __FUNCTION__);
   5563  rv = InsertTextAsSubAction(aStringToInsert, InsertTextFor::NormalText);
   5564  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5565                       "EditorBase::InsertTextAsSubAction() failed");
   5566  return EditorBase::ToGenericNSResult(rv);
   5567 }
   5568 
   5569 nsresult EditorBase::ReplaceTextAsAction(
   5570    const nsAString& aString, nsRange* aReplaceRange,
   5571    AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable,
   5572    PreventSetSelection aPreventSetSelection, nsIPrincipal* aPrincipal) {
   5573  MOZ_ASSERT(aString.FindChar(nsCRT::CR) == kNotFound);
   5574  MOZ_ASSERT_IF(!aReplaceRange, IsTextEditor());
   5575 
   5576  AutoEditActionDataSetter editActionData(*this, EditAction::eReplaceText,
   5577                                          aPrincipal);
   5578  if (NS_WARN_IF(!editActionData.CanHandle())) {
   5579    return NS_ERROR_NOT_INITIALIZED;
   5580  }
   5581  if (aAllowBeforeInputEventCancelable == AllowBeforeInputEventCancelable::No) {
   5582    editActionData.MakeBeforeInputEventNonCancelable();
   5583  }
   5584 
   5585  RefPtr<nsRange> targetRange = [&]() -> already_AddRefed<nsRange> {
   5586    if (aReplaceRange) {
   5587      RefPtr<nsRange> range = nsRange::Create(
   5588          aReplaceRange->GetStartContainer(), aReplaceRange->StartOffset(),
   5589          aReplaceRange->GetEndContainer(), aReplaceRange->EndOffset(),
   5590          IgnoreErrors());
   5591      NS_WARNING_ASSERTION(range && range->IsPositioned(),
   5592                           "nsRange::Create() failed");
   5593      return range.forget();
   5594    }
   5595    nsIContent* const rootContentToSelectAll =
   5596        IsTextEditor()
   5597            ? AsTextEditor()->GetTextNode()
   5598            : static_cast<nsIContent*>(AsHTMLEditor()->ComputeEditingHost());
   5599    if (NS_WARN_IF(!rootContentToSelectAll)) {
   5600      return nullptr;
   5601    }
   5602    RefPtr<nsRange> range =
   5603        nsRange::Create(rootContentToSelectAll, 0, rootContentToSelectAll,
   5604                        rootContentToSelectAll->Length(), IgnoreErrors());
   5605    NS_WARNING_ASSERTION(range && range->IsPositioned(),
   5606                         "nsRange::Create() failed");
   5607    return range.forget();
   5608  }();
   5609  if (NS_WARN_IF(!targetRange) || NS_WARN_IF(!targetRange->IsPositioned())) {
   5610    return NS_ERROR_FAILURE;
   5611  }
   5612  if (IsTextEditor()) {
   5613    editActionData.SetData(aString);
   5614  } else {
   5615    editActionData.InitializeDataTransfer(aString);
   5616    RefPtr<StaticRange> staticTargetRange = StaticRange::Create(
   5617        targetRange->StartRef(), targetRange->EndRef(), IgnoreErrors());
   5618    MOZ_ASSERT(staticTargetRange);
   5619    MOZ_ASSERT(staticTargetRange->IsPositioned());
   5620    editActionData.AppendTargetRange(std::move(staticTargetRange));
   5621  }
   5622 
   5623  AutoSelectionRestorer restorer(
   5624      aPreventSetSelection == PreventSetSelection::Yes ? this : nullptr);
   5625  nsresult rv = NS_OK;
   5626  auto raii = MakeScopeExit([&] {
   5627    if (aPreventSetSelection == PreventSetSelection::Yes && NS_FAILED(rv)) {
   5628      restorer.Abort();
   5629    }
   5630  });
   5631 
   5632  // Before dispatching eEditorBeforeInput, we should set `Selection` as the
   5633  // target range.  Then, we can expose the target range with
   5634  // .selectionStart and .selectionEnd, etc even on TextEditor too.
   5635  if (SelectionRef().RangeCount() != 1u ||
   5636      !targetRange->HasEqualBoundaries(*SelectionRef().GetRangeAt(0u))) {
   5637    IgnoredErrorResult error;
   5638    SelectionRef().RemoveAllRanges(error);
   5639    if (MOZ_UNLIKELY(error.Failed())) {
   5640      NS_WARNING("Selection::RemoveAllRanges() failed");
   5641      rv = error.StealNSResult();  // rv is used by `raii`.
   5642      return rv;
   5643    }
   5644    SelectionRef().AddRangeAndSelectFramesAndNotifyListeners(*targetRange,
   5645                                                             error);
   5646    if (MOZ_UNLIKELY(error.Failed())) {
   5647      NS_WARNING(
   5648          "Selection::AddRangeAndSelectFramesAndNotifyListeners() failed");
   5649      rv = error.StealNSResult();  // rv is used by `raii`.
   5650      return rv;
   5651    }
   5652  }
   5653 
   5654  rv = editActionData.MaybeDispatchBeforeInputEvent();
   5655  if (NS_FAILED(rv)) {
   5656    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5657                         "MaybeDispatchBeforeInputEvent() failed");
   5658    return EditorBase::ToGenericNSResult(rv);
   5659  }
   5660 
   5661  // If a `beforeinput` event listener changed the `Selection`, we should should
   5662  // not restore the original one because restoring Selection may confuse the
   5663  // web app.
   5664  if (SelectionRef().RangeCount() != 1u ||
   5665      !targetRange->HasEqualBoundaries(*SelectionRef().GetRangeAt(0u))) {
   5666    restorer.Abort();
   5667  }
   5668 
   5669  AutoPlaceholderBatch treatAsOneTransaction(
   5670      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   5671 
   5672  // This should emulates inserting text for better undo/redo behavior.
   5673  IgnoredErrorResult ignoredError;
   5674  AutoEditSubActionNotifier startToHandleEditSubAction(
   5675      *this, EditSubAction::eInsertText, nsIEditor::eNext, ignoredError);
   5676  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   5677    rv = NS_ERROR_EDITOR_DESTROYED;  // rv is used by `raii`.
   5678    return EditorBase::ToGenericNSResult(rv);
   5679  }
   5680  NS_WARNING_ASSERTION(
   5681      !ignoredError.Failed(),
   5682      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   5683 
   5684  if (!aReplaceRange) {
   5685    // Use fast path if we're `TextEditor` because it may be in a hot path.
   5686    if (IsTextEditor()) {
   5687      restorer.Abort();  // XXX Is this intended?
   5688      nsresult rv = MOZ_KnownLive(AsTextEditor())->SetTextAsSubAction(aString);
   5689      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5690                           "TextEditor::SetTextAsSubAction() failed");
   5691      return EditorBase::ToGenericNSResult(rv);
   5692    }
   5693 
   5694    MOZ_ASSERT_UNREACHABLE("Setting value of `HTMLEditor` isn't supported");
   5695    rv = NS_ERROR_FAILURE;  // rv is used by `raii`.
   5696    return EditorBase::ToGenericNSResult(rv);
   5697  }
   5698 
   5699  if (aString.IsEmpty() && aReplaceRange->Collapsed()) {
   5700    restorer.Abort();  // XXX Is this intended?
   5701 
   5702    NS_WARNING("Setting value was empty and replaced range was empty");
   5703    return NS_OK;
   5704  }
   5705 
   5706  rv = ReplaceSelectionAsSubAction(aString);
   5707  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5708                       "EditorBase::ReplaceSelectionAsSubAction() failed");
   5709 
   5710  return EditorBase::ToGenericNSResult(rv);
   5711 }
   5712 
   5713 nsresult EditorBase::ReplaceSelectionAsSubAction(const nsAString& aString) {
   5714  if (aString.IsEmpty()) {
   5715    nsresult rv = DeleteSelectionAsSubAction(
   5716        nsIEditor::eNone,
   5717        IsTextEditor() ? nsIEditor::eNoStrip : nsIEditor::eStrip);
   5718    NS_WARNING_ASSERTION(
   5719        NS_SUCCEEDED(rv),
   5720        "EditorBase::DeleteSelectionAsSubAction(eNone) failed");
   5721    return rv;
   5722  }
   5723 
   5724  nsresult rv = InsertTextAsSubAction(aString, InsertTextFor::NormalText);
   5725  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5726                       "EditorBase::InsertTextAsSubAction() failed");
   5727  return rv;
   5728 }
   5729 
   5730 nsresult EditorBase::HandleInlineSpellCheck(
   5731    const EditorDOMPoint& aPreviouslySelectedStart,
   5732    const AbstractRange* aRange) {
   5733  MOZ_ASSERT(IsEditActionDataAvailable());
   5734 
   5735  if (!mInlineSpellChecker) {
   5736    return NS_OK;
   5737  }
   5738  nsresult rv = mInlineSpellChecker->SpellCheckAfterEditorChange(
   5739      GetTopLevelEditSubAction(), SelectionRef(),
   5740      aPreviouslySelectedStart.GetContainer(),
   5741      aPreviouslySelectedStart.Offset(),
   5742      aRange ? aRange->GetStartContainer() : nullptr,
   5743      aRange ? aRange->StartOffset() : 0,
   5744      aRange ? aRange->GetEndContainer() : nullptr,
   5745      aRange ? aRange->EndOffset() : 0);
   5746  NS_WARNING_ASSERTION(
   5747      NS_SUCCEEDED(rv),
   5748      "mozInlineSpellChecker::SpellCheckAfterEditorChange() failed");
   5749  return rv;
   5750 }
   5751 
   5752 Element* EditorBase::FindSelectionRoot(const nsINode& aNode) const {
   5753  return GetRoot();
   5754 }
   5755 
   5756 void EditorBase::InitializeSelectionAncestorLimit(
   5757    Element& aAncestorLimit) const {
   5758  MOZ_ASSERT(IsEditActionDataAvailable());
   5759 
   5760  MOZ_KnownLive(SelectionRef()).SetAncestorLimiter(&aAncestorLimit);
   5761 }
   5762 
   5763 nsresult EditorBase::InitializeSelection(
   5764    const nsINode& aOriginalEventTargetNode) {
   5765  MOZ_ASSERT(IsEditActionDataAvailable());
   5766 
   5767  const RefPtr<Element> selectionRootContent =
   5768      FindSelectionRoot(aOriginalEventTargetNode);
   5769  if (!selectionRootContent) {
   5770    return NS_OK;
   5771  }
   5772 
   5773  nsCOMPtr<nsISelectionController> selectionController =
   5774      GetSelectionController();
   5775  if (NS_WARN_IF(!selectionController)) {
   5776    return NS_ERROR_FAILURE;
   5777  }
   5778 
   5779  // Init the caret
   5780  RefPtr<nsCaret> caret = GetCaret();
   5781  if (NS_WARN_IF(!caret)) {
   5782    return NS_ERROR_FAILURE;
   5783  }
   5784  caret->SetSelection(&SelectionRef());
   5785  DebugOnly<nsresult> rvIgnored =
   5786      selectionController->SetCaretReadOnly(IsReadonly());
   5787  NS_WARNING_ASSERTION(
   5788      NS_SUCCEEDED(rvIgnored),
   5789      "nsISelectionController::SetCaretReadOnly() failed, but ignored");
   5790  rvIgnored = selectionController->SetCaretEnabled(true);
   5791  NS_WARNING_ASSERTION(
   5792      NS_SUCCEEDED(rvIgnored),
   5793      "nsISelectionController::SetCaretEnabled() failed, but ignored");
   5794  // Init selection
   5795  rvIgnored =
   5796      selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
   5797  NS_WARNING_ASSERTION(
   5798      NS_SUCCEEDED(rvIgnored),
   5799      "nsISelectionController::SetSelectionFlags() failed, but ignored");
   5800 
   5801  selectionController->SelectionWillTakeFocus();
   5802 
   5803  // If the computed selection root isn't root content, we should set it
   5804  // as selection ancestor limit.  However, if that is root element, it means
   5805  // there is not limitation of the selection, then, we must set nullptr.
   5806  // NOTE: If we set a root element to the ancestor limit, some selection
   5807  // methods don't work fine.
   5808  if (selectionRootContent->GetParent()) {
   5809    InitializeSelectionAncestorLimit(*selectionRootContent);
   5810  } else {
   5811    SelectionRef().SetAncestorLimiter(nullptr);
   5812  }
   5813 
   5814  // If there is composition in a text control when this method is called, we
   5815  // may need to restore IME selection because if the text control is reframed,
   5816  // this already forgot IME selection and the transaction.
   5817  // Note that if this is an HTMLEditor, updating composition makes the new
   5818  // composition string appear around IME or normal selection.  Therefore,
   5819  // we don't need to do nothing here.
   5820  if (IsTextEditor() && mComposition && mComposition->IsMovingToNewTextNode()) {
   5821    // We need to look for the new text node from current selection.
   5822    // XXX If selection is changed during reframe, this doesn't work well!
   5823    const auto atStartOfFirstRange =
   5824        EditorBase::GetFirstSelectionStartPoint<EditorRawDOMPoint>();
   5825    EditorRawDOMPoint betterInsertionPoint =
   5826        AsTextEditor()->FindBetterInsertionPoint(atStartOfFirstRange);
   5827    RefPtr<Text> textNode = betterInsertionPoint.GetContainerAs<Text>();
   5828    MOZ_ASSERT(textNode,
   5829               "There must be text node if composition string is not empty");
   5830    if (textNode) {
   5831      MOZ_ASSERT(textNode->Length() >= mComposition->XPEndOffsetInTextNode(),
   5832                 "The text node must be different from the old text node");
   5833      RefPtr<TextRangeArray> ranges = mComposition->GetRanges();
   5834      DebugOnly<nsresult> rvIgnored = CompositionTransaction::SetIMESelection(
   5835          *this, textNode, mComposition->XPOffsetInTextNode(),
   5836          mComposition->XPLengthInTextNode(), ranges);
   5837      NS_WARNING_ASSERTION(
   5838          NS_SUCCEEDED(rvIgnored),
   5839          "CompositionTransaction::SetIMESelection() failed, but ignored");
   5840      mComposition->OnUpdateCompositionInEditor(
   5841          mComposition->String(), *textNode,
   5842          mComposition->XPOffsetInTextNode());
   5843    }
   5844  }
   5845 
   5846  return NS_OK;
   5847 }
   5848 
   5849 nsresult EditorBase::FinalizeSelection() {
   5850  nsCOMPtr<nsISelectionController> selectionController =
   5851      GetSelectionController();
   5852  if (NS_WARN_IF(!selectionController)) {
   5853    return NS_ERROR_FAILURE;
   5854  }
   5855 
   5856  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   5857  if (NS_WARN_IF(!editActionData.CanHandle())) {
   5858    return NS_ERROR_NOT_INITIALIZED;
   5859  }
   5860 
   5861  SelectionRef().SetAncestorLimiter(nullptr);
   5862 
   5863  if (NS_WARN_IF(!GetPresShell())) {
   5864    return NS_ERROR_NOT_INITIALIZED;
   5865  }
   5866 
   5867  if (RefPtr<nsCaret> caret = GetCaret()) {
   5868    DebugOnly<nsresult> rvIgnored = selectionController->SetCaretEnabled(false);
   5869    NS_WARNING_ASSERTION(
   5870        NS_SUCCEEDED(rvIgnored),
   5871        "nsISelectionController::SetCaretEnabled(false) failed, but ignored");
   5872  }
   5873 
   5874  RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
   5875  if (NS_WARN_IF(!focusManager)) {
   5876    return NS_ERROR_NOT_INITIALIZED;
   5877  }
   5878  // TODO: Running script from here makes harder to handle blur events.  We
   5879  //       should do this asynchronously.
   5880  focusManager->UpdateCaretForCaretBrowsingMode();
   5881  if (Element* rootElement = GetExposedRoot()) {
   5882    if (rootElement->OwnerDoc()->GetUnretargetedFocusedContent() !=
   5883        rootElement) {
   5884      selectionController->SelectionWillLoseFocus();
   5885    } else {
   5886      // We leave this selection as the focused one. When the focus returns, it
   5887      // either returns to us (nothing to do), or it returns to something else,
   5888      // and nsDocumentViewerFocusListener::HandleEvent fixes it up.
   5889    }
   5890  }
   5891  return NS_OK;
   5892 }
   5893 
   5894 Element* EditorBase::GetExposedRoot() const {
   5895  Element* rootElement = GetRoot();
   5896  if (!rootElement || !rootElement->IsInNativeAnonymousSubtree()) {
   5897    return rootElement;
   5898  }
   5899  return Element::FromNodeOrNull(
   5900      rootElement->GetClosestNativeAnonymousSubtreeRootParentOrHost());
   5901 }
   5902 
   5903 nsresult EditorBase::DetermineCurrentDirection() {
   5904  // Get the current root direction from its frame
   5905  Element* rootElement = GetExposedRoot();
   5906  if (NS_WARN_IF(!rootElement)) {
   5907    return NS_ERROR_FAILURE;
   5908  }
   5909 
   5910  // If we don't have an explicit direction, determine our direction
   5911  // from the content's direction
   5912  if (!IsRightToLeft() && !IsLeftToRight()) {
   5913    nsIFrame* frameForRootElement = rootElement->GetPrimaryFrame();
   5914    if (NS_WARN_IF(!frameForRootElement)) {
   5915      return NS_ERROR_FAILURE;
   5916    }
   5917 
   5918    // Set the flag here, to enable us to use the same code path below.
   5919    // It will be flipped before returning from the function.
   5920    if (frameForRootElement->StyleVisibility()->mDirection ==
   5921        StyleDirection::Rtl) {
   5922      mFlags |= nsIEditor::eEditorRightToLeft;
   5923    } else {
   5924      mFlags |= nsIEditor::eEditorLeftToRight;
   5925    }
   5926  }
   5927 
   5928  return NS_OK;
   5929 }
   5930 
   5931 nsresult EditorBase::ToggleTextDirectionAsAction(nsIPrincipal* aPrincipal) {
   5932  AutoEditActionDataSetter editActionData(*this, EditAction::eSetTextDirection,
   5933                                          aPrincipal);
   5934  if (NS_WARN_IF(!editActionData.CanHandle())) {
   5935    return NS_ERROR_NOT_INITIALIZED;
   5936  }
   5937 
   5938  nsresult rv = DetermineCurrentDirection();
   5939  if (NS_FAILED(rv)) {
   5940    NS_WARNING("EditorBase::DetermineCurrentDirection() failed");
   5941    return EditorBase::ToGenericNSResult(rv);
   5942  }
   5943 
   5944  MOZ_ASSERT(IsRightToLeft() || IsLeftToRight());
   5945  // Note that we need to consider new direction before dispatching
   5946  // "beforeinput" event since "beforeinput" event listener may change it
   5947  // but not canceled.
   5948  TextDirection newDirection =
   5949      IsRightToLeft() ? TextDirection::eLTR : TextDirection::eRTL;
   5950  editActionData.SetData(IsRightToLeft() ? u"ltr"_ns : u"rtl"_ns);
   5951 
   5952  // FYI: Oddly, Chrome does not dispatch beforeinput event in this case but
   5953  //      dispatches input event.
   5954  rv = editActionData.MaybeDispatchBeforeInputEvent();
   5955  if (NS_FAILED(rv)) {
   5956    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5957                         "MaybeDispatchBeforeInputEvent() failed");
   5958    return EditorBase::ToGenericNSResult(rv);
   5959  }
   5960 
   5961  rv = SetTextDirectionTo(newDirection);
   5962  if (NS_FAILED(rv)) {
   5963    NS_WARNING("EditorBase::SetTextDirectionTo() failed");
   5964    return EditorBase::ToGenericNSResult(rv);
   5965  }
   5966 
   5967  editActionData.MarkAsHandled();
   5968 
   5969  // XXX When we don't change the text direction, do we really need to
   5970  //     dispatch input event?
   5971  DispatchInputEvent();
   5972 
   5973  return NS_OK;
   5974 }
   5975 
   5976 void EditorBase::SwitchTextDirectionTo(TextDirection aTextDirection) {
   5977  MOZ_ASSERT(aTextDirection == TextDirection::eLTR ||
   5978             aTextDirection == TextDirection::eRTL);
   5979 
   5980  AutoEditActionDataSetter editActionData(*this, EditAction::eSetTextDirection);
   5981  if (NS_WARN_IF(!editActionData.CanHandle())) {
   5982    return;
   5983  }
   5984 
   5985  nsresult rv = DetermineCurrentDirection();
   5986  if (NS_WARN_IF(NS_FAILED(rv))) {
   5987    return;
   5988  }
   5989 
   5990  editActionData.SetData(aTextDirection == TextDirection::eLTR ? u"ltr"_ns
   5991                                                               : u"rtl"_ns);
   5992 
   5993  // FYI: Oddly, Chrome does not dispatch beforeinput event in this case but
   5994  //      dispatches input event.
   5995  rv = editActionData.MaybeDispatchBeforeInputEvent();
   5996  if (NS_FAILED(rv)) {
   5997    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   5998                         "MaybeDispatchBeforeInputEvent() failed");
   5999    return;
   6000  }
   6001 
   6002  if ((aTextDirection == TextDirection::eLTR && IsRightToLeft()) ||
   6003      (aTextDirection == TextDirection::eRTL && IsLeftToRight())) {
   6004    // Do it only when the direction is still different from the original
   6005    // new direction.  Note that "beforeinput" event listener may have already
   6006    // changed the direction here, but they may not cancel the event.
   6007    nsresult rv = SetTextDirectionTo(aTextDirection);
   6008    if (NS_FAILED(rv)) {
   6009      NS_WARNING("EditorBase::SetTextDirectionTo() failed");
   6010      return;
   6011    }
   6012  }
   6013 
   6014  editActionData.MarkAsHandled();
   6015 
   6016  // XXX When we don't change the text direction, do we really need to
   6017  //     dispatch input event?
   6018  DispatchInputEvent();
   6019 }
   6020 
   6021 nsresult EditorBase::SetTextDirectionTo(TextDirection aTextDirection) {
   6022  const RefPtr<Element> editingHostOrTextControlElement =
   6023      IsHTMLEditor() ? AsHTMLEditor()->ComputeEditingHost(
   6024                           HTMLEditor::LimitInBodyElement::No)
   6025                     : GetExposedRoot();
   6026  if (!editingHostOrTextControlElement) {  // Don't warn, HTMLEditor may have no
   6027                                           // active editing host
   6028    return NS_OK;
   6029  }
   6030 
   6031  if (aTextDirection == TextDirection::eLTR) {
   6032    NS_ASSERTION(!IsLeftToRight(), "Unexpected mutually exclusive flag");
   6033    mFlags &= ~nsIEditor::eEditorRightToLeft;
   6034    mFlags |= nsIEditor::eEditorLeftToRight;
   6035    nsresult rv =
   6036        AutoElementAttrAPIWrapper(*this, *editingHostOrTextControlElement)
   6037            .SetAttr(nsGkAtoms::dir, u"ltr"_ns, true);
   6038    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6039                         "AutoElementAttrAPIWrapper::SetAttr() failed");
   6040    return rv;
   6041  }
   6042 
   6043  if (aTextDirection == TextDirection::eRTL) {
   6044    NS_ASSERTION(!IsRightToLeft(), "Unexpected mutually exclusive flag");
   6045    mFlags |= nsIEditor::eEditorRightToLeft;
   6046    mFlags &= ~nsIEditor::eEditorLeftToRight;
   6047    nsresult rv =
   6048        AutoElementAttrAPIWrapper(*this, *editingHostOrTextControlElement)
   6049            .SetAttr(nsGkAtoms::dir, u"rtl"_ns, true);
   6050    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6051                         "AutoElementAttrAPIWrapper::SetAttr() failed");
   6052    return rv;
   6053  }
   6054 
   6055  return NS_OK;
   6056 }
   6057 
   6058 Element* EditorBase::GetFocusedElement() const {
   6059  EventTarget* eventTarget = GetDOMEventTarget();
   6060  if (!eventTarget) {
   6061    return nullptr;
   6062  }
   6063 
   6064  Element* const focusedElement = nsFocusManager::GetFocusedElementStatic();
   6065  MOZ_ASSERT((focusedElement == eventTarget) ==
   6066             SameCOMIdentity(focusedElement, eventTarget));
   6067 
   6068  return (focusedElement == eventTarget) ? focusedElement : nullptr;
   6069 }
   6070 
   6071 bool EditorBase::IsActiveInDOMWindow() const {
   6072  EventTarget* piTarget = GetDOMEventTarget();
   6073  if (!piTarget) {
   6074    return false;
   6075  }
   6076 
   6077  nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
   6078  if (NS_WARN_IF(!focusManager)) {
   6079    return false;  // Do we need to check the singleton instance??
   6080  }
   6081 
   6082  Document* document = GetDocument();
   6083  if (NS_WARN_IF(!document)) {
   6084    return false;
   6085  }
   6086  nsPIDOMWindowOuter* ourWindow = document->GetWindow();
   6087  nsCOMPtr<nsPIDOMWindowOuter> win;
   6088  nsIContent* content = nsFocusManager::GetFocusedDescendant(
   6089      ourWindow, nsFocusManager::eOnlyCurrentWindow, getter_AddRefs(win));
   6090  return SameCOMIdentity(content, piTarget);
   6091 }
   6092 
   6093 bool EditorBase::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const {
   6094  // If the event is trusted, the event should always cause input.
   6095  if (NS_WARN_IF(!aGUIEvent)) {
   6096    return false;
   6097  }
   6098 
   6099  // If this is dispatched by using cordinates but this editor doesn't have
   6100  // focus, we shouldn't handle it.
   6101  if (aGUIEvent->IsUsingCoordinates() && !GetFocusedElement()) {
   6102    return false;
   6103  }
   6104 
   6105  // If a composition event isn't dispatched via widget, we need to ignore them
   6106  // since they cannot be managed by TextComposition. E.g., the event was
   6107  // created by chrome JS.
   6108  // Note that if we allow to handle such events, editor may be confused by
   6109  // strange event order.
   6110  bool needsWidget = false;
   6111  switch (aGUIEvent->mMessage) {
   6112    case eUnidentifiedEvent:
   6113      // If events are not created with proper event interface, their message
   6114      // are initialized with eUnidentifiedEvent.  Let's ignore such event.
   6115      return false;
   6116    case eCompositionStart:
   6117    case eCompositionEnd:
   6118    case eCompositionUpdate:
   6119    case eCompositionChange:
   6120    case eCompositionCommitAsIs:
   6121      // Don't allow composition events whose internal event are not
   6122      // WidgetCompositionEvent.
   6123      if (!aGUIEvent->AsCompositionEvent()) {
   6124        return false;
   6125      }
   6126      needsWidget = true;
   6127      break;
   6128    default:
   6129      break;
   6130  }
   6131  if (needsWidget && !aGUIEvent->mWidget) {
   6132    return false;
   6133  }
   6134 
   6135  // Accept all trusted events.
   6136  if (aGUIEvent->IsTrusted()) {
   6137    return true;
   6138  }
   6139 
   6140  // Ignore untrusted mouse event.
   6141  // XXX Why are we handling other untrusted input events?
   6142  if (aGUIEvent->AsMouseEventBase()) {
   6143    return false;
   6144  }
   6145 
   6146  // Otherwise, we shouldn't handle any input events when we're not an active
   6147  // element of the DOM window.
   6148  return IsActiveInDOMWindow();
   6149 }
   6150 
   6151 nsresult EditorBase::FlushPendingSpellCheck() {
   6152  // If the spell check skip flag is still enabled from creation time,
   6153  // disable it because focused editors are allowed to spell check.
   6154  if (!ShouldSkipSpellCheck()) {
   6155    return NS_OK;
   6156  }
   6157  MOZ_ASSERT(!IsHTMLEditor(), "HTMLEditor should not has pending spell checks");
   6158  nsresult rv = RemoveFlags(nsIEditor::eEditorSkipSpellCheck);
   6159  if (NS_WARN_IF(Destroyed())) {
   6160    return NS_ERROR_EDITOR_DESTROYED;
   6161  }
   6162  NS_WARNING_ASSERTION(
   6163      NS_SUCCEEDED(rv),
   6164      "EditorBase::RemoveFlags(nsIEditor::eEditorSkipSpellCheck) failed");
   6165  return rv;
   6166 }
   6167 
   6168 bool EditorBase::CanKeepHandlingFocusEvent(
   6169    const nsINode& aOriginalEventTargetNode) const {
   6170  if (MOZ_UNLIKELY(!IsListeningToEvents() || Destroyed())) {
   6171    return false;
   6172  }
   6173 
   6174  // If the event target is document mode, we only need to handle the focus
   6175  // event when the document is still in designMode.  Otherwise, the
   6176  // mode has been disabled by somebody while we're handling the focus event.
   6177  if (aOriginalEventTargetNode.IsDocument()) {
   6178    return IsHTMLEditor() && aOriginalEventTargetNode.IsInDesignMode();
   6179  }
   6180  MOZ_ASSERT(aOriginalEventTargetNode.IsContent());
   6181 
   6182  // If nobody has focus, the focus event target has been blurred by somebody
   6183  // else.  So the editor shouldn't initialize itself to start to handle
   6184  // anything.
   6185  const Element* const focusedElement =
   6186      nsFocusManager::GetFocusedElementStatic();
   6187  if (!focusedElement) {
   6188    return false;
   6189  }
   6190 
   6191  // If there's an HTMLEditor registered in the target document and we
   6192  // are not that HTMLEditor (for cases like nested documents), let
   6193  // that HTMLEditor to handle the focus event.
   6194  if (IsHTMLEditor()) {
   6195    const HTMLEditor* precedentHTMLEditor =
   6196        aOriginalEventTargetNode.OwnerDoc()->GetHTMLEditor();
   6197 
   6198    if (precedentHTMLEditor && precedentHTMLEditor != this) {
   6199      return false;
   6200    }
   6201  }
   6202 
   6203  const nsIContent* exposedTargetContent =
   6204      aOriginalEventTargetNode.AsContent()
   6205          ->FindFirstNonChromeOnlyAccessContent();
   6206  const nsIContent* exposedFocusedContent =
   6207      focusedElement->FindFirstNonChromeOnlyAccessContent();
   6208  return exposedTargetContent && exposedFocusedContent &&
   6209         exposedTargetContent == exposedFocusedContent;
   6210 }
   6211 
   6212 nsresult EditorBase::OnFocus(const nsINode& aOriginalEventTargetNode) {
   6213  MOZ_ASSERT(IsEditActionDataAvailable());
   6214 
   6215  InitializeSelection(aOriginalEventTargetNode);
   6216  mSpellCheckerDictionaryUpdated = false;
   6217  if (mInlineSpellChecker && CanEnableSpellCheck()) {
   6218    DebugOnly<nsresult> rvIgnored =
   6219        mInlineSpellChecker->UpdateCurrentDictionary();
   6220    NS_WARNING_ASSERTION(
   6221        NS_SUCCEEDED(rvIgnored),
   6222        "mozInlineSpellCHecker::UpdateCurrentDictionary() failed, but ignored");
   6223    mSpellCheckerDictionaryUpdated = true;
   6224  }
   6225  // XXX Why don't we stop handling focus with the spell checker immediately
   6226  //     after calling InitializeSelection?
   6227  if (MOZ_UNLIKELY(!CanKeepHandlingFocusEvent(aOriginalEventTargetNode))) {
   6228    return NS_ERROR_EDITOR_DESTROYED;
   6229  }
   6230 
   6231  const RefPtr<Element> focusedElement = GetFocusedElement();
   6232  RefPtr<nsPresContext> presContext =
   6233      focusedElement ? focusedElement->GetPresContext(
   6234                           Element::PresContextFor::eForComposedDoc)
   6235                     : GetPresContext();
   6236  if (NS_WARN_IF(!presContext)) {
   6237    return NS_ERROR_FAILURE;
   6238  }
   6239  IMEStateManager::OnFocusInEditor(*presContext, focusedElement, *this);
   6240 
   6241  return NS_OK;
   6242 }
   6243 
   6244 void EditorBase::HideCaret(bool aHide) {
   6245  if (mHidingCaret == aHide) {
   6246    return;
   6247  }
   6248 
   6249  RefPtr<nsCaret> caret = GetCaret();
   6250  if (NS_WARN_IF(!caret)) {
   6251    return;
   6252  }
   6253 
   6254  mHidingCaret = aHide;
   6255  if (aHide) {
   6256    caret->AddForceHide();
   6257  } else {
   6258    caret->RemoveForceHide();
   6259  }
   6260 }
   6261 
   6262 NS_IMETHODIMP EditorBase::Unmask(uint32_t aStart, int64_t aEnd,
   6263                                 uint32_t aTimeout, uint8_t aArgc) {
   6264  if (NS_WARN_IF(!IsPasswordEditor())) {
   6265    return NS_ERROR_NOT_AVAILABLE;
   6266  }
   6267  if (NS_WARN_IF(aArgc >= 1 && aStart == UINT32_MAX) ||
   6268      NS_WARN_IF(aArgc >= 2 && aEnd == 0) ||
   6269      NS_WARN_IF(aArgc >= 2 && aEnd > 0 && aStart >= aEnd)) {
   6270    return NS_ERROR_INVALID_ARG;
   6271  }
   6272 
   6273  AutoEditActionDataSetter editActionData(*this, EditAction::eHidePassword);
   6274  if (NS_WARN_IF(!editActionData.CanHandle())) {
   6275    return NS_ERROR_NOT_INITIALIZED;
   6276  }
   6277 
   6278  uint32_t start = aArgc < 1 ? 0 : aStart;
   6279  uint32_t length = aArgc < 2 || aEnd < 0 ? UINT32_MAX : aEnd - start;
   6280  uint32_t timeout = aArgc < 3 ? 0 : aTimeout;
   6281  nsresult rv = MOZ_KnownLive(AsTextEditor())
   6282                    ->SetUnmaskRangeAndNotify(start, length, timeout);
   6283  if (NS_FAILED(rv)) {
   6284    NS_WARNING("TextEditor::SetUnmaskRangeAndNotify() failed");
   6285    return EditorBase::ToGenericNSResult(rv);
   6286  }
   6287 
   6288  // Flush pending layout right now since the caller may access us before
   6289  // doing it.
   6290  if (RefPtr<PresShell> presShell = GetPresShell()) {
   6291    presShell->FlushPendingNotifications(FlushType::Layout);
   6292  }
   6293 
   6294  return NS_OK;
   6295 }
   6296 
   6297 NS_IMETHODIMP EditorBase::Mask() {
   6298  if (NS_WARN_IF(!IsPasswordEditor())) {
   6299    return NS_ERROR_NOT_AVAILABLE;
   6300  }
   6301 
   6302  AutoEditActionDataSetter editActionData(*this, EditAction::eHidePassword);
   6303  if (NS_WARN_IF(!editActionData.CanHandle())) {
   6304    return NS_ERROR_NOT_INITIALIZED;
   6305  }
   6306 
   6307  nsresult rv = MOZ_KnownLive(AsTextEditor())->MaskAllCharactersAndNotify();
   6308  if (NS_FAILED(rv)) {
   6309    NS_WARNING("TextEditor::MaskAllCharactersAndNotify() failed");
   6310    return EditorBase::ToGenericNSResult(rv);
   6311  }
   6312 
   6313  // Flush pending layout right now since the caller may access us before
   6314  // doing it.
   6315  if (RefPtr<PresShell> presShell = GetPresShell()) {
   6316    presShell->FlushPendingNotifications(FlushType::Layout);
   6317  }
   6318 
   6319  return NS_OK;
   6320 }
   6321 
   6322 NS_IMETHODIMP EditorBase::GetUnmaskedStart(uint32_t* aResult) {
   6323  if (NS_WARN_IF(!IsPasswordEditor())) {
   6324    *aResult = 0;
   6325    return NS_ERROR_NOT_AVAILABLE;
   6326  }
   6327  *aResult =
   6328      AsTextEditor()->IsAllMasked() ? 0 : AsTextEditor()->UnmaskedStart();
   6329  return NS_OK;
   6330 }
   6331 
   6332 NS_IMETHODIMP EditorBase::GetUnmaskedEnd(uint32_t* aResult) {
   6333  if (NS_WARN_IF(!IsPasswordEditor())) {
   6334    *aResult = 0;
   6335    return NS_ERROR_NOT_AVAILABLE;
   6336  }
   6337  *aResult = AsTextEditor()->IsAllMasked() ? 0 : AsTextEditor()->UnmaskedEnd();
   6338  return NS_OK;
   6339 }
   6340 
   6341 NS_IMETHODIMP EditorBase::GetAutoMaskingEnabled(bool* aResult) {
   6342  if (NS_WARN_IF(!IsPasswordEditor())) {
   6343    *aResult = false;
   6344    return NS_ERROR_NOT_AVAILABLE;
   6345  }
   6346  *aResult = AsTextEditor()->IsMaskingPassword();
   6347  return NS_OK;
   6348 }
   6349 
   6350 NS_IMETHODIMP EditorBase::GetPasswordMask(nsAString& aPasswordMask) {
   6351  aPasswordMask.Assign(TextEditor::PasswordMask());
   6352  return NS_OK;
   6353 }
   6354 
   6355 template <typename PT, typename CT>
   6356 EditorBase::AutoCaretBidiLevelManager::AutoCaretBidiLevelManager(
   6357    const EditorBase& aEditorBase, nsIEditor::EDirection aDirectionAndAmount,
   6358    const EditorDOMPointBase<PT, CT>& aPointAtCaret) {
   6359  MOZ_ASSERT(aEditorBase.IsEditActionDataAvailable());
   6360 
   6361  nsPresContext* presContext = aEditorBase.GetPresContext();
   6362  if (NS_WARN_IF(!presContext)) {
   6363    mFailed = true;
   6364    return;
   6365  }
   6366 
   6367  if (!presContext->BidiEnabled()) {
   6368    return;  // Perform the deletion
   6369  }
   6370 
   6371  if (!aPointAtCaret.IsInContentNode()) {
   6372    mFailed = true;
   6373    return;
   6374  }
   6375 
   6376  // XXX Not sure whether this requires strong reference here.
   6377  RefPtr<nsFrameSelection> frameSelection =
   6378      aEditorBase.SelectionRef().GetFrameSelection();
   6379  if (NS_WARN_IF(!frameSelection)) {
   6380    mFailed = true;
   6381    return;
   6382  }
   6383 
   6384  nsPrevNextBidiLevels levels = frameSelection->GetPrevNextBidiLevels(
   6385      aPointAtCaret.template ContainerAs<nsIContent>(), aPointAtCaret.Offset(),
   6386      true);
   6387 
   6388  mozilla::intl::BidiEmbeddingLevel levelBefore = levels.mLevelBefore;
   6389  mozilla::intl::BidiEmbeddingLevel levelAfter = levels.mLevelAfter;
   6390 
   6391  mozilla::intl::BidiEmbeddingLevel currentCaretLevel =
   6392      frameSelection->GetCaretBidiLevel();
   6393 
   6394  mozilla::intl::BidiEmbeddingLevel levelOfDeletion;
   6395  levelOfDeletion = (nsIEditor::eNext == aDirectionAndAmount ||
   6396                     nsIEditor::eNextWord == aDirectionAndAmount)
   6397                        ? levelAfter
   6398                        : levelBefore;
   6399 
   6400  if (currentCaretLevel == levelOfDeletion) {
   6401    return;  // Perform the deletion
   6402  }
   6403 
   6404  // Set the bidi level of the caret to that of the
   6405  // character that will be (or would have been) deleted
   6406  mNewCaretBidiLevel = Some(levelOfDeletion);
   6407  mCanceled =
   6408      !StaticPrefs::bidi_edit_delete_immediately() && levelBefore != levelAfter;
   6409 }
   6410 
   6411 void EditorBase::AutoCaretBidiLevelManager::MaybeUpdateCaretBidiLevel(
   6412    const EditorBase& aEditorBase) const {
   6413  MOZ_ASSERT(!mFailed);
   6414  if (mNewCaretBidiLevel.isNothing()) {
   6415    return;
   6416  }
   6417  RefPtr<nsFrameSelection> frameSelection =
   6418      aEditorBase.SelectionRef().GetFrameSelection();
   6419  MOZ_ASSERT(frameSelection);
   6420  frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
   6421      mNewCaretBidiLevel.value());
   6422 }
   6423 
   6424 void EditorBase::UndefineCaretBidiLevel() const {
   6425  MOZ_ASSERT(IsEditActionDataAvailable());
   6426 
   6427  /**
   6428   * After inserting text the caret Bidi level must be set to the level of the
   6429   * inserted text.This is difficult, because we cannot know what the level is
   6430   * until after the Bidi algorithm is applied to the whole paragraph.
   6431   *
   6432   * So we set the caret Bidi level to UNDEFINED here, and the caret code will
   6433   * set it correctly later
   6434   */
   6435  nsFrameSelection* frameSelection = SelectionRef().GetFrameSelection();
   6436  if (frameSelection) {
   6437    frameSelection->UndefineCaretBidiLevel();
   6438  }
   6439 }
   6440 
   6441 NS_IMETHODIMP EditorBase::GetTextLength(uint32_t* aCount) {
   6442  return NS_ERROR_NOT_IMPLEMENTED;
   6443 }
   6444 
   6445 NS_IMETHODIMP EditorBase::GetNewlineHandling(int32_t* aNewlineHandling) {
   6446  if (NS_WARN_IF(!aNewlineHandling)) {
   6447    return NS_ERROR_INVALID_ARG;
   6448  }
   6449  *aNewlineHandling = mNewlineHandling;
   6450  return NS_OK;
   6451 }
   6452 
   6453 NS_IMETHODIMP EditorBase::SetNewlineHandling(int32_t aNewlineHandling) {
   6454  switch (aNewlineHandling) {
   6455    case nsIEditor::eNewlinesPasteIntact:
   6456    case nsIEditor::eNewlinesPasteToFirst:
   6457    case nsIEditor::eNewlinesReplaceWithSpaces:
   6458    case nsIEditor::eNewlinesStrip:
   6459    case nsIEditor::eNewlinesReplaceWithCommas:
   6460    case nsIEditor::eNewlinesStripSurroundingWhitespace:
   6461      mNewlineHandling = aNewlineHandling;
   6462      return NS_OK;
   6463    default:
   6464      NS_ERROR("SetNewlineHandling() is called with wrong value");
   6465      return NS_ERROR_INVALID_ARG;
   6466  }
   6467 }
   6468 
   6469 bool EditorBase::IsSelectionRangeContainerNotContent() const {
   6470  MOZ_ASSERT(IsEditActionDataAvailable());
   6471 
   6472  // TODO: Make all callers use !AutoClonedRangeArray::IsInContent() instead.
   6473  const uint32_t rangeCount = SelectionRef().RangeCount();
   6474  for (const uint32_t i : IntegerRange(rangeCount)) {
   6475    MOZ_ASSERT(SelectionRef().RangeCount() == rangeCount);
   6476    const nsRange* range = SelectionRef().GetRangeAt(i);
   6477    MOZ_ASSERT(range);
   6478    if (MOZ_UNLIKELY(!range) || MOZ_UNLIKELY(!range->GetStartContainer()) ||
   6479        MOZ_UNLIKELY(!range->GetStartContainer()->IsContent()) ||
   6480        MOZ_UNLIKELY(!range->GetEndContainer()) ||
   6481        MOZ_UNLIKELY(!range->GetEndContainer()->IsContent())) {
   6482      return true;
   6483    }
   6484  }
   6485  return false;
   6486 }
   6487 
   6488 NS_IMETHODIMP EditorBase::InsertText(const nsAString& aStringToInsert) {
   6489  nsresult rv = InsertTextAsAction(aStringToInsert);
   6490  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6491                       "EditorBase::InsertTextAsAction() failed");
   6492  return rv;
   6493 }
   6494 
   6495 nsresult EditorBase::InsertTextAsAction(const nsAString& aStringToInsert,
   6496                                        nsIPrincipal* aPrincipal) {
   6497  // Showing this assertion is fine if this method is called by outside via
   6498  // mutation event listener or something.  Otherwise, this is called by
   6499  // wrong method.
   6500  NS_ASSERTION(!mPlaceholderBatch,
   6501               "Should be called only when this is the only edit action of the "
   6502               "operation "
   6503               "unless mutation event listener nests some operations");
   6504 
   6505  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertText,
   6506                                          aPrincipal);
   6507  // Note that we don't need to replace native line breaks with XP line breaks
   6508  // here because Chrome does not do it.
   6509  MOZ_ASSERT(!aStringToInsert.IsVoid());
   6510  editActionData.SetData(aStringToInsert);
   6511  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   6512  if (NS_FAILED(rv)) {
   6513    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   6514                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
   6515    return EditorBase::ToGenericNSResult(rv);
   6516  }
   6517 
   6518  nsString stringToInsert(aStringToInsert);
   6519  if (IsTextEditor()) {
   6520    nsContentUtils::PlatformToDOMLineBreaks(stringToInsert);
   6521  }
   6522  AutoPlaceholderBatch treatAsOneTransaction(
   6523      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   6524  rv = InsertTextAsSubAction(stringToInsert, InsertTextFor::NormalText);
   6525  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6526                       "EditorBase::InsertTextAsSubAction() failed");
   6527  return EditorBase::ToGenericNSResult(rv);
   6528 }
   6529 
   6530 nsresult EditorBase::InsertTextAsSubAction(const nsAString& aStringToInsert,
   6531                                           InsertTextFor aPurpose) {
   6532  MOZ_ASSERT(IsEditActionDataAvailable());
   6533  MOZ_ASSERT(mPlaceholderBatch);
   6534  MOZ_ASSERT(IsHTMLEditor() ||
   6535             aStringToInsert.FindChar(nsCRT::CR) == kNotFound);
   6536  MOZ_ASSERT_IF(aPurpose == InsertTextFor::CompositionStart ||
   6537                    aPurpose == InsertTextFor::CompositionUpdate ||
   6538                    aPurpose == InsertTextFor::CompositionEnd ||
   6539                    aPurpose == InsertTextFor::CompositionStartAndEnd,
   6540                mComposition);
   6541 
   6542  if (NS_WARN_IF(!mInitSucceeded)) {
   6543    return NS_ERROR_NOT_INITIALIZED;
   6544  }
   6545 
   6546  if (NS_WARN_IF(Destroyed())) {
   6547    return NS_ERROR_EDITOR_DESTROYED;
   6548  }
   6549 
   6550  EditSubAction editSubAction = ShouldHandleIMEComposition()
   6551                                    ? EditSubAction::eInsertTextComingFromIME
   6552                                    : EditSubAction::eInsertText;
   6553 
   6554  IgnoredErrorResult ignoredError;
   6555  AutoEditSubActionNotifier startToHandleEditSubAction(
   6556      *this, editSubAction, nsIEditor::eNext, ignoredError);
   6557  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   6558    return ignoredError.StealNSResult();
   6559  }
   6560  NS_WARNING_ASSERTION(
   6561      !ignoredError.Failed(),
   6562      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   6563 
   6564  Result<EditActionResult, nsresult> result =
   6565      HandleInsertText(aStringToInsert, aPurpose);
   6566  if (MOZ_UNLIKELY(result.isErr())) {
   6567    NS_WARNING("EditorBase::HandleInsertText() failed");
   6568    return result.unwrapErr();
   6569  }
   6570  return NS_OK;
   6571 }
   6572 
   6573 NS_IMETHODIMP EditorBase::InsertLineBreak() { return NS_ERROR_NOT_IMPLEMENTED; }
   6574 
   6575 /*****************************************************************************
   6576 * mozilla::EditorBase::AutoEditActionDataSetter
   6577 *****************************************************************************/
   6578 
   6579 EditorBase::AutoEditActionDataSetter::AutoEditActionDataSetter(
   6580    const EditorBase& aEditorBase, EditAction aEditAction,
   6581    nsIPrincipal* aPrincipal /* = nullptr */)
   6582    : mEditorBase(const_cast<EditorBase&>(aEditorBase)),
   6583      mPrincipal(aPrincipal),
   6584      mParentData(aEditorBase.mEditActionData),
   6585      mData(VoidString()),
   6586      mRawEditAction(aEditAction),
   6587      mEditorWasDestroyedDuringHandlingEditAction(
   6588          mParentData &&
   6589          mParentData->mEditorWasDestroyedDuringHandlingEditAction),
   6590      mEditorWasReinitialized(mParentData &&
   6591                              mParentData->mEditorWasReinitialized) {
   6592  // If we're nested edit action, copies necessary data from the parent.
   6593  if (mParentData) {
   6594    mSelection = mParentData->mSelection;
   6595    MOZ_ASSERT(!mSelection ||
   6596               (mSelection->GetType() == SelectionType::eNormal));
   6597    mTextNode = mParentData->mTextNode;
   6598 
   6599    // If we're not editing something, we should inherit the parent's edit
   6600    // action. This may occur if creator or its callee use public methods which
   6601    // just returns something.
   6602    if (IsEditActionInOrderToEditSomething(aEditAction)) {
   6603      mEditAction = aEditAction;
   6604    } else {
   6605      mEditAction = mParentData->mEditAction;
   6606      // If we inherit an edit action whose handler needs to dispatch a
   6607      // clipboard event, we should inherit the clipboard dispatching state
   6608      // too because this nest occurs by a clipboard event listener or
   6609      // a beforeinput/mutation event listener is important for checking
   6610      // whether we've already called `MaybeDispatchBeforeInputEvent()`
   6611      // property in some points.  If the former case, not yet dispatching
   6612      // beforeinput event is okay (not fine).
   6613      mHasTriedToDispatchClipboardEvent =
   6614          mParentData->mHasTriedToDispatchClipboardEvent;
   6615    }
   6616    mTopLevelEditSubAction = mParentData->mTopLevelEditSubAction;
   6617 
   6618    // Parent's mTopLevelEditSubActionData should be referred instead so that
   6619    // we don't need to set mTopLevelEditSubActionData.mSelectedRange nor
   6620    // mTopLevelEditActionData.mChangedRange here.
   6621 
   6622    mDirectionOfTopLevelEditSubAction =
   6623        mParentData->mDirectionOfTopLevelEditSubAction;
   6624  } else {
   6625    mSelection = mEditorBase.GetSelection();
   6626    if (NS_WARN_IF(!mSelection)) {
   6627      return;
   6628    }
   6629    // Although we shouldn't have had the cached Text yet because we're the
   6630    // topmost instance of AutoEditActionDataSetter and we'll register this to
   6631    // aEditorBase below.  However, for clarifying, let's explicitly ignore the
   6632    // cached Text.  Additionally, this may be called for initializing
   6633    // aEditorBase too.  Therefore, we need to avoid the assertions in
   6634    // GetTextNode() so that we need to check whether the editor is initialized.
   6635    mTextNode = mEditorBase.IsTextEditor() && mEditorBase.mInitSucceeded
   6636                    ? mEditorBase.AsTextEditor()->GetTextNode(
   6637                          TextEditor::IgnoreTextNodeCache::Yes)
   6638                    : nullptr;
   6639 
   6640    MOZ_ASSERT(mSelection->GetType() == SelectionType::eNormal);
   6641 
   6642    mEditAction = aEditAction;
   6643    mDirectionOfTopLevelEditSubAction = eNone;
   6644    if (mEditorBase.IsHTMLEditor()) {
   6645      mTopLevelEditSubActionData.mSelectedRange =
   6646          mEditorBase.AsHTMLEditor()
   6647              ->GetSelectedRangeItemForTopLevelEditSubAction();
   6648      mTopLevelEditSubActionData.mChangedRange =
   6649          mEditorBase.AsHTMLEditor()->GetChangedRangeForTopLevelEditSubAction();
   6650      mTopLevelEditSubActionData.mCachedPendingStyles.emplace();
   6651    }
   6652  }
   6653  mEditorBase.mEditActionData = this;
   6654 
   6655  if (aEditorBase.IsHTMLEditor() &&
   6656      MOZ_LOG_TEST(gHTMLEditorEditActionStartLog, LogLevel::Info) &&
   6657      aEditAction != EditAction::eNone &&
   6658      aEditAction != EditAction::eNotEditing &&
   6659      aEditAction != EditAction::eInitializing) {
   6660    const HTMLEditor& htmlEditor = *aEditorBase.AsHTMLEditor();
   6661    Element* const editingHost =
   6662        htmlEditor.ComputeEditingHost(HTMLEditor::LimitInBodyElement::No);
   6663    nsAutoString innerHTML;
   6664    if (editingHost) {
   6665      editingHost->GetInnerHTML(innerHTML, IgnoreErrors());
   6666      innerHTML.ReplaceSubstring(u"\n", u"\\n");
   6667      innerHTML.ReplaceSubstring(u"\r", u"\\r");
   6668      innerHTML.ReplaceSubstring(u"\t", u"\\t");
   6669      innerHTML.ReplaceSubstring(u"\f", u"\\f");
   6670      innerHTML.ReplaceSubstring(u"\u00A0", u"&nbsp;");
   6671    }
   6672    MOZ_ASSERT(mSelection);
   6673    MOZ_LOG(
   6674        gHTMLEditorEditActionStartLog, LogLevel::Info,
   6675        ("%s\nediting host: %s\ninnerHTML: \"%s\"\nselection range "
   6676         "count: %u",
   6677         ToString(aEditAction).c_str(), ToString(RefPtr{editingHost}).c_str(),
   6678         NS_ConvertUTF16toUTF8(innerHTML).get(), mSelection->RangeCount()));
   6679    for (const uint32_t index : IntegerRange(mSelection->RangeCount())) {
   6680      nsRange* const range = mSelection->GetRangeAt(index);
   6681      MOZ_ASSERT(range);
   6682      EditorRawDOMRange editorRange(*range);
   6683      MOZ_LOG(gHTMLEditorEditActionStartLog, LogLevel::Info,
   6684              ("getRangeAt(%u): %s", index, ToString(editorRange).c_str()));
   6685    }
   6686  }
   6687 }
   6688 
   6689 EditorBase::AutoEditActionDataSetter::~AutoEditActionDataSetter() {
   6690  MOZ_ASSERT(mHasCanHandleChecked);
   6691 
   6692  if (!mSelection || NS_WARN_IF(mEditorBase.mEditActionData != this)) {
   6693    return;
   6694  }
   6695  mEditorBase.mEditActionData = mParentData;
   6696 
   6697  MOZ_ASSERT(
   6698      !mTopLevelEditSubActionData.mSelectedRange ||
   6699          (!mTopLevelEditSubActionData.mSelectedRange->mStartContainer &&
   6700           !mTopLevelEditSubActionData.mSelectedRange->mEndContainer),
   6701      "mTopLevelEditSubActionData.mSelectedRange should've been cleared");
   6702 }
   6703 
   6704 void EditorBase::AutoEditActionDataSetter::OnEditorInitialized() {
   6705  if (mEditorWasDestroyedDuringHandlingEditAction) {
   6706    mEditorWasReinitialized = true;
   6707  }
   6708  if (mEditorBase.IsTextEditor()) {
   6709    mTextNode = mEditorBase.AsTextEditor()->GetTextNode(
   6710        TextEditor::IgnoreTextNodeCache::Yes);
   6711  }
   6712  if (mParentData) {
   6713    mParentData->OnEditorInitialized();
   6714  }
   6715 }
   6716 
   6717 void EditorBase::AutoEditActionDataSetter::UpdateSelectionCache(
   6718    Selection& aSelection) {
   6719  MOZ_ASSERT(aSelection.GetType() == SelectionType::eNormal);
   6720 
   6721  if (mSelection == &aSelection) {
   6722    return;
   6723  }
   6724 
   6725  AutoEditActionDataSetter& topLevelEditActionData =
   6726      [&]() -> AutoEditActionDataSetter& {
   6727    for (AutoEditActionDataSetter* editActionData = this;;
   6728         editActionData = editActionData->mParentData) {
   6729      if (!editActionData->mParentData) {
   6730        return *editActionData;
   6731      }
   6732    }
   6733    MOZ_ASSERT_UNREACHABLE("You do something wrong");
   6734  }();
   6735 
   6736  RefPtr<Selection> previousSelection = mSelection;
   6737 
   6738  // Keep grabbing the old selection in the top level edit action data until the
   6739  // all owners end handling it.
   6740  if (previousSelection) {
   6741    topLevelEditActionData.mRetiredSelections.AppendElement(*previousSelection);
   6742  }
   6743 
   6744  // If the old selection is in batch, we should end the batch which
   6745  // `EditorBase::BeginUpdateViewBatch` started.
   6746  if (mEditorBase.mUpdateCount && previousSelection) {
   6747    previousSelection->EndBatchChanges(__FUNCTION__);
   6748  }
   6749 
   6750  mSelection = &aSelection;
   6751  for (AutoEditActionDataSetter* parentActionData = mParentData;
   6752       parentActionData; parentActionData = parentActionData->mParentData) {
   6753    if (!parentActionData->mSelection) {
   6754      continue;
   6755    }
   6756    // Skip scanning mRetiredSelections if we've already handled the selection
   6757    // previous time.
   6758    if (parentActionData->mSelection != previousSelection) {
   6759      if (!topLevelEditActionData.mRetiredSelections.Contains(
   6760              OwningNonNull<Selection>(*parentActionData->mSelection))) {
   6761        topLevelEditActionData.mRetiredSelections.AppendElement(
   6762            *parentActionData->mSelection);
   6763      }
   6764      previousSelection = parentActionData->mSelection;
   6765    }
   6766    parentActionData->mSelection = &aSelection;
   6767  }
   6768 
   6769  // Restart the batching in the new selection.
   6770  if (mEditorBase.mUpdateCount) {
   6771    aSelection.StartBatchChanges(__FUNCTION__);
   6772  }
   6773 }
   6774 
   6775 void EditorBase::AutoEditActionDataSetter::SetColorData(
   6776    const nsAString& aData) {
   6777  MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
   6778             "It's too late to set data since this may have already dispatched "
   6779             "a beforeinput event");
   6780 
   6781  if (aData.IsEmpty()) {
   6782    // When removing color/background-color, let's use empty string.
   6783    mData.Truncate();
   6784    MOZ_ASSERT(!mData.IsVoid());
   6785    return;
   6786  }
   6787 
   6788  DebugOnly<bool> validColorValue = HTMLEditUtils::GetNormalizedCSSColorValue(
   6789      aData, HTMLEditUtils::ZeroAlphaColor::RGBAValue, mData);
   6790  MOZ_ASSERT_IF(validColorValue, !mData.IsVoid());
   6791 }
   6792 
   6793 void EditorBase::AutoEditActionDataSetter::InitializeDataTransfer(
   6794    DataTransfer* aDataTransfer) {
   6795  MOZ_ASSERT(aDataTransfer);
   6796  MOZ_ASSERT(aDataTransfer->IsReadOnly());
   6797  MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
   6798             "It's too late to set dataTransfer since this may have already "
   6799             "dispatched a beforeinput event");
   6800 
   6801  mDataTransfer = aDataTransfer;
   6802 }
   6803 
   6804 void EditorBase::AutoEditActionDataSetter::InitializeDataTransfer(
   6805    nsITransferable* aTransferable) {
   6806  MOZ_ASSERT(aTransferable);
   6807  MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
   6808             "It's too late to set dataTransfer since this may have already "
   6809             "dispatched a beforeinput event");
   6810 
   6811  Document* document = mEditorBase.GetDocument();
   6812  nsIGlobalObject* scopeObject =
   6813      document ? document->GetScopeObject() : nullptr;
   6814  mDataTransfer = new DataTransfer(scopeObject, eEditorInput, aTransferable);
   6815 }
   6816 
   6817 void EditorBase::AutoEditActionDataSetter::InitializeDataTransfer(
   6818    const nsAString& aString) {
   6819  MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
   6820             "It's too late to set dataTransfer since this may have already "
   6821             "dispatched a beforeinput event");
   6822  Document* document = mEditorBase.GetDocument();
   6823  nsIGlobalObject* scopeObject =
   6824      document ? document->GetScopeObject() : nullptr;
   6825  mDataTransfer = new DataTransfer(scopeObject, eEditorInput, aString);
   6826 }
   6827 
   6828 void EditorBase::AutoEditActionDataSetter::InitializeDataTransferWithClipboard(
   6829    SettingDataTransfer aSettingDataTransfer, DataTransfer* aDataTransfer,
   6830    nsIClipboard::ClipboardType aClipboardType) {
   6831  MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
   6832             "It's too late to set dataTransfer since this may have already "
   6833             "dispatched a beforeinput event");
   6834 
   6835  Document* document = mEditorBase.GetDocument();
   6836  nsIGlobalObject* scopeObject =
   6837      document ? document->GetScopeObject() : nullptr;
   6838  // mDataTransfer will be used for eEditorInput event, but we can keep
   6839  // using ePaste and ePasteNoFormatting here.  If we need to use eEditorInput,
   6840  // we need to create eEditorInputNoFormatting or something...
   6841  EventMessage message =
   6842      (aSettingDataTransfer == SettingDataTransfer::eWithFormat)
   6843          ? ePaste
   6844          : ePasteNoFormatting;
   6845  if (aDataTransfer) {
   6846    // The DataTransfer being passed in will be used in a paste event, which
   6847    // means it will be cleared after that event is done firing. We don't want
   6848    // that for "input" and "beforeinput" events, so make a copy of its data.
   6849    aDataTransfer->Clone(scopeObject, message,
   6850                         /* aUserCancelled = */ false,
   6851                         /* aIsCrossDomainSubFrameDrop = */ false,
   6852                         getter_AddRefs(mDataTransfer));
   6853  } else {
   6854    mDataTransfer = MakeRefPtr<DataTransfer>(
   6855        scopeObject, message, true /* is external */, Some(aClipboardType));
   6856  }
   6857 }
   6858 
   6859 void EditorBase::AutoEditActionDataSetter::AppendTargetRange(
   6860    StaticRange& aTargetRange) {
   6861  mTargetRanges.AppendElement(aTargetRange);
   6862 }
   6863 
   6864 void EditorBase::AutoEditActionDataSetter::AppendTargetRange(
   6865    RefPtr<StaticRange>&& aTargetRange) {
   6866  mTargetRanges.AppendElement(std::move(aTargetRange));
   6867 }
   6868 
   6869 bool EditorBase::AutoEditActionDataSetter::IsBeforeInputEventEnabled() const {
   6870  // Don't dispatch "beforeinput" event when the editor user makes us stop
   6871  // dispatching input event.
   6872  if (mEditorBase.IsSuppressingDispatchingInputEvent()) {
   6873    return false;
   6874  }
   6875  return EditorBase::TreatAsUserInput(mPrincipal);
   6876 }
   6877 
   6878 // static
   6879 bool EditorBase::TreatAsUserInput(nsIPrincipal* aPrincipal) {
   6880  // If aPrincipal it not nullptr, it means that the caller is handling an edit
   6881  // action which is requested by JS.  If it's not chrome script, we shouldn't
   6882  // dispatch "beforeinput" event.
   6883  if (aPrincipal && !aPrincipal->IsSystemPrincipal()) {
   6884    // But if it's content script of an addon, `execCommand` calls are a
   6885    // part of browser's default action from point of view of web apps.
   6886    // Therefore, we should dispatch `beforeinput` event.
   6887    // https://github.com/w3c/input-events/issues/91
   6888    if (!aPrincipal->GetIsAddonOrExpandedAddonPrincipal()) {
   6889      return false;
   6890    }
   6891  }
   6892 
   6893  return true;
   6894 }
   6895 
   6896 nsresult EditorBase::AutoEditActionDataSetter::MaybeFlushPendingNotifications()
   6897    const {
   6898  MOZ_ASSERT(CanHandle());
   6899  if (!MayEditActionRequireLayout(mRawEditAction)) {
   6900    return NS_SUCCESS_DOM_NO_OPERATION;
   6901  }
   6902  OwningNonNull<EditorBase> editorBase = mEditorBase;
   6903  RefPtr<PresShell> presShell = editorBase->GetPresShell();
   6904  if (MOZ_UNLIKELY(NS_WARN_IF(!presShell))) {
   6905    return NS_ERROR_NOT_AVAILABLE;
   6906  }
   6907  presShell->FlushPendingNotifications(FlushType::Layout);
   6908  if (MOZ_UNLIKELY(NS_WARN_IF(editorBase->Destroyed()))) {
   6909    return NS_ERROR_EDITOR_DESTROYED;
   6910  }
   6911  return NS_OK;
   6912 }
   6913 
   6914 void EditorBase::AutoEditActionDataSetter::MarkEditActionCanceled() {
   6915  mBeforeInputEventCanceled = true;
   6916  if (mEditorBase.IsHTMLEditor()) {
   6917    mEditorBase.AsHTMLEditor()->mHasBeforeInputBeenCanceled = true;
   6918  }
   6919 }
   6920 
   6921 nsresult EditorBase::AutoEditActionDataSetter::MaybeDispatchBeforeInputEvent(
   6922    nsIEditor::EDirection aDeleteDirectionAndAmount /* = nsIEditor::eNone */) {
   6923  MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
   6924             "We've already handled beforeinput event");
   6925  MOZ_ASSERT(CanHandle());
   6926  MOZ_ASSERT_IF(IsBeforeInputEventEnabled(),
   6927                ShouldAlreadyHaveHandledBeforeInputEventDispatching());
   6928  MOZ_ASSERT_IF(!MayEditActionDeleteAroundCollapsedSelection(mEditAction),
   6929                aDeleteDirectionAndAmount == nsIEditor::eNone);
   6930 
   6931  mHasTriedToDispatchBeforeInputEvent = true;
   6932 
   6933  if (!IsBeforeInputEventEnabled()) {
   6934    return NS_OK;
   6935  }
   6936 
   6937  if (mEditorBase.IsHTMLEditor()) {
   6938    mEditorBase.AsHTMLEditor()->mLastCollapsibleWhiteSpaceAppendedTextNode =
   6939        nullptr;
   6940  }
   6941 
   6942  // If we're called from OnCompositionEnd(), we shouldn't dispatch
   6943  // "beforeinput" event since the preceding OnCompositionChange() call has
   6944  // already dispatched "beforeinput" event for this.
   6945  if (mEditAction == EditAction::eCommitComposition ||
   6946      mEditAction == EditAction::eCancelComposition) {
   6947    return NS_OK;
   6948  }
   6949 
   6950  RefPtr<Element> targetElement = mEditorBase.GetInputEventTargetElement();
   6951  if (!targetElement) {
   6952    // If selection is not in editable element and it is outside of any
   6953    // editing hosts, there may be no target element to dispatch `beforeinput`
   6954    // event.  In this case, the caller shouldn't keep handling the edit
   6955    // action since web apps cannot override it with `beforeinput` event
   6956    // listener, but for backward compatibility, we should return a special
   6957    // success code instead of error.
   6958    MOZ_LOG(gEventLog, LogLevel::Error,
   6959            ("%p %s: Failed dispatching \"beforeinput\" event due to no target",
   6960             &mEditorBase,
   6961             mEditorBase.mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor"));
   6962    return NS_OK;
   6963  }
   6964  OwningNonNull<EditorBase> editorBase = mEditorBase;
   6965  EditorInputType inputType = ToInputType(mEditAction);
   6966  if (editorBase->IsHTMLEditor() && mTargetRanges.IsEmpty()) {
   6967    // If the edit action will delete selected ranges, compute the range
   6968    // strictly.
   6969    if (MayEditActionDeleteAroundCollapsedSelection(mEditAction) ||
   6970        (!editorBase->SelectionRef().IsCollapsed() &&
   6971         MayEditActionDeleteSelection(mEditAction))) {
   6972      if (!editorBase
   6973               ->FlushPendingNotificationsIfToHandleDeletionWithFrameSelection(
   6974                   aDeleteDirectionAndAmount)) {
   6975        NS_WARNING(
   6976            "Flusing pending notifications caused destroying the editor");
   6977        return NS_ERROR_EDITOR_DESTROYED;
   6978      }
   6979 
   6980      AutoClonedSelectionRangeArray rangesToDelete(editorBase->SelectionRef());
   6981      if (!rangesToDelete.Ranges().IsEmpty()) {
   6982        nsresult rv = MOZ_KnownLive(editorBase->AsHTMLEditor())
   6983                          ->ComputeTargetRanges(aDeleteDirectionAndAmount,
   6984                                                rangesToDelete);
   6985        if (rv == NS_ERROR_EDITOR_DESTROYED) {
   6986          NS_WARNING("HTMLEditor::ComputeTargetRanges() destroyed the editor");
   6987          return NS_ERROR_EDITOR_DESTROYED;
   6988        }
   6989        if (rv == NS_ERROR_EDITOR_NO_EDITABLE_RANGE) {
   6990          // For now, keep dispatching `beforeinput` event even if no selection
   6991          // range can be editable.
   6992          rv = NS_OK;
   6993        }
   6994        NS_WARNING_ASSERTION(
   6995            NS_SUCCEEDED(rv),
   6996            "HTMLEditor::ComputeTargetRanges() failed, but ignored");
   6997        for (auto& range : rangesToDelete.Ranges()) {
   6998          RefPtr<StaticRange> staticRange =
   6999              StaticRange::Create(range, IgnoreErrors());
   7000          if (NS_WARN_IF(!staticRange)) {
   7001            continue;
   7002          }
   7003          AppendTargetRange(*staticRange);
   7004        }
   7005      }
   7006    }
   7007    // Otherwise, just set target ranges to selection ranges.
   7008    else if (MayHaveTargetRangesOnHTMLEditor(inputType)) {
   7009      if (uint32_t rangeCount = editorBase->SelectionRef().RangeCount()) {
   7010        mTargetRanges.SetCapacity(rangeCount);
   7011        for (const uint32_t i : IntegerRange(rangeCount)) {
   7012          MOZ_ASSERT(editorBase->SelectionRef().RangeCount() == rangeCount);
   7013          const nsRange* range = editorBase->SelectionRef().GetRangeAt(i);
   7014          MOZ_ASSERT(range);
   7015          MOZ_ASSERT(range->IsPositioned());
   7016          if (MOZ_UNLIKELY(NS_WARN_IF(!range)) ||
   7017              MOZ_UNLIKELY(NS_WARN_IF(!range->IsPositioned()))) {
   7018            continue;
   7019          }
   7020          // Now, we need to fix the offset of target range because it may
   7021          // be referred after modifying the DOM tree and range boundaries
   7022          // of `range` may have not computed offset yet.
   7023          RefPtr<StaticRange> targetRange = StaticRange::Create(
   7024              range->GetStartContainer(), range->StartOffset(),
   7025              range->GetEndContainer(), range->EndOffset(), IgnoreErrors());
   7026          if (NS_WARN_IF(!targetRange) ||
   7027              NS_WARN_IF(!targetRange->IsPositioned())) {
   7028            continue;
   7029          }
   7030          mTargetRanges.AppendElement(std::move(targetRange));
   7031        }
   7032      }
   7033    }
   7034  }
   7035  nsEventStatus status = nsEventStatus_eIgnore;
   7036  InputEventOptions::NeverCancelable neverCancelable =
   7037      mMakeBeforeInputEventNonCancelable
   7038          ? InputEventOptions::NeverCancelable::Yes
   7039          : InputEventOptions::NeverCancelable::No;
   7040  WillDispatchInputEvent();
   7041  MOZ_LOG(gEventLog, LogLevel::Info,
   7042          ("%p %s: Dispatching \"beforeinput\" event: { inputType=\"%s\" }...",
   7043           editorBase.get(),
   7044           editorBase->mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   7045           ToString(ToInputType(GetEditAction())).c_str()));
   7046  nsresult rv = nsContentUtils::DispatchInputEvent(
   7047      targetElement, eEditorBeforeInput, inputType, editorBase,
   7048      mDataTransfer
   7049          ? InputEventOptions(mDataTransfer, std::move(mTargetRanges),
   7050                              neverCancelable)
   7051          : InputEventOptions(mData, std::move(mTargetRanges), neverCancelable),
   7052      &status);
   7053  MOZ_LOG(gEventLog, LogLevel::Info,
   7054          ("%p %s: Dispatched \"beforeinput\" event: { inputType=\"%s\" }, "
   7055           "defaultPrevented=%s",
   7056           editorBase.get(),
   7057           editorBase->mIsHTMLEditorClass ? "HTMLEditor" : "TextEditor",
   7058           ToString(ToInputType(GetEditAction())).c_str(),
   7059           status == nsEventStatus_eConsumeNoDefault ? "true" : "false"));
   7060  DidDispatchInputEvent();
   7061  if (NS_WARN_IF(mEditorBase.Destroyed())) {
   7062    return NS_ERROR_EDITOR_DESTROYED;
   7063  }
   7064  if (NS_FAILED(rv)) {
   7065    NS_WARNING("nsContentUtils::DispatchInputEvent() failed");
   7066    return rv;
   7067  }
   7068  if (status == nsEventStatus_eConsumeNoDefault) {
   7069    MarkEditActionCanceled();
   7070    return NS_ERROR_EDITOR_ACTION_CANCELED;
   7071  }
   7072 
   7073  nsCOMPtr<nsIWidget> widget = editorBase->GetWidget();
   7074  if (!StaticPrefs::dom_events_textevent_enabled() ||
   7075      !targetElement->IsInComposedDoc() || !widget) {
   7076    return NS_OK;
   7077  }
   7078  nsString textInputData;
   7079  RefPtr<DataTransfer> textInputDataTransfer;
   7080  switch (inputType) {
   7081    case EditorInputType::eInsertCompositionText:
   7082      // If the composition is still being composed, we should not dispatch
   7083      // textInput event, but we need to dispatch it for the last composition
   7084      // change because web apps should know the inserting commit string as
   7085      // same as input from keyboard.
   7086      if (mEditAction == EditAction::eUpdateComposition) {
   7087        return NS_OK;
   7088      }
   7089      [[fallthrough]];
   7090    case EditorInputType::eInsertText:
   7091      textInputData = mData;
   7092      break;
   7093    case EditorInputType::eInsertFromDrop:
   7094    case EditorInputType::eInsertFromPaste:
   7095    case EditorInputType::eInsertFromPasteAsQuotation:
   7096      if (mDataTransfer) {
   7097        textInputDataTransfer = mDataTransfer;
   7098      } else {
   7099        textInputData = mData;
   7100      }
   7101      break;
   7102    case EditorInputType::eInsertLineBreak:
   7103    case EditorInputType::eInsertParagraph:
   7104      // Don't dispatch `textInput` on <input> because Chrome does not do it.
   7105      // On the other hand, we need to dispatch it on <textarea> and
   7106      // contenteditable.
   7107      if (mEditorBase.IsTextEditor() && mEditorBase.IsSingleLineEditor()) {
   7108        return NS_OK;
   7109      }
   7110      textInputData.Assign(u'\n');
   7111      break;
   7112    default:
   7113      return NS_OK;
   7114  }
   7115 
   7116  InternalLegacyTextEvent textEvent(true, eLegacyTextInput, widget);
   7117  textEvent.mData = std::move(textInputData);
   7118  textEvent.mDataTransfer = std::move(textInputDataTransfer);
   7119  textEvent.mInputType = inputType;
   7120  // Make it always cancelable even though we ignore it when inserting or
   7121  // deleting composition.  This is compatible with Chrome.
   7122  // However, if and only if it's unsafe, let's set it not cancelable because of
   7123  // asynchronous dispatching.
   7124  textEvent.mFlags.mCancelable = nsContentUtils::IsSafeToRunScript();
   7125 
   7126  status = nsEventStatus_eIgnore;
   7127  rv = AsyncEventDispatcher::RunDOMEventWhenSafe(*targetElement, textEvent,
   7128                                                 &status);
   7129  if (NS_WARN_IF(mEditorBase.Destroyed())) {
   7130    return NS_ERROR_EDITOR_DESTROYED;
   7131  }
   7132  if (NS_FAILED(rv)) {
   7133    NS_WARNING("AsyncEventDispatcher::RunDOMEventWhenSafe() failed");
   7134    return rv;
   7135  }
   7136  if (status == nsEventStatus_eConsumeNoDefault) {
   7137    MarkEditActionCanceled();
   7138    return NS_ERROR_EDITOR_ACTION_CANCELED;
   7139  }
   7140  return NS_OK;
   7141 }
   7142 
   7143 /*****************************************************************************
   7144 * mozilla::EditorBase::TopLevelEditSubActionData
   7145 *****************************************************************************/
   7146 
   7147 nsresult EditorBase::TopLevelEditSubActionData::AddNodeToChangedRange(
   7148    const HTMLEditor& aHTMLEditor, nsINode& aNode) {
   7149  EditorRawDOMPoint startPoint(&aNode);
   7150  EditorRawDOMPoint endPoint(&aNode);
   7151  DebugOnly<bool> advanced = endPoint.AdvanceOffset();
   7152  NS_WARNING_ASSERTION(advanced, "Failed to set endPoint to next to aNode");
   7153  nsresult rv = AddRangeToChangedRange(aHTMLEditor, startPoint, endPoint);
   7154  NS_WARNING_ASSERTION(
   7155      NS_SUCCEEDED(rv),
   7156      "TopLevelEditSubActionData::AddRangeToChangedRange() failed");
   7157  return rv;
   7158 }
   7159 
   7160 nsresult EditorBase::TopLevelEditSubActionData::AddPointToChangedRange(
   7161    const HTMLEditor& aHTMLEditor, const EditorRawDOMPoint& aPoint) {
   7162  nsresult rv = AddRangeToChangedRange(aHTMLEditor, aPoint, aPoint);
   7163  NS_WARNING_ASSERTION(
   7164      NS_SUCCEEDED(rv),
   7165      "TopLevelEditSubActionData::AddRangeToChangedRange() failed");
   7166  return rv;
   7167 }
   7168 
   7169 nsresult EditorBase::TopLevelEditSubActionData::AddRangeToChangedRange(
   7170    const HTMLEditor& aHTMLEditor, const EditorRawDOMPoint& aStart,
   7171    const EditorRawDOMPoint& aEnd) {
   7172  if (NS_WARN_IF(!aStart.IsSet()) || NS_WARN_IF(!aEnd.IsSet())) {
   7173    return NS_ERROR_INVALID_ARG;
   7174  }
   7175 
   7176  if (!aHTMLEditor.IsDescendantOfRoot(aStart.GetContainer()) ||
   7177      (aStart.GetContainer() != aEnd.GetContainer() &&
   7178       !aHTMLEditor.IsDescendantOfRoot(aEnd.GetContainer()))) {
   7179    return NS_OK;
   7180  }
   7181 
   7182  // If mChangedRange hasn't been set, we can just set it to `aStart` and
   7183  // `aEnd`.
   7184  if (!mChangedRange->IsPositioned()) {
   7185    nsresult rv = mChangedRange->SetStartAndEnd(aStart.ToRawRangeBoundary(),
   7186                                                aEnd.ToRawRangeBoundary());
   7187    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetStartAndEnd() failed");
   7188    return rv;
   7189  }
   7190 
   7191  Maybe<int32_t> relation =
   7192      mChangedRange->StartRef().IsSet()
   7193          ? nsContentUtils::ComparePoints(mChangedRange->StartRef(),
   7194                                          aStart.ToRawRangeBoundary())
   7195          : Some(1);
   7196  if (NS_WARN_IF(!relation)) {
   7197    return NS_ERROR_FAILURE;
   7198  }
   7199 
   7200  // If aStart is before start of mChangedRange, reset the start.
   7201  if (*relation > 0) {
   7202    ErrorResult error;
   7203    mChangedRange->SetStart(aStart.ToRawRangeBoundary(), error);
   7204    if (error.Failed()) {
   7205      NS_WARNING("nsRange::SetStart() failed");
   7206      return error.StealNSResult();
   7207    }
   7208  }
   7209 
   7210  relation = mChangedRange->EndRef().IsSet()
   7211                 ? nsContentUtils::ComparePoints(mChangedRange->EndRef(),
   7212                                                 aEnd.ToRawRangeBoundary())
   7213                 : Some(1);
   7214  if (NS_WARN_IF(!relation)) {
   7215    return NS_ERROR_FAILURE;
   7216  }
   7217 
   7218  // If aEnd is after end of mChangedRange, reset the end.
   7219  if (*relation < 0) {
   7220    ErrorResult error;
   7221    mChangedRange->SetEnd(aEnd.ToRawRangeBoundary(), error);
   7222    if (error.Failed()) {
   7223      NS_WARNING("nsRange::SetEnd() failed");
   7224      return error.StealNSResult();
   7225    }
   7226  }
   7227 
   7228  return NS_OK;
   7229 }
   7230 
   7231 void EditorBase::TopLevelEditSubActionData::DidCreateElement(
   7232    EditorBase& aEditorBase, Element& aNewElement) {
   7233  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7234 
   7235  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7236    return;  // We have not been initialized yet or already been destroyed.
   7237  }
   7238 
   7239  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7240    return;  // Temporarily disabled by edit sub-action handler.
   7241  }
   7242 
   7243  DebugOnly<nsresult> rvIgnored =
   7244      AddNodeToChangedRange(*aEditorBase.AsHTMLEditor(), aNewElement);
   7245  NS_WARNING_ASSERTION(
   7246      NS_SUCCEEDED(rvIgnored),
   7247      "TopLevelEditSubActionData::AddNodeToChangedRange() failed, but ignored");
   7248 }
   7249 
   7250 void EditorBase::TopLevelEditSubActionData::DidInsertContent(
   7251    EditorBase& aEditorBase, nsIContent& aNewContent) {
   7252  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7253 
   7254  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7255    return;  // We have not been initialized yet or already been destroyed.
   7256  }
   7257 
   7258  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7259    return;  // Temporarily disabled by edit sub-action handler.
   7260  }
   7261 
   7262  DebugOnly<nsresult> rvIgnored =
   7263      AddNodeToChangedRange(*aEditorBase.AsHTMLEditor(), aNewContent);
   7264  NS_WARNING_ASSERTION(
   7265      NS_SUCCEEDED(rvIgnored),
   7266      "TopLevelEditSubActionData::AddNodeToChangedRange() failed, but ignored");
   7267 }
   7268 
   7269 void EditorBase::TopLevelEditSubActionData::WillDeleteContent(
   7270    EditorBase& aEditorBase, nsIContent& aRemovingContent) {
   7271  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7272 
   7273  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7274    return;  // We have not been initialized yet or already been destroyed.
   7275  }
   7276 
   7277  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7278    return;  // Temporarily disabled by edit sub-action handler.
   7279  }
   7280 
   7281  DebugOnly<nsresult> rvIgnored =
   7282      AddNodeToChangedRange(*aEditorBase.AsHTMLEditor(), aRemovingContent);
   7283  NS_WARNING_ASSERTION(
   7284      NS_SUCCEEDED(rvIgnored),
   7285      "TopLevelEditSubActionData::AddNodeToChangedRange() failed, but ignored");
   7286 }
   7287 
   7288 void EditorBase::TopLevelEditSubActionData::DidSplitContent(
   7289    EditorBase& aEditorBase, nsIContent& aSplitContent,
   7290    nsIContent& aNewContent) {
   7291  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7292 
   7293  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7294    return;  // We have not been initialized yet or already been destroyed.
   7295  }
   7296 
   7297  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7298    return;  // Temporarily disabled by edit sub-action handler.
   7299  }
   7300 
   7301  DebugOnly<nsresult> rvIgnored = AddRangeToChangedRange(
   7302      *aEditorBase.AsHTMLEditor(), EditorRawDOMPoint::AtEndOf(aSplitContent),
   7303      EditorRawDOMPoint::AtEndOf(aNewContent));
   7304  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   7305                       "TopLevelEditSubActionData::AddRangeToChangedRange() "
   7306                       "failed, but ignored");
   7307 }
   7308 
   7309 void EditorBase::TopLevelEditSubActionData::DidJoinContents(
   7310    EditorBase& aEditorBase, const EditorRawDOMPoint& aJoinedPoint) {
   7311  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7312 
   7313  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7314    return;  // We have not been initialized yet or already been destroyed.
   7315  }
   7316 
   7317  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7318    return;  // Temporarily disabled by edit sub-action handler.
   7319  }
   7320 
   7321  DebugOnly<nsresult> rvIgnored =
   7322      AddPointToChangedRange(*aEditorBase.AsHTMLEditor(), aJoinedPoint);
   7323  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   7324                       "TopLevelEditSubActionData::AddPointToChangedRange() "
   7325                       "failed, but ignored");
   7326 }
   7327 
   7328 void EditorBase::TopLevelEditSubActionData::DidInsertText(
   7329    EditorBase& aEditorBase, const EditorRawDOMPoint& aInsertionBegin,
   7330    const EditorRawDOMPoint& aInsertionEnd) {
   7331  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7332 
   7333  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7334    return;  // We have not been initialized yet or already been destroyed.
   7335  }
   7336 
   7337  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7338    return;  // Temporarily disabled by edit sub-action handler.
   7339  }
   7340 
   7341  DebugOnly<nsresult> rvIgnored = AddRangeToChangedRange(
   7342      *aEditorBase.AsHTMLEditor(), aInsertionBegin, aInsertionEnd);
   7343  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   7344                       "TopLevelEditSubActionData::AddRangeToChangedRange() "
   7345                       "failed, but ignored");
   7346 }
   7347 
   7348 void EditorBase::TopLevelEditSubActionData::DidDeleteText(
   7349    EditorBase& aEditorBase, const EditorRawDOMPoint& aStartInTextNode) {
   7350  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7351 
   7352  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7353    return;  // We have not been initialized yet or already been destroyed.
   7354  }
   7355 
   7356  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7357    return;  // Temporarily disabled by edit sub-action handler.
   7358  }
   7359 
   7360  DebugOnly<nsresult> rvIgnored =
   7361      AddPointToChangedRange(*aEditorBase.AsHTMLEditor(), aStartInTextNode);
   7362  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   7363                       "TopLevelEditSubActionData::AddPointToChangedRange() "
   7364                       "failed, but ignored");
   7365 }
   7366 
   7367 void EditorBase::TopLevelEditSubActionData::WillDeleteRange(
   7368    EditorBase& aEditorBase, const EditorRawDOMPoint& aStart,
   7369    const EditorRawDOMPoint& aEnd) {
   7370  MOZ_ASSERT(aEditorBase.AsHTMLEditor());
   7371  MOZ_ASSERT(aStart.IsSet());
   7372  MOZ_ASSERT(aEnd.IsSet());
   7373 
   7374  if (!aEditorBase.mInitSucceeded || aEditorBase.Destroyed()) {
   7375    return;  // We have not been initialized yet or already been destroyed.
   7376  }
   7377 
   7378  if (!aEditorBase.EditSubActionDataRef().mAdjustChangedRangeFromListener) {
   7379    return;  // Temporarily disabled by edit sub-action handler.
   7380  }
   7381 
   7382  // XXX Looks like that this is wrong.  We delete multiple selection ranges
   7383  //     once, but this adds only first range into the changed range.
   7384  //     Anyway, we should take the range as an argument.
   7385  DebugOnly<nsresult> rvIgnored =
   7386      AddRangeToChangedRange(*aEditorBase.AsHTMLEditor(), aStart, aEnd);
   7387  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   7388                       "TopLevelEditSubActionData::AddRangeToChangedRange() "
   7389                       "failed, but ignored");
   7390 }
   7391 
   7392 nsPIDOMWindowOuter* EditorBase::GetWindow() const {
   7393  return mDocument ? mDocument->GetWindow() : nullptr;
   7394 }
   7395 
   7396 nsPIDOMWindowInner* EditorBase::GetInnerWindow() const {
   7397  return mDocument ? mDocument->GetInnerWindow() : nullptr;
   7398 }
   7399 
   7400 PresShell* EditorBase::GetPresShell() const {
   7401  return mDocument ? mDocument->GetPresShell() : nullptr;
   7402 }
   7403 
   7404 nsPresContext* EditorBase::GetPresContext() const {
   7405  PresShell* presShell = GetPresShell();
   7406  return presShell ? presShell->GetPresContext() : nullptr;
   7407 }
   7408 
   7409 already_AddRefed<nsCaret> EditorBase::GetCaret() const {
   7410  PresShell* presShell = GetPresShell();
   7411  if (NS_WARN_IF(!presShell)) {
   7412    return nullptr;
   7413  }
   7414  return presShell->GetCaret();
   7415 }
   7416 
   7417 nsISelectionController* EditorBase::GetSelectionController() const {
   7418  if (mSelectionController) {
   7419    return mSelectionController;
   7420  }
   7421  if (!mDocument) {
   7422    return nullptr;
   7423  }
   7424  return mDocument->GetPresShell();
   7425 }
   7426 
   7427 bool EditorBase::ArePreservingSelection() const {
   7428  return IsEditActionDataAvailable() && SavedSelectionRef().RangeCount();
   7429 }
   7430 
   7431 void EditorBase::PreserveSelectionAcrossActions() {
   7432  MOZ_ASSERT(IsEditActionDataAvailable());
   7433 
   7434  SavedSelectionRef().SaveSelection(SelectionRef());
   7435  RangeUpdaterRef().RegisterSelectionState(SavedSelectionRef());
   7436 }
   7437 
   7438 nsresult EditorBase::RestorePreservedSelection() {
   7439  MOZ_ASSERT(IsEditActionDataAvailable());
   7440 
   7441  if (!SavedSelectionRef().RangeCount()) {
   7442    // XXX Returning error when it does not store is odd because no selection
   7443    //     ranges is not illegal case in general.
   7444    return NS_ERROR_FAILURE;
   7445  }
   7446  DebugOnly<nsresult> rvIgnored =
   7447      SavedSelectionRef().RestoreSelection(SelectionRef());
   7448  NS_WARNING_ASSERTION(
   7449      NS_SUCCEEDED(rvIgnored),
   7450      "SelectionState::RestoreSelection() failed, but ignored");
   7451  StopPreservingSelection();
   7452  return NS_OK;
   7453 }
   7454 
   7455 void EditorBase::StopPreservingSelection() {
   7456  MOZ_ASSERT(IsEditActionDataAvailable());
   7457 
   7458  RangeUpdaterRef().DropSelectionState(SavedSelectionRef());
   7459  SavedSelectionRef().RemoveAllRanges();
   7460 }
   7461 
   7462 nsresult EditorBase::GetDataFromDataTransferOrClipboard(
   7463    DataTransfer* aDataTransfer, nsITransferable* aTransferable,
   7464    nsIClipboard::ClipboardType aClipboardType) const {
   7465  MOZ_ASSERT(aTransferable);
   7466  if (aDataTransfer) {
   7467    MOZ_ASSERT(aDataTransfer->ClipboardType() == Some(aClipboardType));
   7468    bool readFromClipboard = true;
   7469    nsresult rv = [aDataTransfer, aTransferable,
   7470                   &readFromClipboard]() -> nsresult {
   7471      nsIClipboardDataSnapshot* snapshot =
   7472          aDataTransfer->GetClipboardDataSnapshot();
   7473      MOZ_ASSERT(snapshot);
   7474      bool snapshotIsValid = false;
   7475      snapshot->GetValid(&snapshotIsValid);
   7476      // By default, if we have a valid snapshot, we should only read from that
   7477      // and not fall back to the clipboard to avoid bypassing a BLOCK result
   7478      // from Content Analysis (bug 1936204). There are some exceptions which
   7479      // are dealt with later, however.
   7480      readFromClipboard = !snapshotIsValid;
   7481      if (!snapshotIsValid) {
   7482        NS_WARNING(
   7483            "DataTransfer::GetClipboardDataSnapshot() is not valid, falling "
   7484            "back "
   7485            "to clipboard");
   7486        return NS_ERROR_FAILURE;
   7487      }
   7488      AutoTArray<nsCString, 10> transferableFlavors;
   7489      nsresult rv =
   7490          aTransferable->FlavorsTransferableCanImport(transferableFlavors);
   7491      if (NS_FAILED(rv)) {
   7492        NS_WARNING("nsITransferable::FlavorsTransferableCanImport() failed");
   7493        return rv;
   7494      }
   7495      if (transferableFlavors.Length() == 1) {
   7496        // avoid creating unneeded temporary transferables
   7497        rv = snapshot->GetDataSync(aTransferable);
   7498        if (NS_FAILED(rv)) {
   7499          NS_WARNING("nsIClipboardDataSnapshot::GetDataSync() failed");
   7500        }
   7501        // If this fails, it may be because the snapshot was invalid and we
   7502        // didn't detect it until now, so fall back to reading the clipboard.
   7503        readFromClipboard = rv == NS_ERROR_NOT_AVAILABLE;
   7504        return rv;
   7505      }
   7506      AutoTArray<nsCString, 5> snapshotFlavors;
   7507      rv = snapshot->GetFlavorList(snapshotFlavors);
   7508      if (NS_FAILED(rv)) {
   7509        NS_WARNING("nsIClipboardDataSnapshot::GetFlavorList() failed");
   7510        return rv;
   7511      }
   7512      for (const auto& transferableFlavor : transferableFlavors) {
   7513        if (snapshotFlavors.Contains(transferableFlavor)) {
   7514          AutoTArray<nsCString, 1> singleTypeArray{transferableFlavor};
   7515          auto singleTransferableToCheck =
   7516              ContentParent::CreateClipboardTransferable(singleTypeArray);
   7517          if (singleTransferableToCheck.isErr()) {
   7518            NS_WARNING("Failed to CreateClipboardTransferable()");
   7519            return singleTransferableToCheck.unwrapErr();
   7520          }
   7521          nsCOMPtr<nsITransferable> singleTransferable =
   7522              singleTransferableToCheck.unwrap();
   7523          rv = snapshot->GetDataSync(singleTransferable);
   7524          if (NS_FAILED(rv)) {
   7525            NS_WARNING("nsIClipboardDataSnapshot::GetDataSync() failed");
   7526            // If this fails, it may be because the snapshot was invalid and we
   7527            // didn't detect it until now, so fall back to reading the
   7528            // clipboard.
   7529            readFromClipboard = rv == NS_ERROR_NOT_AVAILABLE;
   7530            return rv;
   7531          }
   7532          nsCOMPtr<nsISupports> data;
   7533          rv = singleTransferable->GetTransferData(transferableFlavor.get(),
   7534                                                   getter_AddRefs(data));
   7535          if (NS_FAILED(rv)) {
   7536            NS_WARNING("nsITransferable::GetTransferData() failed");
   7537            return rv;
   7538          }
   7539          rv = aTransferable->SetTransferData(transferableFlavor.get(), data);
   7540          if (NS_FAILED(rv)) {
   7541            NS_WARNING("nsITransferable::SetTransferData() failed");
   7542            return rv;
   7543          }
   7544          return NS_OK;
   7545        }
   7546      }
   7547      // The snapshot doesn't have any relevant data. Leave aTransferable empty
   7548      // but return NS_OK since the operation did succeed (there just isn't any
   7549      // data) and some tests expect this.
   7550      return NS_OK;
   7551    }();
   7552    // If the operation failed, only fall back to the clipboard if indicated.
   7553    if (NS_SUCCEEDED(rv) || !readFromClipboard) {
   7554      return rv;
   7555    }
   7556  }
   7557 
   7558  // Get Clipboard Service
   7559  nsresult rv;
   7560  nsCOMPtr<nsIClipboard> clipboard =
   7561      do_GetService("@mozilla.org/widget/clipboard;1", &rv);
   7562  if (NS_FAILED(rv)) {
   7563    NS_WARNING("Failed to get nsIClipboard service");
   7564    return rv;
   7565  }
   7566 
   7567  auto* windowContext = GetDocument()->GetWindowContext();
   7568  if (!windowContext) {
   7569    NS_WARNING("No window context");
   7570    return NS_ERROR_FAILURE;
   7571  }
   7572  rv = clipboard->GetData(aTransferable, aClipboardType, windowContext);
   7573  if (NS_FAILED(rv)) {
   7574    NS_WARNING("nsIClipboard::GetData() failed");
   7575    return rv;
   7576  }
   7577  return NS_OK;
   7578 }
   7579 }  // namespace mozilla