tor-browser

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

HTMLEditor.cpp (294046B)


      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 "HTMLEditor.h"
      7 #include "HTMLEditHelpers.h"
      8 #include "HTMLEditorInlines.h"
      9 #include "HTMLEditorNestedClasses.h"
     10 
     11 #include "AutoClonedRangeArray.h"
     12 #include "AutoSelectionRestorer.h"
     13 #include "CSSEditUtils.h"
     14 #include "EditAction.h"
     15 #include "EditorBase.h"
     16 #include "EditorDOMAPIWrapper.h"
     17 #include "EditorDOMPoint.h"
     18 #include "EditorLineBreak.h"
     19 #include "EditorUtils.h"
     20 #include "ErrorList.h"
     21 #include "HTMLEditorEventListener.h"
     22 #include "HTMLEditUtils.h"
     23 #include "InsertNodeTransaction.h"
     24 #include "JoinNodesTransaction.h"
     25 #include "MoveNodeTransaction.h"
     26 #include "PendingStyles.h"
     27 #include "ReplaceTextTransaction.h"
     28 #include "SplitNodeTransaction.h"
     29 #include "WhiteSpaceVisibilityKeeper.h"
     30 #include "WSRunScanner.h"
     31 
     32 #include "mozilla/ComposerCommandsUpdater.h"
     33 #include "mozilla/ContentIterator.h"
     34 #include "mozilla/DebugOnly.h"
     35 #include "mozilla/EditorForwards.h"
     36 #include "mozilla/Encoding.h"  // for Encoding
     37 #include "mozilla/FlushType.h"
     38 #include "mozilla/IMEStateManager.h"
     39 #include "mozilla/IntegerRange.h"  // for IntegerRange
     40 #include "mozilla/mozInlineSpellChecker.h"
     41 #include "mozilla/Preferences.h"
     42 #include "mozilla/PresShell.h"
     43 #include "mozilla/StaticPrefs_editor.h"
     44 #include "mozilla/StyleSheet.h"
     45 #include "mozilla/StyleSheetInlines.h"
     46 #include "mozilla/glean/EditorLibeditorMetrics.h"
     47 #include "mozilla/TextControlElement.h"
     48 #include "mozilla/TextEditor.h"
     49 #include "mozilla/TextEvents.h"
     50 #include "mozilla/TextServicesDocument.h"
     51 #include "mozilla/ToString.h"
     52 #include "mozilla/css/Loader.h"
     53 #include "mozilla/dom/AncestorIterator.h"
     54 #include "mozilla/dom/Attr.h"
     55 #include "mozilla/dom/BorrowedAttrInfo.h"
     56 #include "mozilla/dom/CharacterDataBuffer.h"
     57 #include "mozilla/dom/DocumentFragment.h"
     58 #include "mozilla/dom/DocumentInlines.h"
     59 #include "mozilla/dom/Element.h"
     60 #include "mozilla/dom/ElementInlines.h"
     61 #include "mozilla/dom/Event.h"
     62 #include "mozilla/dom/EventTarget.h"
     63 #include "mozilla/dom/HTMLAnchorElement.h"
     64 #include "mozilla/dom/HTMLBodyElement.h"
     65 #include "mozilla/dom/HTMLBRElement.h"
     66 #include "mozilla/dom/HTMLButtonElement.h"
     67 #include "mozilla/dom/HTMLSummaryElement.h"
     68 #include "mozilla/dom/NameSpaceConstants.h"
     69 #include "mozilla/dom/Selection.h"
     70 
     71 #include "nsContentList.h"
     72 #include "nsContentUtils.h"
     73 #include "nsCRT.h"
     74 #include "nsDebug.h"
     75 #include "nsElementTable.h"
     76 #include "nsFocusManager.h"
     77 #include "nsGenericHTMLElement.h"
     78 #include "nsGkAtoms.h"
     79 #include "nsHTMLDocument.h"
     80 #include "nsIContent.h"
     81 #include "nsIContentInlines.h"  // for nsINode::IsInDesignMode()
     82 #include "nsIEditActionListener.h"
     83 #include "nsIFrame.h"
     84 #include "nsIPrincipal.h"
     85 #include "nsISelectionController.h"
     86 #include "nsIURI.h"
     87 #include "nsIWidget.h"
     88 #include "nsNetUtil.h"
     89 #include "nsPresContext.h"
     90 #include "nsPrintfCString.h"
     91 #include "nsPIDOMWindow.h"
     92 #include "nsStyledElement.h"
     93 #include "nsUnicharUtils.h"
     94 
     95 namespace mozilla {
     96 
     97 using namespace dom;
     98 using namespace widget;
     99 
    100 LazyLogModule gHTMLEditorFocusLog("HTMLEditorFocus");
    101 
    102 using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
    103 using LeafNodeType = HTMLEditUtils::LeafNodeType;
    104 using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
    105 using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
    106 
    107 // Some utilities to handle overloading of "A" tag for link and named anchor.
    108 static bool IsLinkTag(const nsAtom& aTagName) {
    109  return &aTagName == nsGkAtoms::href;
    110 }
    111 
    112 static bool IsNamedAnchorTag(const nsAtom& aTagName) {
    113  return &aTagName == nsGkAtoms::anchor;
    114 }
    115 
    116 // Helper struct for DoJoinNodes() and DoSplitNode().
    117 struct MOZ_STACK_CLASS SavedRange final {
    118  RefPtr<Selection> mSelection;
    119  nsCOMPtr<nsINode> mStartContainer;
    120  nsCOMPtr<nsINode> mEndContainer;
    121  uint32_t mStartOffset = 0;
    122  uint32_t mEndOffset = 0;
    123 };
    124 
    125 /******************************************************************************
    126 * HTMLEditor
    127 *****************************************************************************/
    128 
    129 template Result<CreateContentResult, nsresult>
    130 HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
    131    nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert,
    132    SplitAtEdges aSplitAtEdges);
    133 template Result<CreateElementResult, nsresult>
    134 HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
    135    Element& aContentToInsert, const EditorDOMPoint& aPointToInsert,
    136    SplitAtEdges aSplitAtEdges);
    137 template Result<CreateTextResult, nsresult>
    138 HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
    139    Text& aContentToInsert, const EditorDOMPoint& aPointToInsert,
    140    SplitAtEdges aSplitAtEdges);
    141 
    142 MOZ_RUNINIT HTMLEditor::InitializeInsertingElement
    143    HTMLEditor::DoNothingForNewElement =
    144        [](HTMLEditor&, Element&, const EditorDOMPoint&) { return NS_OK; };
    145 
    146 MOZ_RUNINIT HTMLEditor::InitializeInsertingElement
    147    HTMLEditor::InsertNewBRElement =
    148        [](HTMLEditor& aHTMLEditor, Element& aNewElement,
    149           const EditorDOMPoint&) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    150          MOZ_ASSERT(!aNewElement.IsInComposedDoc());
    151          Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
    152              aHTMLEditor.InsertLineBreak(WithTransaction::No,
    153                                          LineBreakType::BRElement,
    154                                          EditorDOMPoint(&aNewElement, 0u));
    155          if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
    156            NS_WARNING(
    157                "HTMLEditor::InsertLineBreak(WithTransaction::No, "
    158                "LineBreakType::BRElement) failed");
    159            return insertBRElementResultOrError.unwrapErr();
    160          }
    161          insertBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
    162          return NS_OK;
    163        };
    164 
    165 // static
    166 Result<CreateElementResult, nsresult>
    167 HTMLEditor::AppendNewElementToInsertingElement(
    168    HTMLEditor& aHTMLEditor, const nsStaticAtom& aTagName, Element& aNewElement,
    169    const InitializeInsertingElement& aInitializer) {
    170  MOZ_ASSERT(!aNewElement.IsInComposedDoc());
    171  Result<CreateElementResult, nsresult> createNewElementResult =
    172      aHTMLEditor.CreateAndInsertElement(
    173          WithTransaction::No, const_cast<nsStaticAtom&>(aTagName),
    174          EditorDOMPoint(&aNewElement, 0u), aInitializer);
    175  NS_WARNING_ASSERTION(
    176      createNewElementResult.isOk(),
    177      "HTMLEditor::CreateAndInsertElement(WithTransaction::No) failed");
    178  return createNewElementResult;
    179 }
    180 
    181 // static
    182 Result<CreateElementResult, nsresult>
    183 HTMLEditor::AppendNewElementWithBRToInsertingElement(
    184    HTMLEditor& aHTMLEditor, const nsStaticAtom& aTagName,
    185    Element& aNewElement) {
    186  MOZ_ASSERT(!aNewElement.IsInComposedDoc());
    187  Result<CreateElementResult, nsresult> createNewElementWithBRResult =
    188      HTMLEditor::AppendNewElementToInsertingElement(
    189          aHTMLEditor, aTagName, aNewElement, HTMLEditor::InsertNewBRElement);
    190  NS_WARNING_ASSERTION(
    191      createNewElementWithBRResult.isOk(),
    192      "HTMLEditor::AppendNewElementToInsertingElement() failed");
    193  return createNewElementWithBRResult;
    194 }
    195 
    196 MOZ_RUNINIT HTMLEditor::AttributeFilter HTMLEditor::CopyAllAttributes =
    197    [](HTMLEditor&, const Element&, const Element&, int32_t, const nsAtom&,
    198       nsString&) { return true; };
    199 MOZ_RUNINIT HTMLEditor::AttributeFilter HTMLEditor::CopyAllAttributesExceptId =
    200    [](HTMLEditor&, const Element&, const Element&, int32_t aNamespaceID,
    201       const nsAtom& aAttrName, nsString&) {
    202      return aNamespaceID != kNameSpaceID_None || &aAttrName != nsGkAtoms::id;
    203    };
    204 MOZ_RUNINIT HTMLEditor::AttributeFilter HTMLEditor::CopyAllAttributesExceptDir =
    205    [](HTMLEditor&, const Element&, const Element&, int32_t aNamespaceID,
    206       const nsAtom& aAttrName, nsString&) {
    207      return aNamespaceID != kNameSpaceID_None || &aAttrName != nsGkAtoms::dir;
    208    };
    209 MOZ_RUNINIT HTMLEditor::AttributeFilter
    210    HTMLEditor::CopyAllAttributesExceptIdAndDir =
    211        [](HTMLEditor&, const Element&, const Element&, int32_t aNamespaceID,
    212           const nsAtom& aAttrName, nsString&) {
    213          return !(
    214              aNamespaceID == kNameSpaceID_None &&
    215              (&aAttrName == nsGkAtoms::id || &aAttrName == nsGkAtoms::dir));
    216        };
    217 
    218 HTMLEditor::HTMLEditor(const Document& aDocument)
    219    : EditorBase(EditorBase::EditorType::HTML),
    220      mCRInParagraphCreatesParagraph(false),
    221      mIsObjectResizingEnabled(
    222          StaticPrefs::editor_resizing_enabled_by_default()),
    223      mIsResizing(false),
    224      mPreserveRatio(false),
    225      mResizedObjectIsAnImage(false),
    226      mIsAbsolutelyPositioningEnabled(
    227          StaticPrefs::editor_positioning_enabled_by_default()),
    228      mResizedObjectIsAbsolutelyPositioned(false),
    229      mGrabberClicked(false),
    230      mIsMoving(false),
    231      mSnapToGridEnabled(false),
    232      mIsInlineTableEditingEnabled(
    233          StaticPrefs::editor_inline_table_editing_enabled_by_default()),
    234      mIsCSSPrefChecked(StaticPrefs::editor_use_css()),
    235      mOriginalX(0),
    236      mOriginalY(0),
    237      mResizedObjectX(0),
    238      mResizedObjectY(0),
    239      mResizedObjectWidth(0),
    240      mResizedObjectHeight(0),
    241      mResizedObjectMarginLeft(0),
    242      mResizedObjectMarginTop(0),
    243      mResizedObjectBorderLeft(0),
    244      mResizedObjectBorderTop(0),
    245      mXIncrementFactor(0),
    246      mYIncrementFactor(0),
    247      mWidthIncrementFactor(0),
    248      mHeightIncrementFactor(0),
    249      mInfoXIncrement(20),
    250      mInfoYIncrement(20),
    251      mPositionedObjectX(0),
    252      mPositionedObjectY(0),
    253      mPositionedObjectWidth(0),
    254      mPositionedObjectHeight(0),
    255      mPositionedObjectMarginLeft(0),
    256      mPositionedObjectMarginTop(0),
    257      mPositionedObjectBorderLeft(0),
    258      mPositionedObjectBorderTop(0),
    259      mGridSize(0),
    260      mDefaultParagraphSeparator(ParagraphSeparator::div) {}
    261 
    262 HTMLEditor::~HTMLEditor() {
    263  glean::htmleditors::with_beforeinput_listeners
    264      .EnumGet(static_cast<glean::htmleditors::WithBeforeinputListenersLabel>(
    265          MayHaveBeforeInputEventListenersForTelemetry() ? 1 : 0))
    266      .Add();
    267  glean::htmleditors::overridden_by_beforeinput_listeners
    268      .EnumGet(static_cast<
    269               glean::htmleditors::OverriddenByBeforeinputListenersLabel>(
    270          mHasBeforeInputBeenCanceled ? 1 : 0))
    271      .Add();
    272  glean::htmleditors::with_mutation_observers_without_beforeinput_listeners
    273      .EnumGet(static_cast<
    274               glean::htmleditors::
    275                   WithMutationObserversWithoutBeforeinputListenersLabel>(
    276          !MayHaveBeforeInputEventListenersForTelemetry() &&
    277                  MutationObserverHasObservedNodeForTelemetry()
    278              ? 1
    279              : 0))
    280      .Add();
    281 
    282  mPendingStylesToApplyToNewContent = nullptr;
    283 
    284  if (mDisabledLinkHandling) {
    285    if (Document* doc = GetDocument()) {
    286      doc->SetLinkHandlingEnabled(mOldLinkHandlingEnabled);
    287    }
    288  }
    289 
    290  RemoveEventListeners();
    291 
    292  HideAnonymousEditingUIs();
    293 }
    294 
    295 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLEditor)
    296 
    297 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLEditor, EditorBase)
    298  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStylesToApplyToNewContent)
    299  NS_IMPL_CYCLE_COLLECTION_UNLINK(mComposerCommandsUpdater)
    300  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChangedRangeForTopLevelEditSubAction)
    301  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaddingBRElementForEmptyEditor)
    302  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastCollapsibleWhiteSpaceAppendedTextNode)
    303  tmp->HideAnonymousEditingUIs();
    304 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    305 
    306 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLEditor, EditorBase)
    307  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStylesToApplyToNewContent)
    308  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mComposerCommandsUpdater)
    309  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChangedRangeForTopLevelEditSubAction)
    310  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaddingBRElementForEmptyEditor)
    311  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastCollapsibleWhiteSpaceAppendedTextNode)
    312 
    313  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle)
    314  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle)
    315  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopRightHandle)
    316  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLeftHandle)
    317  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRightHandle)
    318  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomLeftHandle)
    319  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomHandle)
    320  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomRightHandle)
    321  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActivatedHandle)
    322  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingShadow)
    323  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingInfo)
    324  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizedObject)
    325 
    326  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbsolutelyPositionedObject)
    327  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGrabber)
    328  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPositioningShadow)
    329 
    330  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineEditedCell)
    331  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnBeforeButton)
    332  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveColumnButton)
    333  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnAfterButton)
    334  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowBeforeButton)
    335  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveRowButton)
    336  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowAfterButton)
    337 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    338 
    339 NS_IMPL_ADDREF_INHERITED(HTMLEditor, EditorBase)
    340 NS_IMPL_RELEASE_INHERITED(HTMLEditor, EditorBase)
    341 
    342 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLEditor)
    343  NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor)
    344  NS_INTERFACE_MAP_ENTRY(nsIHTMLObjectResizer)
    345  NS_INTERFACE_MAP_ENTRY(nsIHTMLAbsPosEditor)
    346  NS_INTERFACE_MAP_ENTRY(nsIHTMLInlineTableEditor)
    347  NS_INTERFACE_MAP_ENTRY(nsITableEditor)
    348  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    349  NS_INTERFACE_MAP_ENTRY(nsIEditorMailSupport)
    350 NS_INTERFACE_MAP_END_INHERITING(EditorBase)
    351 
    352 nsresult HTMLEditor::Init(Document& aDocument,
    353                          ComposerCommandsUpdater& aComposerCommandsUpdater,
    354                          uint32_t aFlags) {
    355  MOZ_ASSERT(!mInitSucceeded,
    356             "HTMLEditor::Init() called again without calling PreDestroy()?");
    357 
    358  MOZ_DIAGNOSTIC_ASSERT(!mComposerCommandsUpdater ||
    359                        mComposerCommandsUpdater == &aComposerCommandsUpdater);
    360  mComposerCommandsUpdater = &aComposerCommandsUpdater;
    361 
    362  RefPtr<PresShell> presShell = aDocument.GetPresShell();
    363  if (NS_WARN_IF(!presShell)) {
    364    return NS_ERROR_FAILURE;
    365  }
    366  nsresult rv = InitInternal(aDocument, nullptr, *presShell, aFlags);
    367  if (NS_FAILED(rv)) {
    368    NS_WARNING("EditorBase::InitInternal() failed");
    369    return rv;
    370  }
    371 
    372  // Init mutation observer
    373  aDocument.AddMutationObserverUnlessExists(this);
    374 
    375  if (!mRootElement) {
    376    UpdateRootElement();
    377  }
    378 
    379  // disable Composer-only features
    380  if (IsMailEditor()) {
    381    DebugOnly<nsresult> rvIgnored = SetAbsolutePositioningEnabled(false);
    382    NS_WARNING_ASSERTION(
    383        NS_SUCCEEDED(rvIgnored),
    384        "HTMLEditor::SetAbsolutePositioningEnabled(false) failed, but ignored");
    385    rvIgnored = SetSnapToGridEnabled(false);
    386    NS_WARNING_ASSERTION(
    387        NS_SUCCEEDED(rvIgnored),
    388        "HTMLEditor::SetSnapToGridEnabled(false) failed, but ignored");
    389  }
    390 
    391  // disable links
    392  Document* document = GetDocument();
    393  if (NS_WARN_IF(!document)) {
    394    return NS_ERROR_FAILURE;
    395  }
    396  if (!IsPlaintextMailComposer() && !IsInteractionAllowed()) {
    397    mDisabledLinkHandling = true;
    398    mOldLinkHandlingEnabled = document->LinkHandlingEnabled();
    399    document->SetLinkHandlingEnabled(false);
    400  }
    401 
    402  // init the type-in state
    403  mPendingStylesToApplyToNewContent = new PendingStyles();
    404 
    405  AutoEditActionDataSetter editActionData(*this, EditAction::eInitializing);
    406  if (NS_WARN_IF(!editActionData.CanHandle())) {
    407    return NS_ERROR_FAILURE;
    408  }
    409 
    410  rv = InitEditorContentAndSelection();
    411  if (NS_FAILED(rv)) {
    412    NS_WARNING("HTMLEditor::InitEditorContentAndSelection() failed");
    413    // XXX Sholdn't we expose `NS_ERROR_EDITOR_DESTROYED` even though this
    414    //     is a public method?
    415    return EditorBase::ToGenericNSResult(rv);
    416  }
    417 
    418  // Throw away the old transaction manager if this is not the first time that
    419  // we're initializing the editor.
    420  ClearUndoRedo();
    421  EnableUndoRedo();  // FYI: Creating mTransactionManager in this call
    422 
    423  if (mTransactionManager) {
    424    mTransactionManager->Attach(*this);
    425  }
    426 
    427  MOZ_ASSERT(!mInitSucceeded, "HTMLEditor::Init() shouldn't be nested");
    428  mInitSucceeded = true;
    429  editActionData.OnEditorInitialized();
    430  return NS_OK;
    431 }
    432 
    433 nsresult HTMLEditor::PostCreate() {
    434  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    435  if (NS_WARN_IF(!editActionData.CanHandle())) {
    436    return NS_ERROR_NOT_INITIALIZED;
    437  }
    438 
    439  nsresult rv = PostCreateInternal();
    440  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    441                       "EditorBase::PostCreatInternal() failed");
    442  return rv;
    443 }
    444 
    445 void HTMLEditor::PreDestroy() {
    446  if (mDidPreDestroy) {
    447    return;
    448  }
    449 
    450  mInitSucceeded = false;
    451 
    452  // FYI: Cannot create AutoEditActionDataSetter here.  However, it does not
    453  //      necessary for the methods called by the following code.
    454 
    455  RefPtr<Document> document = GetDocument();
    456  if (document) {
    457    document->RemoveMutationObserver(this);
    458  }
    459 
    460  // Clean up after our anonymous content -- we don't want these nodes to
    461  // stay around (which they would, since the frames have an owning reference).
    462  PresShell* presShell = GetPresShell();
    463  if (presShell && presShell->IsDestroying()) {
    464    // Just destroying PresShell now.
    465    // We have to keep UI elements of anonymous content until PresShell
    466    // is destroyed.
    467    RefPtr<HTMLEditor> self = this;
    468    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
    469        "HTMLEditor::PreDestroy", [self]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
    470          self->HideAnonymousEditingUIs();
    471        }));
    472  } else {
    473    // PresShell is alive or already gone.
    474    HideAnonymousEditingUIs();
    475  }
    476 
    477  mPaddingBRElementForEmptyEditor = nullptr;
    478 
    479  PreDestroyInternal();
    480 }
    481 
    482 bool HTMLEditor::IsStyleEditable() const {
    483  if (IsInDesignMode()) {
    484    return true;
    485  }
    486  if (IsPlaintextMailComposer()) {
    487    return false;
    488  }
    489  const Element* const editingHost = ComputeEditingHost(LimitInBodyElement::No);
    490  // Let's return true if there is no focused editing host for the backward
    491  // compatibility.
    492  return !editingHost || !editingHost->IsContentEditablePlainTextOnly();
    493 }
    494 
    495 NS_IMETHODIMP HTMLEditor::GetDocumentCharacterSet(nsACString& aCharacterSet) {
    496  nsresult rv = GetDocumentCharsetInternal(aCharacterSet);
    497  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    498                       "HTMLEditor::GetDocumentCharsetInternal() failed");
    499  return rv;
    500 }
    501 
    502 NS_IMETHODIMP HTMLEditor::SetDocumentCharacterSet(
    503    const nsACString& aCharacterSet) {
    504  AutoEditActionDataSetter editActionData(*this, EditAction::eSetCharacterSet);
    505  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    506  if (NS_FAILED(rv)) {
    507    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    508                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    509    return EditorBase::ToGenericNSResult(rv);
    510  }
    511 
    512  RefPtr<Document> document = GetDocument();
    513  if (NS_WARN_IF(!document)) {
    514    return EditorBase::ToGenericNSResult(NS_ERROR_NOT_INITIALIZED);
    515  }
    516  // This method is scriptable, so add-ons could pass in something other
    517  // than a canonical name.
    518  const Encoding* encoding = Encoding::ForLabelNoReplacement(aCharacterSet);
    519  if (!encoding) {
    520    NS_WARNING("Encoding::ForLabelNoReplacement() failed");
    521    return EditorBase::ToGenericNSResult(NS_ERROR_INVALID_ARG);
    522  }
    523  document->SetDocumentCharacterSet(WrapNotNull(encoding));
    524 
    525  // Update META charset element.
    526  if (UpdateMetaCharsetWithTransaction(*document, aCharacterSet)) {
    527    return NS_OK;
    528  }
    529 
    530  // Set attributes to the created element
    531  if (aCharacterSet.IsEmpty()) {
    532    return NS_OK;
    533  }
    534 
    535  RefPtr<nsContentList> headElementList =
    536      document->GetElementsByTagName(u"head"_ns);
    537  if (NS_WARN_IF(!headElementList)) {
    538    return NS_OK;
    539  }
    540 
    541  nsCOMPtr<nsIContent> primaryHeadElement = headElementList->Item(0);
    542  if (NS_WARN_IF(!primaryHeadElement)) {
    543    return NS_OK;
    544  }
    545 
    546  // Create a new meta charset tag
    547  Result<CreateElementResult, nsresult> createNewMetaElementResult =
    548      CreateAndInsertElement(
    549          WithTransaction::Yes, *nsGkAtoms::meta,
    550          EditorDOMPoint(primaryHeadElement, 0),
    551          [&aCharacterSet](HTMLEditor&, Element& aMetaElement,
    552                           const EditorDOMPoint&) {
    553            MOZ_ASSERT(!aMetaElement.IsInComposedDoc());
    554            DebugOnly<nsresult> rvIgnored =
    555                aMetaElement.SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv,
    556                                     u"Content-Type"_ns, false);
    557            NS_WARNING_ASSERTION(
    558                NS_SUCCEEDED(rvIgnored),
    559                "Element::SetAttr(nsGkAtoms::httpEquiv, \"Content-Type\", "
    560                "false) failed, but ignored");
    561            rvIgnored =
    562                aMetaElement.SetAttr(kNameSpaceID_None, nsGkAtoms::content,
    563                                     u"text/html;charset="_ns +
    564                                         NS_ConvertASCIItoUTF16(aCharacterSet),
    565                                     false);
    566            NS_WARNING_ASSERTION(
    567                NS_SUCCEEDED(rvIgnored),
    568                nsPrintfCString(
    569                    "Element::SetAttr(nsGkAtoms::content, "
    570                    "\"text/html;charset=%s\", false) failed, but ignored",
    571                    nsPromiseFlatCString(aCharacterSet).get())
    572                    .get());
    573            return NS_OK;
    574          });
    575  NS_WARNING_ASSERTION(createNewMetaElementResult.isOk(),
    576                       "HTMLEditor::CreateAndInsertElement(WithTransaction::"
    577                       "Yes, nsGkAtoms::meta) failed, but ignored");
    578  // Probably, we don't need to update selection in this case since we should
    579  // not put selection into <head> element.
    580  createNewMetaElementResult.inspect().IgnoreCaretPointSuggestion();
    581  return NS_OK;
    582 }
    583 
    584 bool HTMLEditor::UpdateMetaCharsetWithTransaction(
    585    Document& aDocument, const nsACString& aCharacterSet) {
    586  // get a list of META tags
    587  RefPtr<nsContentList> metaElementList =
    588      aDocument.GetElementsByTagName(u"meta"_ns);
    589  if (NS_WARN_IF(!metaElementList)) {
    590    return false;
    591  }
    592 
    593  for (uint32_t i = 0; i < metaElementList->Length(true); ++i) {
    594    RefPtr<Element> metaElement = metaElementList->Item(i)->AsElement();
    595    MOZ_ASSERT(metaElement);
    596 
    597    nsAutoString currentValue;
    598    metaElement->GetAttr(nsGkAtoms::httpEquiv, currentValue);
    599 
    600    if (!FindInReadable(u"content-type"_ns, currentValue,
    601                        nsCaseInsensitiveStringComparator)) {
    602      continue;
    603    }
    604 
    605    metaElement->GetAttr(nsGkAtoms::content, currentValue);
    606 
    607    constexpr auto charsetEquals = u"charset="_ns;
    608    nsAString::const_iterator originalStart, start, end;
    609    originalStart = currentValue.BeginReading(start);
    610    currentValue.EndReading(end);
    611    if (!FindInReadable(charsetEquals, start, end,
    612                        nsCaseInsensitiveStringComparator)) {
    613      continue;
    614    }
    615 
    616    // set attribute to <original prefix> charset=text/html
    617    nsresult rv = SetAttributeWithTransaction(
    618        *metaElement, *nsGkAtoms::content,
    619        Substring(originalStart, start) + charsetEquals +
    620            NS_ConvertASCIItoUTF16(aCharacterSet));
    621    NS_WARNING_ASSERTION(
    622        NS_SUCCEEDED(rv),
    623        "EditorBase::SetAttributeWithTransaction(nsGkAtoms::content) failed");
    624    return NS_SUCCEEDED(rv);
    625  }
    626  return false;
    627 }
    628 
    629 NS_IMETHODIMP HTMLEditor::NotifySelectionChanged(Document* aDocument,
    630                                                 Selection* aSelection,
    631                                                 int16_t aReason,
    632                                                 int32_t aAmount) {
    633  if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
    634    return NS_ERROR_INVALID_ARG;
    635  }
    636 
    637  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    638  if (NS_WARN_IF(!editActionData.CanHandle())) {
    639    return NS_ERROR_NOT_INITIALIZED;
    640  }
    641 
    642  if (mPendingStylesToApplyToNewContent) {
    643    RefPtr<PendingStyles> pendingStyles = mPendingStylesToApplyToNewContent;
    644    pendingStyles->OnSelectionChange(*this, aReason);
    645 
    646    // We used a class which derived from nsISelectionListener to call
    647    // HTMLEditor::RefreshEditingUI().  The lifetime of the class was
    648    // exactly same as mPendingStylesToApplyToNewContent.  So, call it only when
    649    // mPendingStylesToApplyToNewContent is not nullptr.
    650    if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
    651                    nsISelectionListener::KEYPRESS_REASON |
    652                    nsISelectionListener::SELECTALL_REASON)) &&
    653        aSelection) {
    654      // the selection changed and we need to check if we have to
    655      // hide and/or redisplay resizing handles
    656      // FYI: This is an XPCOM method.  So, the caller, Selection, guarantees
    657      //      the lifetime of this instance.  So, don't need to grab this with
    658      //      local variable.
    659      DebugOnly<nsresult> rv = RefreshEditingUI();
    660      NS_WARNING_ASSERTION(
    661          NS_SUCCEEDED(rv),
    662          "HTMLEditor::RefreshEditingUI() failed, but ignored");
    663    }
    664  }
    665 
    666  if (mComposerCommandsUpdater) {
    667    RefPtr<ComposerCommandsUpdater> updater = mComposerCommandsUpdater;
    668    updater->OnSelectionChange();
    669  }
    670 
    671  nsresult rv = EditorBase::NotifySelectionChanged(aDocument, aSelection,
    672                                                   aReason, aAmount);
    673  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    674                       "EditorBase::NotifySelectionChanged() failed");
    675  return rv;
    676 }
    677 
    678 void HTMLEditor::UpdateRootElement() {
    679  // Use the HTML documents body element as the editor root if we didn't
    680  // get a root element during initialization.
    681 
    682  mRootElement = GetBodyElement();
    683  if (!mRootElement) {
    684    RefPtr<Document> doc = GetDocument();
    685    if (doc) {
    686      // If there is no HTML body element,
    687      // we should use the document root element instead.
    688      mRootElement = doc->GetDocumentElement();
    689    }
    690    // else leave it null, for lack of anything better.
    691  }
    692 }
    693 
    694 nsresult HTMLEditor::FocusedElementOrDocumentBecomesEditable(
    695    Document& aDocument, Element* aElement) {
    696  MOZ_LOG(gHTMLEditorFocusLog, LogLevel::Info,
    697          ("%s(aDocument=%p, aElement=%s): mHasFocus=%s, mIsInDesignMode=%s, "
    698           "aDocument.IsInDesignMode()=%s, aElement->IsInDesignMode()=%s",
    699           __FUNCTION__, &aDocument, ToString(RefPtr{aElement}).c_str(),
    700           mHasFocus ? "true" : "false", mIsInDesignMode ? "true" : "false",
    701           aDocument.IsInDesignMode() ? "true" : "false",
    702           aElement ? (aElement->IsInDesignMode() ? "true" : "false") : "N/A"));
    703 
    704  const bool enteringInDesignMode =
    705      (aDocument.IsInDesignMode() && (!aElement || aElement->IsInDesignMode()));
    706 
    707  // If we should've already handled focus event, selection limiter should not
    708  // be set.  However, IMEStateManager is not notified the pseudo focus change
    709  // in this case. Therefore, we need to notify IMEStateManager of this.
    710  if (mHasFocus) {
    711    if (enteringInDesignMode) {
    712      mIsInDesignMode = true;
    713      return NS_OK;
    714    }
    715    // Although editor is already initialized due to re-used, ISM may not
    716    // create IME content observer yet. So we have to create it.
    717    Result<IMEState, nsresult> newStateOrError = GetPreferredIMEState();
    718    if (MOZ_UNLIKELY(newStateOrError.isErr())) {
    719      NS_WARNING("HTMLEditor::GetPreferredIMEState() failed");
    720      mIsInDesignMode = false;
    721      return NS_OK;
    722    }
    723    const RefPtr<Element> focusedElement = GetFocusedElement();
    724    if (focusedElement) {
    725      MOZ_ASSERT(focusedElement == aElement);
    726      TextControlElement* const textControlElement =
    727          TextControlElement::FromNode(focusedElement);
    728      if (textControlElement &&
    729          textControlElement->IsSingleLineTextControlOrTextArea()) {
    730        // Let's emulate blur first.
    731        DebugOnly<nsresult> rv = FinalizeSelection();
    732        NS_WARNING_ASSERTION(
    733            NS_SUCCEEDED(rv),
    734            "HTMLEditor::FinalizeSelection() failed, but ignored");
    735        mHasFocus = false;
    736        mIsInDesignMode = false;
    737      }
    738      IMEStateManager::UpdateIMEState(newStateOrError.unwrap(), focusedElement,
    739                                      *this);
    740      // XXX Do we need to notify focused TextEditor of focus?  In theory,
    741      // the TextEditor should get focus event in this case.
    742    }
    743    mIsInDesignMode = false;
    744    return NS_OK;
    745  }
    746 
    747  // If we should be in the design mode, we want to handle focus event fired
    748  // on the document node.  Therefore, we should emulate it here.
    749  if (enteringInDesignMode) {
    750    MOZ_ASSERT(&aDocument == GetDocument());
    751    nsresult rv = OnFocus(aDocument);
    752    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::OnFocus() failed");
    753    return rv;
    754  }
    755 
    756  if (NS_WARN_IF(!aElement)) {
    757    return NS_ERROR_INVALID_ARG;
    758  }
    759 
    760  // Otherwise, we should've already handled focus event on the element,
    761  // therefore, we need to emulate it here.
    762  MOZ_ASSERT(nsFocusManager::GetFocusedElementStatic() == aElement);
    763  nsresult rv = OnFocus(*aElement);
    764  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::OnFocus() failed");
    765 
    766  // Note that we don't need to call
    767  // IMEStateManager::MaybeOnEditableStateDisabled here because
    768  // EditorBase::OnFocus must have already been called IMEStateManager::OnFocus
    769  // if succeeded. And perhaps, it's okay that IME is not enabled when
    770  // HTMLEditor fails to start handling since nobody can handle composition
    771  // events anyway...
    772 
    773  return rv;
    774 }
    775 
    776 nsresult HTMLEditor::OnFocus(const nsINode& aOriginalEventTargetNode) {
    777  MOZ_LOG(gHTMLEditorFocusLog, LogLevel::Info,
    778          ("%s(aOriginalEventTarget=%s): mIsInDesignMode=%s, "
    779           "aOriginalEventTargetNode.IsInDesignMode()=%s",
    780           __FUNCTION__, ToString(RefPtr{&aOriginalEventTargetNode}).c_str(),
    781           mIsInDesignMode ? "true" : "false",
    782           aOriginalEventTargetNode.IsInDesignMode() ? "true" : "false"));
    783 
    784  // Before doing anything, we should check whether the original target is still
    785  // valid focus event target because it may have already lost focus.
    786  if (!CanKeepHandlingFocusEvent(aOriginalEventTargetNode)) {
    787    return NS_OK;
    788  }
    789 
    790  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    791  if (NS_WARN_IF(!editActionData.CanHandle())) {
    792    return NS_ERROR_FAILURE;
    793  }
    794 
    795  nsresult rv = EditorBase::OnFocus(aOriginalEventTargetNode);
    796  if (NS_FAILED(rv)) {
    797    NS_WARNING("EditorBase::OnFocus() failed");
    798    return rv;
    799  }
    800  mHasFocus = true;
    801  mIsInDesignMode = aOriginalEventTargetNode.IsInDesignMode();
    802  return NS_OK;
    803 }
    804 
    805 nsresult HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
    806    HTMLEditor* aHTMLEditor, Document& aDocument, Element* aElement) {
    807  MOZ_LOG(
    808      gHTMLEditorFocusLog, LogLevel::Info,
    809      ("%s(aHTMLEditor=%p, aDocument=%p, aElement=%s): "
    810       "aHTMLEditor->HasFocus()=%s, aHTMLEditor->IsInDesignMode()=%s, "
    811       "aDocument.IsInDesignMode()=%s, aElement->IsInDesignMode()=%s, "
    812       "nsFocusManager::GetFocusedElementStatic()=%s",
    813       __FUNCTION__, aHTMLEditor, &aDocument,
    814       ToString(RefPtr{aElement}).c_str(),
    815       aHTMLEditor ? (aHTMLEditor->HasFocus() ? "true" : "false") : "N/A",
    816       aHTMLEditor ? (aHTMLEditor->IsInDesignMode() ? "true" : "false") : "N/A",
    817       aDocument.IsInDesignMode() ? "true" : "false",
    818       aElement ? (aElement->IsInDesignMode() ? "true" : "false") : "N/A",
    819       ToString(RefPtr{nsFocusManager::GetFocusedElementStatic()}).c_str()));
    820 
    821  nsresult rv = [&]() MOZ_CAN_RUN_SCRIPT {
    822    // If HTMLEditor has not been created yet, we just need to adjust
    823    // IMEStateManager.  So, don't return error.
    824    if (!aHTMLEditor || !aHTMLEditor->HasFocus()) {
    825      return NS_OK;
    826    }
    827 
    828    // Let's emulate blur first.
    829    nsresult rv = aHTMLEditor->FinalizeSelection();
    830    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    831                         "HTMLEditor::FinalizeSelection() failed");
    832    aHTMLEditor->mHasFocus = false;
    833    aHTMLEditor->mIsInDesignMode = false;
    834 
    835    RefPtr<Element> focusedElement = nsFocusManager::GetFocusedElementStatic();
    836    if (focusedElement && !focusedElement->IsInComposedDoc()) {
    837      // nsFocusManager may keep storing the focused element even after
    838      // disconnected from the tree, but HTMLEditor cannot work with editable
    839      // nodes not in a composed document.  Therefore, we should treat no
    840      // focused element in the case.
    841      focusedElement = nullptr;
    842    }
    843    TextControlElement* const focusedTextControlElement =
    844        TextControlElement::FromNodeOrNull(focusedElement);
    845    if ((focusedElement && focusedElement->IsEditable() &&
    846         focusedElement->OwnerDoc() == aHTMLEditor->GetDocument() &&
    847         (!focusedTextControlElement ||
    848          !focusedTextControlElement->IsSingleLineTextControlOrTextArea())) ||
    849        (!focusedElement && aDocument.IsInDesignMode())) {
    850      // Then, the focused element is still editable, let's emulate focus to
    851      // make the editor be ready to handle input.
    852      DebugOnly<nsresult> rvIgnored = aHTMLEditor->OnFocus(
    853          focusedElement ? static_cast<nsINode&>(*focusedElement)
    854                         : static_cast<nsINode&>(aDocument));
    855      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    856                           "HTMLEditor::OnFocus() failed, but ignored");
    857    } else if (focusedTextControlElement &&
    858               focusedTextControlElement->IsSingleLineTextControlOrTextArea()) {
    859      if (const RefPtr<TextEditor> textEditor =
    860              focusedTextControlElement->GetExtantTextEditor()) {
    861        textEditor->OnFocus(*focusedElement);
    862      }
    863    }
    864    return rv;
    865  }();
    866 
    867  // If the element becomes not editable without focus change, IMEStateManager
    868  // does not have a chance to disable IME.  Therefore, (even if we fail to
    869  // handle the emulated blur/focus above,) we should notify IMEStateManager of
    870  // the editing state change.  Note that if the window of the HTMLEditor has
    871  // already lost focus, we don't need to do that and we should not touch the
    872  // other windows.
    873  if (const RefPtr<nsPresContext> presContext = aDocument.GetPresContext()) {
    874    const RefPtr<Element> focusedElementInDocument =
    875        Element::FromNodeOrNull(aDocument.GetUnretargetedFocusedContent());
    876    MOZ_ASSERT_IF(focusedElementInDocument,
    877                  focusedElementInDocument->GetPresContext(
    878                      Element::PresContextFor::eForComposedDoc));
    879    IMEStateManager::MaybeOnEditableStateDisabled(*presContext,
    880                                                  focusedElementInDocument);
    881  }
    882 
    883  return rv;
    884 }
    885 
    886 nsresult HTMLEditor::OnBlur(const EventTarget* aEventTarget) {
    887  MOZ_LOG(gHTMLEditorFocusLog, LogLevel::Info,
    888          ("%s(aEventTarget=%s): mHasFocus=%s, mIsInDesignMode=%s, "
    889           "aEventTarget->IsInDesignMode()=%s",
    890           __FUNCTION__, ToString(RefPtr{aEventTarget}).c_str(),
    891           mHasFocus ? "true" : "false", mIsInDesignMode ? "true" : "false",
    892           nsINode::FromEventTargetOrNull(aEventTarget)
    893               ? (nsINode::FromEventTarget(aEventTarget)->IsInDesignMode()
    894                      ? "true"
    895                      : "false")
    896               : "N/A"));
    897  const Element* eventTargetAsElement =
    898      Element::FromEventTargetOrNull(aEventTarget);
    899 
    900  // If another element already has focus, we should not maintain the selection
    901  // because we may not have the rights doing it.
    902  const Element* focusedElement = nsFocusManager::GetFocusedElementStatic();
    903  if (focusedElement && focusedElement != eventTargetAsElement) {
    904    // XXX If we had focus and new focused element is a text control, we may
    905    // need to notify focus of its TextEditor...
    906    mIsInDesignMode = false;
    907    mHasFocus = false;
    908    return NS_OK;
    909  }
    910 
    911  // If we're in the designMode and blur occurs, the target must be the document
    912  // node.  If a blur event is fired and the target is an element, it must be
    913  // delayed blur event at initializing the `HTMLEditor`.
    914  if (mIsInDesignMode && eventTargetAsElement &&
    915      eventTargetAsElement->IsInComposedDoc()) {
    916    return NS_OK;
    917  }
    918 
    919  nsresult rv = FinalizeSelection();
    920  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    921                       "EditorBase::FinalizeSelection() failed");
    922  mIsInDesignMode = false;
    923  mHasFocus = false;
    924  return rv;
    925 }
    926 
    927 Element* HTMLEditor::FindSelectionRoot(const nsINode& aNode) const {
    928  MOZ_ASSERT(aNode.IsDocument() || aNode.IsContent(),
    929             "aNode must be content or document node");
    930 
    931  if (NS_WARN_IF(!aNode.IsInComposedDoc())) {
    932    return nullptr;
    933  }
    934 
    935  if (aNode.IsInDesignMode()) {
    936    return GetDocument()->GetRootElement();
    937  }
    938 
    939  nsIContent* content = const_cast<nsIContent*>(aNode.AsContent());
    940  if (!content->HasFlag(NODE_IS_EDITABLE)) {
    941    // If the content is in read-write state but is not editable itself,
    942    // return it as the selection root.
    943    if (content->IsElement() &&
    944        content->AsElement()->State().HasState(ElementState::READWRITE)) {
    945      return content->AsElement();
    946    }
    947    return nullptr;
    948  }
    949 
    950  // For non-readonly editors we want to find the root of the editable subtree
    951  // containing aContent.
    952  return content->GetEditingHost();
    953 }
    954 
    955 bool HTMLEditor::EntireDocumentIsEditable() const {
    956  Document* document = GetDocument();
    957  return document && document->GetDocumentElement() &&
    958         (document->GetDocumentElement()->IsEditable() ||
    959          (document->GetBody() && document->GetBody()->IsEditable()));
    960 }
    961 
    962 void HTMLEditor::CreateEventListeners() {
    963  // Don't create the handler twice
    964  if (!mEventListener) {
    965    mEventListener = new HTMLEditorEventListener();
    966  }
    967 }
    968 
    969 nsresult HTMLEditor::InstallEventListeners() {
    970  // FIXME InstallEventListeners() should not be called if we failed to set
    971  // document or create an event listener.  So, these checks should be
    972  // MOZ_DIAGNOSTIC_ASSERT instead.
    973  MOZ_ASSERT(GetDocument());
    974  if (MOZ_UNLIKELY(!GetDocument()) || NS_WARN_IF(!mEventListener)) {
    975    return NS_ERROR_NOT_INITIALIZED;
    976  }
    977 
    978  // NOTE: HTMLEditor doesn't need to initialize mEventTarget here because
    979  // the target must be document node and it must be referenced as weak pointer.
    980 
    981  HTMLEditorEventListener* listener =
    982      reinterpret_cast<HTMLEditorEventListener*>(mEventListener.get());
    983  nsresult rv = listener->Connect(this);
    984  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    985                       "HTMLEditorEventListener::Connect() failed");
    986  return rv;
    987 }
    988 
    989 void HTMLEditor::Detach(
    990    const ComposerCommandsUpdater& aComposerCommandsUpdater) {
    991  MOZ_DIAGNOSTIC_ASSERT_IF(
    992      mComposerCommandsUpdater,
    993      &aComposerCommandsUpdater == mComposerCommandsUpdater);
    994  if (mComposerCommandsUpdater == &aComposerCommandsUpdater) {
    995    mComposerCommandsUpdater = nullptr;
    996    if (mTransactionManager) {
    997      mTransactionManager->Detach(*this);
    998    }
    999  }
   1000 }
   1001 
   1002 NS_IMETHODIMP HTMLEditor::BeginningOfDocument() {
   1003  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1004  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1005    return NS_ERROR_NOT_INITIALIZED;
   1006  }
   1007 
   1008  nsresult rv = MaybeCollapseSelectionAtFirstEditableNode(false);
   1009  NS_WARNING_ASSERTION(
   1010      NS_SUCCEEDED(rv),
   1011      "HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(false) failed");
   1012  return rv;
   1013 }
   1014 
   1015 NS_IMETHODIMP HTMLEditor::EndOfDocument() {
   1016  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1017  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1018    return NS_ERROR_NOT_INITIALIZED;
   1019  }
   1020  nsresult rv = CollapseSelectionToEndOfLastLeafNodeOfDocument();
   1021  NS_WARNING_ASSERTION(
   1022      NS_SUCCEEDED(rv),
   1023      "HTMLEditor::CollapseSelectionToEndOfLastLeafNodeOfDocument() failed");
   1024  // This is low level API for embedders and chrome script so that we can return
   1025  // raw error code here.
   1026  return rv;
   1027 }
   1028 
   1029 nsresult HTMLEditor::CollapseSelectionToEndOfLastLeafNodeOfDocument() const {
   1030  MOZ_ASSERT(IsEditActionDataAvailable());
   1031 
   1032  // We should do nothing with the result of GetRoot() if only a part of the
   1033  // document is editable.
   1034  if (!EntireDocumentIsEditable()) {
   1035    return NS_OK;
   1036  }
   1037 
   1038  RefPtr<Element> bodyOrDocumentElement = GetRoot();
   1039  if (NS_WARN_IF(!bodyOrDocumentElement)) {
   1040    return NS_ERROR_NULL_POINTER;
   1041  }
   1042 
   1043  auto pointToPutCaret = [&]() -> EditorRawDOMPoint {
   1044    nsCOMPtr<nsIContent> lastLeafContent = HTMLEditUtils::GetLastLeafContent(
   1045        *bodyOrDocumentElement, {LeafNodeType::OnlyLeafNode});
   1046    if (!lastLeafContent) {
   1047      return EditorRawDOMPoint::AtEndOf(*bodyOrDocumentElement);
   1048    }
   1049    // TODO: We should put caret into text node if it's visible.
   1050    return lastLeafContent->IsText() ||
   1051                   HTMLEditUtils::IsContainerNode(*lastLeafContent)
   1052               ? EditorRawDOMPoint::AtEndOf(*lastLeafContent)
   1053               : EditorRawDOMPoint(lastLeafContent);
   1054  }();
   1055  nsresult rv = CollapseSelectionTo(pointToPutCaret);
   1056  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1057                       "EditorBase::CollapseSelectionTo() failed");
   1058  return rv;
   1059 }
   1060 
   1061 void HTMLEditor::InitializeSelectionAncestorLimit(
   1062    Element& aAncestorLimit) const {
   1063  MOZ_ASSERT(IsEditActionDataAvailable());
   1064 
   1065  // Hack for initializing selection.
   1066  // HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode() will try to
   1067  // collapse selection at first editable text node or inline element which
   1068  // cannot have text nodes as its children.  However, selection has already
   1069  // set into the new editing host by user, we should not change it.  For
   1070  // solving this issue, we should do nothing if selection range is in active
   1071  // editing host except it's not collapsed at start of the editing host since
   1072  // aSelection.SetAncestorLimiter(aAncestorLimit) will collapse selection
   1073  // at start of the new limiter if focus node of aSelection is outside of the
   1074  // editing host.  However, we need to check here if selection is already
   1075  // collapsed at start of the editing host because it's possible JS to do it.
   1076  // In such case, we should not modify selection with calling
   1077  // MaybeCollapseSelectionAtFirstEditableNode().
   1078 
   1079  // Basically, we should try to collapse selection at first editable node
   1080  // in HTMLEditor.
   1081  bool tryToCollapseSelectionAtFirstEditableNode = true;
   1082  if (SelectionRef().RangeCount() == 1 && SelectionRef().IsCollapsed()) {
   1083    Element* editingHost = ComputeEditingHost();
   1084    const nsRange* range = SelectionRef().GetRangeAt(0);
   1085    if (range->GetStartContainer() == editingHost && !range->StartOffset()) {
   1086      // JS or user operation has already collapsed selection at start of
   1087      // the editing host.  So, we don't need to try to change selection
   1088      // in this case.
   1089      tryToCollapseSelectionAtFirstEditableNode = false;
   1090    }
   1091  }
   1092 
   1093  EditorBase::InitializeSelectionAncestorLimit(aAncestorLimit);
   1094 
   1095  // XXX Do we need to check if we still need to change selection?  E.g.,
   1096  //     we could have already lost focus while we're changing the ancestor
   1097  //     limiter because it may causes "selectionchange" event.
   1098  if (tryToCollapseSelectionAtFirstEditableNode) {
   1099    DebugOnly<nsresult> rvIgnored =
   1100        MaybeCollapseSelectionAtFirstEditableNode(true);
   1101    NS_WARNING_ASSERTION(
   1102        NS_SUCCEEDED(rvIgnored),
   1103        "HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(true) failed, "
   1104        "but ignored");
   1105  }
   1106 
   1107  // If the target is a text control element, we won't handle user input
   1108  // for the `TextEditor` in it.  However, we need to be open for `execCommand`.
   1109  // Therefore, we shouldn't set ancestor limit in this case.
   1110  // Note that we should do this once setting ancestor limiter for backward
   1111  // compatiblity of select events, etc.  (Selection should be collapsed into
   1112  // the text control element.)
   1113  if (aAncestorLimit.HasIndependentSelection()) {
   1114    SelectionRef().SetAncestorLimiter(nullptr);
   1115  }
   1116 }
   1117 
   1118 nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
   1119    bool aIgnoreIfSelectionInEditingHost) const {
   1120  MOZ_ASSERT(IsEditActionDataAvailable());
   1121 
   1122  RefPtr<Element> editingHost = ComputeEditingHost(LimitInBodyElement::No);
   1123  if (NS_WARN_IF(!editingHost)) {
   1124    return NS_OK;
   1125  }
   1126 
   1127  // If selection range is already in the editing host and the range is not
   1128  // start of the editing host, we shouldn't reset selection.  E.g., window
   1129  // is activated when the editor had focus before inactivated.
   1130  if (aIgnoreIfSelectionInEditingHost && SelectionRef().RangeCount() == 1) {
   1131    const nsRange* range = SelectionRef().GetRangeAt(0);
   1132    if (!range->Collapsed() ||
   1133        range->GetStartContainer() != editingHost.get() ||
   1134        range->StartOffset()) {
   1135      return NS_OK;
   1136    }
   1137  }
   1138 
   1139  for (nsIContent* leafContent = HTMLEditUtils::GetFirstLeafContent(
   1140           *editingHost,
   1141           {LeafNodeType::LeafNodeOrNonEditableNode,
   1142            LeafNodeType::LeafNodeOrChildBlock},
   1143           BlockInlineCheck::UseComputedDisplayStyle, editingHost);
   1144       leafContent;) {
   1145    // If we meet a non-editable node first, we should move caret to start
   1146    // of the container block or editing host.
   1147    if (!EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) {
   1148      MOZ_ASSERT(leafContent->GetParent());
   1149      MOZ_ASSERT(EditorUtils::IsEditableContent(*leafContent->GetParent(),
   1150                                                EditorType::HTML));
   1151      if (const Element* editableBlockElementOrInlineEditingHost =
   1152              HTMLEditUtils::GetAncestorElement(
   1153                  *leafContent,
   1154                  HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost,
   1155                  BlockInlineCheck::UseComputedDisplayStyle)) {
   1156        nsresult rv = CollapseSelectionTo(
   1157            EditorDOMPoint(editableBlockElementOrInlineEditingHost, 0));
   1158        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1159                             "EditorBase::CollapseSelectionTo() failed");
   1160        return rv;
   1161      }
   1162      NS_WARNING("Found leaf content did not have editable parent, why?");
   1163      return NS_ERROR_FAILURE;
   1164    }
   1165 
   1166    // When we meet an empty inline element, we should look for a next sibling.
   1167    // For example, if current editor is:
   1168    // <div contenteditable><span></span><b><br></b></div>
   1169    // then, we should put caret at the <br> element.  So, let's check if found
   1170    // node is an empty inline container element.
   1171    if (Element* leafElement = Element::FromNode(leafContent)) {
   1172      if (HTMLEditUtils::IsInlineContent(
   1173              *leafElement, BlockInlineCheck::UseComputedDisplayStyle) &&
   1174          !HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafElement) &&
   1175          HTMLEditUtils::CanNodeContain(*leafElement,
   1176                                        *nsGkAtoms::textTagName)) {
   1177        // Chromium collapses selection to start of the editing host when this
   1178        // is the last leaf content.  So, we don't need special handling here.
   1179        leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
   1180            *leafElement,
   1181            {LeafNodeType::LeafNodeOrNonEditableNode,
   1182             LeafNodeType::LeafNodeOrChildBlock},
   1183            BlockInlineCheck::UseComputedDisplayStyle, editingHost);
   1184        continue;
   1185      }
   1186    }
   1187 
   1188    if (Text* text = leafContent->GetAsText()) {
   1189      // If there is editable and visible text node, move caret at first of
   1190      // the visible character.
   1191      const WSScanResult scanResultInTextNode =
   1192          WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1193              {WSRunScanner::Option::OnlyEditableNodes},
   1194              EditorRawDOMPoint(text, 0));
   1195      if ((scanResultInTextNode.InVisibleOrCollapsibleCharacters() ||
   1196           scanResultInTextNode.ReachedPreformattedLineBreak()) &&
   1197          scanResultInTextNode.TextPtr() == text) {
   1198        nsresult rv = CollapseSelectionTo(
   1199            scanResultInTextNode.PointAtReachedContent<EditorRawDOMPoint>());
   1200        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1201                             "EditorBase::CollapseSelectionTo() failed");
   1202        return rv;
   1203      }
   1204      // If it's an invisible text node, keep scanning next leaf.
   1205      leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
   1206          *leafContent,
   1207          {LeafNodeType::LeafNodeOrNonEditableNode,
   1208           LeafNodeType::LeafNodeOrChildBlock},
   1209          BlockInlineCheck::UseComputedDisplayStyle, editingHost);
   1210      continue;
   1211    }
   1212 
   1213    // If there is editable <br> or something void element like <img>, <input>,
   1214    // <hr> etc, move caret before it.
   1215    if (!HTMLEditUtils::CanNodeContain(*leafContent, *nsGkAtoms::textTagName) ||
   1216        HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafContent)) {
   1217      MOZ_ASSERT(leafContent->GetParent());
   1218      if (EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) {
   1219        nsresult rv = CollapseSelectionTo(EditorDOMPoint(leafContent));
   1220        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1221                             "EditorBase::CollapseSelectionTo() failed");
   1222        return rv;
   1223      }
   1224      MOZ_ASSERT_UNREACHABLE(
   1225          "How do we reach editable leaf in non-editable element?");
   1226      // But if it's not editable, let's put caret at start of editing host
   1227      // for now.
   1228      nsresult rv = CollapseSelectionTo(EditorDOMPoint(editingHost, 0));
   1229      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1230                           "EditorBase::CollapseSelectionTo() failed");
   1231      return rv;
   1232    }
   1233 
   1234    // If we meet non-empty block element, we need to scan its child too.
   1235    if (HTMLEditUtils::IsBlockElement(
   1236            *leafContent, BlockInlineCheck::UseComputedDisplayStyle) &&
   1237        !HTMLEditUtils::IsEmptyNode(
   1238            *leafContent,
   1239            {EmptyCheckOption::TreatSingleBRElementAsVisible,
   1240             EmptyCheckOption::TreatNonEditableContentAsInvisible}) &&
   1241        !HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafContent)) {
   1242      leafContent = HTMLEditUtils::GetFirstLeafContent(
   1243          *leafContent,
   1244          {LeafNodeType::LeafNodeOrNonEditableNode,
   1245           LeafNodeType::LeafNodeOrChildBlock},
   1246          BlockInlineCheck::UseComputedDisplayStyle, editingHost);
   1247      continue;
   1248    }
   1249 
   1250    // Otherwise, we must meet an empty block element or a data node like
   1251    // comment node.  Let's ignore it.
   1252    leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
   1253        *leafContent,
   1254        {LeafNodeType::LeafNodeOrNonEditableNode,
   1255         LeafNodeType::LeafNodeOrChildBlock},
   1256        BlockInlineCheck::UseComputedDisplayStyle, editingHost);
   1257  }
   1258 
   1259  // If there is no visible/editable node except another block element in
   1260  // current editing host, we should move caret to very first of the editing
   1261  // host.
   1262  // XXX This may not make sense, but Chromium behaves so.  Therefore, the
   1263  //     reason why we do this is just compatibility with Chromium.
   1264  nsresult rv = CollapseSelectionTo(EditorDOMPoint(editingHost, 0));
   1265  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1266                       "EditorBase::CollapseSelectionTo() failed");
   1267  return rv;
   1268 }
   1269 
   1270 void HTMLEditor::PreHandleMouseDown(const MouseEvent& aMouseDownEvent) {
   1271  if (mPendingStylesToApplyToNewContent) {
   1272    // mPendingStylesToApplyToNewContent will be notified of selection change
   1273    // even if aMouseDownEvent is not an acceptable event for this editor.
   1274    // Therefore, we need to notify it of this event too.
   1275    mPendingStylesToApplyToNewContent->PreHandleMouseEvent(aMouseDownEvent);
   1276  }
   1277 }
   1278 
   1279 void HTMLEditor::PreHandleMouseUp(const MouseEvent& aMouseUpEvent) {
   1280  if (mPendingStylesToApplyToNewContent) {
   1281    // mPendingStylesToApplyToNewContent will be notified of selection change
   1282    // even if aMouseUpEvent is not an acceptable event for this editor.
   1283    // Therefore, we need to notify it of this event too.
   1284    mPendingStylesToApplyToNewContent->PreHandleMouseEvent(aMouseUpEvent);
   1285  }
   1286 }
   1287 
   1288 void HTMLEditor::PreHandleSelectionChangeCommand(Command aCommand) {
   1289  if (mPendingStylesToApplyToNewContent) {
   1290    mPendingStylesToApplyToNewContent->PreHandleSelectionChangeCommand(
   1291        aCommand);
   1292  }
   1293 }
   1294 
   1295 void HTMLEditor::PostHandleSelectionChangeCommand(Command aCommand) {
   1296  if (!mPendingStylesToApplyToNewContent) {
   1297    return;
   1298  }
   1299 
   1300  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   1301  if (!editActionData.CanHandle()) {
   1302    return;
   1303  }
   1304  mPendingStylesToApplyToNewContent->PostHandleSelectionChangeCommand(*this,
   1305                                                                      aCommand);
   1306 }
   1307 
   1308 nsresult HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
   1309  // NOTE: When you change this method, you should also change:
   1310  //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
   1311  if (NS_WARN_IF(!aKeyboardEvent)) {
   1312    return NS_ERROR_UNEXPECTED;
   1313  }
   1314 
   1315  if (IsReadonly()) {
   1316    HandleKeyPressEventInReadOnlyMode(*aKeyboardEvent);
   1317    return NS_OK;
   1318  }
   1319 
   1320  MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
   1321             "HandleKeyPressEvent gets non-keypress event");
   1322 
   1323  switch (aKeyboardEvent->mKeyCode) {
   1324    case NS_VK_META:
   1325    case NS_VK_WIN:
   1326    case NS_VK_SHIFT:
   1327    case NS_VK_CONTROL:
   1328    case NS_VK_ALT:
   1329      // FYI: This shouldn't occur since modifier key shouldn't cause eKeyPress
   1330      //      event.
   1331      aKeyboardEvent->PreventDefault();
   1332      return NS_OK;
   1333 
   1334    case NS_VK_BACK:
   1335    case NS_VK_DELETE: {
   1336      nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
   1337      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1338                           "EditorBase::HandleKeyPressEvent() failed");
   1339      return rv;
   1340    }
   1341    case NS_VK_TAB: {
   1342      // Basically, "Tab" key be used only for focus navigation.
   1343      // FYI: In web apps, this is always true.
   1344      if (IsTabbable()) {
   1345        return NS_OK;
   1346      }
   1347 
   1348      // If we're in the plaintext mode, and not tabbable editor, let's
   1349      // insert a horizontal tabulation.
   1350      if (IsPlaintextMailComposer()) {
   1351        if (aKeyboardEvent->IsShift() || aKeyboardEvent->IsControl() ||
   1352            aKeyboardEvent->IsAlt() || aKeyboardEvent->IsMeta()) {
   1353          return NS_OK;
   1354        }
   1355 
   1356        // else we insert the tab straight through
   1357        aKeyboardEvent->PreventDefault();
   1358        nsresult rv = OnInputText(u"\t"_ns);
   1359        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1360                             "EditorBase::OnInputText(\\t) failed");
   1361        return rv;
   1362      }
   1363 
   1364      // Otherwise, e.g., we're an embedding editor in chrome, we can handle
   1365      // "Tab" key as an input.
   1366      if (aKeyboardEvent->IsControl() || aKeyboardEvent->IsAlt() ||
   1367          aKeyboardEvent->IsMeta()) {
   1368        return NS_OK;
   1369      }
   1370 
   1371      RefPtr<Selection> selection = GetSelection();
   1372      if (NS_WARN_IF(!selection) || NS_WARN_IF(!selection->RangeCount())) {
   1373        return NS_ERROR_FAILURE;
   1374      }
   1375 
   1376      nsINode* startContainer = selection->GetRangeAt(0)->GetStartContainer();
   1377      MOZ_ASSERT(startContainer);
   1378      if (!startContainer->IsContent()) {
   1379        break;
   1380      }
   1381 
   1382      const Element* editableBlockElement =
   1383          HTMLEditUtils::GetInclusiveAncestorElement(
   1384              *startContainer->AsContent(),
   1385              HTMLEditUtils::ClosestEditableBlockElement,
   1386              BlockInlineCheck::UseComputedDisplayOutsideStyle);
   1387      if (!editableBlockElement) {
   1388        break;
   1389      }
   1390 
   1391      // If selection is in a table element, we need special handling.
   1392      if (HTMLEditUtils::IsAnyTableElementExceptColumnElement(
   1393              *editableBlockElement)) {
   1394        Result<EditActionResult, nsresult> result =
   1395            HandleTabKeyPressInTable(aKeyboardEvent);
   1396        if (MOZ_UNLIKELY(result.isErr())) {
   1397          NS_WARNING("HTMLEditor::HandleTabKeyPressInTable() failed");
   1398          return EditorBase::ToGenericNSResult(result.unwrapErr());
   1399        }
   1400        if (!result.inspect().Handled()) {
   1401          return NS_OK;
   1402        }
   1403        nsresult rv = ScrollSelectionFocusIntoView();
   1404        NS_WARNING_ASSERTION(
   1405            NS_SUCCEEDED(rv),
   1406            "EditorBase::ScrollSelectionFocusIntoView() failed");
   1407        return EditorBase::ToGenericNSResult(rv);
   1408      }
   1409 
   1410      // If selection is in an list item element, treat it as indent or outdent.
   1411      if (HTMLEditUtils::IsListItemElement(*editableBlockElement)) {
   1412        aKeyboardEvent->PreventDefault();
   1413        if (!aKeyboardEvent->IsShift()) {
   1414          nsresult rv = IndentAsAction();
   1415          NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1416                               "HTMLEditor::IndentAsAction() failed");
   1417          return EditorBase::ToGenericNSResult(rv);
   1418        }
   1419        nsresult rv = OutdentAsAction();
   1420        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1421                             "HTMLEditor::OutdentAsAction() failed");
   1422        return EditorBase::ToGenericNSResult(rv);
   1423      }
   1424 
   1425      // If only "Tab" key is pressed in normal context, just treat it as
   1426      // horizontal tab character input.
   1427      if (aKeyboardEvent->IsShift()) {
   1428        return NS_OK;
   1429      }
   1430      aKeyboardEvent->PreventDefault();
   1431      nsresult rv = OnInputText(u"\t"_ns);
   1432      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1433                           "EditorBase::OnInputText(\\t) failed");
   1434      return EditorBase::ToGenericNSResult(rv);
   1435    }
   1436    case NS_VK_RETURN: {
   1437      if (!aKeyboardEvent->IsInputtingLineBreak()) {
   1438        return NS_OK;
   1439      }
   1440      // Anyway consume the event even if we cannot handle it actually because
   1441      // we've already checked whether the an editing host has focus.
   1442      aKeyboardEvent->PreventDefault();
   1443      const RefPtr<Element> editingHost =
   1444          ComputeEditingHost(LimitInBodyElement::No);
   1445      if (NS_WARN_IF(!editingHost)) {
   1446        return NS_ERROR_UNEXPECTED;
   1447      }
   1448      // Shift + Enter should insert a <br> or a LF instead of splitting current
   1449      // paragraph.  Additionally, if we're in plaintext-only mode, we should
   1450      // do so because Chrome does so, but execCommand("insertParagraph") keeps
   1451      // working as contenteditable=true.  So, we cannot redirect in
   1452      // InsertParagraphSeparatorAsAction().
   1453      if (aKeyboardEvent->IsShift() ||
   1454          editingHost->IsContentEditablePlainTextOnly()) {
   1455        // Only inserts a <br> element.
   1456        nsresult rv = InsertLineBreakAsAction();
   1457        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1458                             "HTMLEditor::InsertLineBreakAsAction() failed");
   1459        return EditorBase::ToGenericNSResult(rv);
   1460      }
   1461      // uses rules to figure out what to insert
   1462      nsresult rv = InsertParagraphSeparatorAsAction();
   1463      NS_WARNING_ASSERTION(
   1464          NS_SUCCEEDED(rv),
   1465          "HTMLEditor::InsertParagraphSeparatorAsAction() failed");
   1466      return EditorBase::ToGenericNSResult(rv);
   1467    }
   1468  }
   1469 
   1470  if (!aKeyboardEvent->IsInputtingText()) {
   1471    // we don't PreventDefault() here or keybindings like control-x won't work
   1472    return NS_OK;
   1473  }
   1474  aKeyboardEvent->PreventDefault();
   1475  // If we dispatch 2 keypress events for a surrogate pair and we set only
   1476  // first `.key` value to the surrogate pair, the preceding one has it and the
   1477  // other has empty string.  In this case, we should handle only the first one
   1478  // with the key value.
   1479  if (!StaticPrefs::dom_event_keypress_dispatch_once_per_surrogate_pair() &&
   1480      !StaticPrefs::dom_event_keypress_key_allow_lone_surrogate() &&
   1481      aKeyboardEvent->mKeyValue.IsEmpty() &&
   1482      IS_SURROGATE(aKeyboardEvent->mCharCode)) {
   1483    return NS_OK;
   1484  }
   1485  nsAutoString str(aKeyboardEvent->mKeyValue);
   1486  if (str.IsEmpty()) {
   1487    str.Assign(static_cast<char16_t>(aKeyboardEvent->mCharCode));
   1488  }
   1489  // FYI: DIfferent from TextEditor, we can treat \r (CR) as-is in HTMLEditor.
   1490  nsresult rv = OnInputText(str);
   1491  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::OnInputText() failed");
   1492  return rv;
   1493 }
   1494 
   1495 NS_IMETHODIMP HTMLEditor::NodeIsBlock(nsINode* aNode, bool* aIsBlock) {
   1496  if (NS_WARN_IF(!aNode)) {
   1497    return NS_ERROR_INVALID_ARG;
   1498  }
   1499  if (MOZ_UNLIKELY(!aNode->IsElement())) {
   1500    *aIsBlock = false;
   1501    return NS_OK;
   1502  }
   1503  // If the node is in composed doc, we'll refer its style.  If we don't flush
   1504  // pending style here, another API call may change the style.  Therefore,
   1505  // let's flush the pending style changes right now.
   1506  if (aNode->IsInComposedDoc()) {
   1507    if (RefPtr<PresShell> presShell = GetPresShell()) {
   1508      presShell->FlushPendingNotifications(FlushType::Style);
   1509    }
   1510  }
   1511  *aIsBlock = HTMLEditUtils::IsBlockElement(
   1512      *aNode->AsElement(), BlockInlineCheck::UseComputedDisplayOutsideStyle);
   1513  return NS_OK;
   1514 }
   1515 
   1516 NS_IMETHODIMP HTMLEditor::UpdateBaseURL() {
   1517  RefPtr<Document> document = GetDocument();
   1518  if (NS_WARN_IF(!document)) {
   1519    return NS_ERROR_FAILURE;
   1520  }
   1521 
   1522  // Look for an HTML <base> tag
   1523  RefPtr<nsContentList> baseElementList =
   1524      document->GetElementsByTagName(u"base"_ns);
   1525 
   1526  // If no base tag, then set baseURL to the document's URL.  This is very
   1527  // important, else relative URLs for links and images are wrong
   1528  if (!baseElementList || !baseElementList->Item(0)) {
   1529    document->SetBaseURI(document->GetDocumentURI());
   1530  }
   1531  return NS_OK;
   1532 }
   1533 
   1534 NS_IMETHODIMP HTMLEditor::InsertLineBreak() {
   1535  // XPCOM method's InsertLineBreak() should insert paragraph separator in
   1536  // HTMLEditor.
   1537  AutoEditActionDataSetter editActionData(
   1538      *this, EditAction::eInsertParagraphSeparator);
   1539  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1540  if (NS_FAILED(rv)) {
   1541    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1542                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   1543    return EditorBase::ToGenericNSResult(rv);
   1544  }
   1545 
   1546  const RefPtr<Element> editingHost =
   1547      ComputeEditingHost(LimitInBodyElement::No);
   1548  if (!editingHost) {
   1549    return NS_SUCCESS_DOM_NO_OPERATION;
   1550  }
   1551 
   1552  Result<EditActionResult, nsresult> result =
   1553      InsertParagraphSeparatorAsSubAction(*editingHost);
   1554  if (MOZ_UNLIKELY(result.isErr())) {
   1555    NS_WARNING("HTMLEditor::InsertParagraphSeparatorAsSubAction() failed");
   1556    return EditorBase::ToGenericNSResult(result.unwrapErr());
   1557  }
   1558  return NS_OK;
   1559 }
   1560 
   1561 nsresult HTMLEditor::InsertLineBreakAsAction(nsIPrincipal* aPrincipal) {
   1562  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak,
   1563                                          aPrincipal);
   1564  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1565  if (NS_FAILED(rv)) {
   1566    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1567                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   1568    return EditorBase::ToGenericNSResult(rv);
   1569  }
   1570 
   1571  if (IsSelectionRangeContainerNotContent()) {
   1572    return NS_SUCCESS_DOM_NO_OPERATION;
   1573  }
   1574 
   1575  rv = InsertLineBreakAsSubAction();
   1576  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1577                       "HTMLEditor::InsertLineBreakAsSubAction() failed");
   1578  // Don't return NS_SUCCESS_DOM_NO_OPERATION for compatibility of `execCommand`
   1579  // result of Chrome.
   1580  return NS_FAILED(rv) ? rv : NS_OK;
   1581 }
   1582 
   1583 nsresult HTMLEditor::InsertParagraphSeparatorAsAction(
   1584    nsIPrincipal* aPrincipal) {
   1585  AutoEditActionDataSetter editActionData(
   1586      *this, EditAction::eInsertParagraphSeparator, aPrincipal);
   1587  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1588  if (NS_FAILED(rv)) {
   1589    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1590                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   1591    return EditorBase::ToGenericNSResult(rv);
   1592  }
   1593 
   1594  const RefPtr<Element> editingHost =
   1595      ComputeEditingHost(LimitInBodyElement::No);
   1596  if (!editingHost) {
   1597    return NS_SUCCESS_DOM_NO_OPERATION;
   1598  }
   1599 
   1600  Result<EditActionResult, nsresult> result =
   1601      InsertParagraphSeparatorAsSubAction(*editingHost);
   1602  if (MOZ_UNLIKELY(result.isErr())) {
   1603    NS_WARNING("HTMLEditor::InsertParagraphSeparatorAsSubAction() failed");
   1604    return EditorBase::ToGenericNSResult(result.unwrapErr());
   1605  }
   1606  return NS_OK;
   1607 }
   1608 
   1609 Result<EditActionResult, nsresult> HTMLEditor::HandleTabKeyPressInTable(
   1610    WidgetKeyboardEvent* aKeyboardEvent) {
   1611  MOZ_ASSERT(aKeyboardEvent);
   1612 
   1613  AutoEditActionDataSetter dummyEditActionData(*this, EditAction::eNotEditing);
   1614  if (NS_WARN_IF(!dummyEditActionData.CanHandle())) {
   1615    // Do nothing if we didn't find a table cell.
   1616    return EditActionResult::IgnoredResult();
   1617  }
   1618 
   1619  // Find enclosing table cell from selection (cell may be selected element)
   1620  const RefPtr<Element> cellElement =
   1621      GetInclusiveAncestorByTagNameAtSelection(*nsGkAtoms::td);
   1622  if (!cellElement) {
   1623    NS_WARNING(
   1624        "HTMLEditor::GetInclusiveAncestorByTagNameAtSelection(*nsGkAtoms::td) "
   1625        "returned nullptr");
   1626    // Do nothing if we didn't find a table cell.
   1627    return EditActionResult::IgnoredResult();
   1628  }
   1629 
   1630  // find enclosing table
   1631  RefPtr<Element> table =
   1632      HTMLEditUtils::GetClosestAncestorTableElement(*cellElement);
   1633  if (!table) {
   1634    NS_WARNING("HTMLEditor::GetClosestAncestorTableElement() failed");
   1635    return EditActionResult::IgnoredResult();
   1636  }
   1637 
   1638  // advance to next cell
   1639  // first create an iterator over the table
   1640  PostContentIterator postOrderIter;
   1641  nsresult rv = postOrderIter.Init(table);
   1642  if (NS_FAILED(rv)) {
   1643    NS_WARNING("PostContentIterator::Init() failed");
   1644    return Err(rv);
   1645  }
   1646  // position postOrderIter at block
   1647  rv = postOrderIter.PositionAt(cellElement);
   1648  if (NS_FAILED(rv)) {
   1649    NS_WARNING("PostContentIterator::PositionAt() failed");
   1650    return Err(rv);
   1651  }
   1652 
   1653  do {
   1654    if (aKeyboardEvent->IsShift()) {
   1655      postOrderIter.Prev();
   1656    } else {
   1657      postOrderIter.Next();
   1658    }
   1659 
   1660    const RefPtr<Element> element =
   1661        Element::FromNodeOrNull(postOrderIter.GetCurrentNode());
   1662    if (element && HTMLEditUtils::IsTableCellElement(*element) &&
   1663        HTMLEditUtils::GetClosestAncestorTableElement(*element) == table) {
   1664      aKeyboardEvent->PreventDefault();
   1665      CollapseSelectionToDeepestNonTableFirstChild(element);
   1666      if (NS_WARN_IF(Destroyed())) {
   1667        return Err(NS_ERROR_EDITOR_DESTROYED);
   1668      }
   1669      return EditActionResult::HandledResult();
   1670    }
   1671  } while (!postOrderIter.IsDone());
   1672 
   1673  if (aKeyboardEvent->IsShift()) {
   1674    return EditActionResult::IgnoredResult();
   1675  }
   1676 
   1677  // If we haven't handled it yet, then we must have run off the end of the
   1678  // table.  Insert a new row.
   1679  // XXX We should investigate whether this behavior is supported by other
   1680  //     browsers later.
   1681  AutoEditActionDataSetter editActionData(*this,
   1682                                          EditAction::eInsertTableRowElement);
   1683  rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1684  if (NS_FAILED(rv)) {
   1685    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1686                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   1687    return Err(rv);
   1688  }
   1689  rv = InsertTableRowsWithTransaction(*cellElement, 1,
   1690                                      InsertPosition::eAfterSelectedCell);
   1691  if (NS_WARN_IF(Destroyed())) {
   1692    return Err(NS_ERROR_EDITOR_DESTROYED);
   1693  }
   1694  if (NS_FAILED(rv)) {
   1695    NS_WARNING(
   1696        "HTMLEditor::InsertTableRowsWithTransaction(*cellElement, 1, "
   1697        "InsertPosition::eAfterSelectedCell) failed");
   1698    return Err(rv);
   1699  }
   1700  aKeyboardEvent->PreventDefault();
   1701  // Put selection in right place.  Use table code to get selection and index
   1702  // to new row...
   1703  RefPtr<Element> tblElement, cell;
   1704  int32_t row;
   1705  rv = GetCellContext(getter_AddRefs(tblElement), getter_AddRefs(cell), nullptr,
   1706                      nullptr, &row, nullptr);
   1707  if (NS_FAILED(rv)) {
   1708    NS_WARNING("HTMLEditor::GetCellContext() failed");
   1709    return Err(rv);
   1710  }
   1711  if (!tblElement) {
   1712    NS_WARNING("HTMLEditor::GetCellContext() didn't return table element");
   1713    return Err(NS_ERROR_FAILURE);
   1714  }
   1715  // ...so that we can ask for first cell in that row...
   1716  cell = GetTableCellElementAt(*tblElement, row, 0);
   1717  // ...and then set selection there.  (Note that normally you should use
   1718  // CollapseSelectionToDeepestNonTableFirstChild(), but we know cell is an
   1719  // empty new cell, so this works fine)
   1720  if (cell) {
   1721    nsresult rv = CollapseSelectionToStartOf(*cell);
   1722    if (NS_FAILED(rv)) {
   1723      NS_WARNING("EditorBase::CollapseSelectionToStartOf() failed");
   1724      return Err(NS_ERROR_EDITOR_DESTROYED);
   1725    }
   1726  }
   1727  if (NS_WARN_IF(Destroyed())) {
   1728    return Err(NS_ERROR_EDITOR_DESTROYED);
   1729  }
   1730  return EditActionResult::HandledResult();
   1731 }
   1732 
   1733 void HTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsINode* aNode) {
   1734  MOZ_ASSERT(IsEditActionDataAvailable());
   1735 
   1736  MOZ_ASSERT(aNode);
   1737 
   1738  nsCOMPtr<nsINode> node = aNode;
   1739 
   1740  for (nsIContent* child = node->GetFirstChild(); child;
   1741       child = child->GetFirstChild()) {
   1742    // Stop if we find a table, don't want to go into nested tables
   1743    if (child->IsHTMLElement(nsGkAtoms::table) ||
   1744        !HTMLEditUtils::IsContainerNode(*child)) {
   1745      break;
   1746    }
   1747    node = child;
   1748  }
   1749 
   1750  DebugOnly<nsresult> rvIgnored = CollapseSelectionToStartOf(*node);
   1751  NS_WARNING_ASSERTION(
   1752      NS_SUCCEEDED(rvIgnored),
   1753      "EditorBase::CollapseSelectionToStartOf() failed, but ignored");
   1754 }
   1755 
   1756 NS_IMETHODIMP HTMLEditor::InsertElementAtSelection(Element* aElement,
   1757                                                   bool aDeleteSelection) {
   1758  InsertElementOptions options;
   1759  if (aDeleteSelection) {
   1760    options += InsertElementOption::DeleteSelection;
   1761  }
   1762  nsresult rv = InsertElementAtSelectionAsAction(aElement, options);
   1763  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1764                       "HTMLEditor::InsertElementAtSelectionAsAction() failed");
   1765  return rv;
   1766 }
   1767 
   1768 nsresult HTMLEditor::InsertElementAtSelectionAsAction(
   1769    Element* aElement, const InsertElementOptions aOptions,
   1770    nsIPrincipal* aPrincipal) {
   1771  if (NS_WARN_IF(!aElement)) {
   1772    return NS_ERROR_INVALID_ARG;
   1773  }
   1774 
   1775  if (IsReadonly()) {
   1776    return NS_OK;
   1777  }
   1778 
   1779  AutoEditActionDataSetter editActionData(
   1780      *this, HTMLEditUtils::GetEditActionForInsert(*aElement), aPrincipal);
   1781  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   1782  if (NS_FAILED(rv)) {
   1783    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   1784                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   1785    return EditorBase::ToGenericNSResult(rv);
   1786  }
   1787 
   1788  DebugOnly<nsresult> rvIgnored = CommitComposition();
   1789  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1790                       "EditorBase::CommitComposition() failed, but ignored");
   1791 
   1792  {
   1793    Result<EditActionResult, nsresult> result = CanHandleHTMLEditSubAction();
   1794    if (MOZ_UNLIKELY(result.isErr())) {
   1795      NS_WARNING("HTMLEditor::CanHandleHTMLEditSubAction() failed");
   1796      return EditorBase::ToGenericNSResult(result.unwrapErr());
   1797    }
   1798    if (result.inspect().Canceled()) {
   1799      return NS_OK;
   1800    }
   1801  }
   1802 
   1803  UndefineCaretBidiLevel();
   1804 
   1805  AutoPlaceholderBatch treatAsOneTransaction(
   1806      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   1807  IgnoredErrorResult ignoredError;
   1808  AutoEditSubActionNotifier startToHandleEditSubAction(
   1809      *this, EditSubAction::eInsertElement, nsIEditor::eNext, ignoredError);
   1810  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   1811    return ignoredError.StealNSResult();
   1812  }
   1813  NS_WARNING_ASSERTION(
   1814      !ignoredError.Failed(),
   1815      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   1816 
   1817  const RefPtr<Element> editingHost =
   1818      ComputeEditingHost(LimitInBodyElement::No);
   1819  if (NS_WARN_IF(!editingHost)) {
   1820    return EditorBase::ToGenericNSResult(NS_ERROR_FAILURE);
   1821  }
   1822 
   1823  rv = EnsureNoPaddingBRElementForEmptyEditor();
   1824  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   1825    return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   1826  }
   1827  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1828                       "EditorBase::EnsureNoPaddingBRElementForEmptyEditor() "
   1829                       "failed, but ignored");
   1830 
   1831  if (NS_SUCCEEDED(rv) && SelectionRef().IsCollapsed()) {
   1832    nsresult rv = EnsureCaretNotAfterInvisibleBRElement(*editingHost);
   1833    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   1834      return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   1835    }
   1836    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1837                         "HTMLEditor::EnsureCaretNotAfterInvisibleBRElement() "
   1838                         "failed, but ignored");
   1839    if (NS_SUCCEEDED(rv)) {
   1840      nsresult rv = PrepareInlineStylesForCaret();
   1841      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   1842        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   1843      }
   1844      NS_WARNING_ASSERTION(
   1845          NS_SUCCEEDED(rv),
   1846          "HTMLEditor::PrepareInlineStylesForCaret() failed, but ignored");
   1847    }
   1848  }
   1849 
   1850  if (aOptions.contains(InsertElementOption::DeleteSelection) &&
   1851      !SelectionRef().IsCollapsed()) {
   1852    if (!HTMLEditUtils::IsBlockElement(
   1853            *aElement, BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
   1854      // E.g., inserting an image.  In this case we don't need to delete any
   1855      // inline wrappers before we do the insertion.  Otherwise we let
   1856      // DeleteSelectionAndPrepareToCreateNode do the deletion for us, which
   1857      // calls DeleteSelection with aStripWrappers = eStrip.
   1858      nsresult rv = DeleteSelectionAsSubAction(
   1859          eNone,
   1860          aOptions.contains(InsertElementOption::SplitAncestorInlineElements)
   1861              ? eStrip
   1862              : eNoStrip);
   1863      if (NS_FAILED(rv)) {
   1864        NS_WARNING(
   1865            "EditorBase::DeleteSelectionAsSubAction(eNone, eNoStrip) failed");
   1866        return EditorBase::ToGenericNSResult(rv);
   1867      }
   1868    }
   1869 
   1870    nsresult rv = DeleteSelectionAndPrepareToCreateNode();
   1871    if (NS_FAILED(rv)) {
   1872      NS_WARNING("HTMLEditor::DeleteSelectionAndPrepareToCreateNode() failed");
   1873      return rv;
   1874    }
   1875  }
   1876  // If deleting, selection will be collapsed.
   1877  // so if not, we collapse it
   1878  else {
   1879    // Named Anchor is a special case,
   1880    // We collapse to insert element BEFORE the selection
   1881    // For all other tags, we insert AFTER the selection
   1882    if (HTMLEditUtils::IsNamedAnchorElement(*aElement)) {
   1883      IgnoredErrorResult ignoredError;
   1884      SelectionRef().CollapseToStart(ignoredError);
   1885      if (NS_WARN_IF(Destroyed())) {
   1886        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   1887      }
   1888      NS_WARNING_ASSERTION(!ignoredError.Failed(),
   1889                           "Selection::CollapseToStart() failed, but ignored");
   1890    } else {
   1891      IgnoredErrorResult ignoredError;
   1892      SelectionRef().CollapseToEnd(ignoredError);
   1893      if (NS_WARN_IF(Destroyed())) {
   1894        return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   1895      }
   1896      NS_WARNING_ASSERTION(!ignoredError.Failed(),
   1897                           "Selection::CollapseToEnd() failed, but ignored");
   1898    }
   1899  }
   1900 
   1901  if (!SelectionRef().GetAnchorNode()) {
   1902    return NS_OK;
   1903  }
   1904  if (NS_WARN_IF(!SelectionRef().GetAnchorNode()->IsInclusiveDescendantOf(
   1905          editingHost))) {
   1906    return NS_ERROR_FAILURE;
   1907  }
   1908 
   1909  EditorRawDOMPoint atAnchor(SelectionRef().AnchorRef());
   1910  // Adjust position based on the node we are going to insert.
   1911  EditorDOMPoint pointToInsert =
   1912      HTMLEditUtils::GetBetterInsertionPointFor<EditorDOMPoint>(*aElement,
   1913                                                                atAnchor);
   1914  if (!pointToInsert.IsSet()) {
   1915    NS_WARNING("HTMLEditUtils::GetBetterInsertionPointFor() failed");
   1916    return NS_ERROR_FAILURE;
   1917  }
   1918  Result<EditorDOMPoint, nsresult> pointToInsertOrError =
   1919      WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
   1920          *this, pointToInsert,
   1921          {WhiteSpaceVisibilityKeeper::NormalizeOption::
   1922               StopIfFollowingWhiteSpacesStartsWithNBSP});
   1923  if (MOZ_UNLIKELY(pointToInsertOrError.isErr())) {
   1924    NS_WARNING(
   1925        "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() failed");
   1926    return pointToInsertOrError.propagateErr();
   1927  }
   1928  pointToInsert = pointToInsertOrError.unwrap();
   1929  if (NS_WARN_IF(!pointToInsert.IsSetAndValidInComposedDoc())) {
   1930    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1931  }
   1932 
   1933  if (aOptions.contains(InsertElementOption::SplitAncestorInlineElements)) {
   1934    if (const RefPtr<Element> topmostInlineElement = Element::FromNodeOrNull(
   1935            HTMLEditUtils::GetMostDistantAncestorInlineElement(
   1936                *pointToInsert.ContainerAs<nsIContent>(),
   1937                BlockInlineCheck::UseComputedDisplayOutsideStyle,
   1938                editingHost))) {
   1939      Result<SplitNodeResult, nsresult> splitInlinesResult =
   1940          SplitNodeDeepWithTransaction(
   1941              *topmostInlineElement, pointToInsert,
   1942              SplitAtEdges::eDoNotCreateEmptyContainer);
   1943      if (MOZ_UNLIKELY(splitInlinesResult.isErr())) {
   1944        NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed");
   1945        return splitInlinesResult.unwrapErr();
   1946      }
   1947      splitInlinesResult.inspect().IgnoreCaretPointSuggestion();
   1948      auto splitPoint =
   1949          splitInlinesResult.inspect().AtSplitPoint<EditorDOMPoint>();
   1950      if (MOZ_LIKELY(splitPoint.IsSet())) {
   1951        pointToInsert = std::move(splitPoint);
   1952      }
   1953    }
   1954  }
   1955  {
   1956    Result<CreateElementResult, nsresult> insertElementResult =
   1957        InsertNodeIntoProperAncestorWithTransaction<Element>(
   1958            *aElement, pointToInsert,
   1959            SplitAtEdges::eAllowToCreateEmptyContainer);
   1960    if (MOZ_UNLIKELY(insertElementResult.isErr())) {
   1961      NS_WARNING(
   1962          "HTMLEditor::InsertNodeIntoProperAncestorWithTransaction("
   1963          "SplitAtEdges::eAllowToCreateEmptyContainer) failed");
   1964      return EditorBase::ToGenericNSResult(insertElementResult.unwrapErr());
   1965    }
   1966    if (MOZ_LIKELY(aElement->IsInComposedDoc())) {
   1967      const auto afterElement = EditorDOMPoint::After(*aElement);
   1968      if (MOZ_LIKELY(afterElement.IsInContentNode())) {
   1969        nsresult rv = EnsureNoFollowingUnnecessaryLineBreak(afterElement);
   1970        if (NS_FAILED(rv)) {
   1971          NS_WARNING(
   1972              "HTMLEditor::EnsureNoFollowingUnnecessaryLineBreak() failed");
   1973          return EditorBase::ToGenericNSResult(rv);
   1974        }
   1975      }
   1976    }
   1977    insertElementResult.inspect().IgnoreCaretPointSuggestion();
   1978  }
   1979  // Set caret after element, but check for special case
   1980  //  of inserting table-related elements: set in first cell instead
   1981  if (!SetCaretInTableCell(aElement)) {
   1982    if (NS_WARN_IF(Destroyed())) {
   1983      return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
   1984    }
   1985    nsresult rv = CollapseSelectionTo(EditorRawDOMPoint::After(*aElement));
   1986    if (NS_FAILED(rv)) {
   1987      NS_WARNING("HTMLEditor::CollapseSelectionTo() failed");
   1988      return EditorBase::ToGenericNSResult(rv);
   1989    }
   1990  }
   1991 
   1992  // check for inserting a whole table at the end of a block. If so insert
   1993  // a br after it.
   1994  if (!aElement->IsHTMLElement(nsGkAtoms::table) ||
   1995      !HTMLEditUtils::IsLastChild(*aElement,
   1996                                  {WalkTreeOption::IgnoreNonEditableNode})) {
   1997    return NS_OK;
   1998  }
   1999 
   2000  Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
   2001      InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement,
   2002                      EditorDOMPoint::After(*aElement),
   2003                      // Will collapse selection to before the new line break.
   2004                      ePrevious);
   2005  if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   2006    NS_WARNING(
   2007        "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   2008        "LineBreakType::BRElement, ePrevious) failed");
   2009    return EditorBase::ToGenericNSResult(
   2010        insertBRElementResultOrError.unwrapErr());
   2011  }
   2012  CreateLineBreakResult insertBRElementResult =
   2013      insertBRElementResultOrError.unwrap();
   2014  MOZ_ASSERT(insertBRElementResult.Handled());
   2015  rv = insertBRElementResult.SuggestCaretPointTo(*this, {});
   2016  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2017                       "CaretPoint::SuggestCaretPointTo() failed");
   2018  return EditorBase::ToGenericNSResult(rv);
   2019 }
   2020 
   2021 template <typename NodeType>
   2022 Result<CreateNodeResultBase<NodeType>, nsresult>
   2023 HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
   2024    NodeType& aContentToInsert, const EditorDOMPoint& aPointToInsert,
   2025    SplitAtEdges aSplitAtEdges) {
   2026  MOZ_ASSERT(aPointToInsert.IsSetAndValidInComposedDoc());
   2027  if (NS_WARN_IF(!aPointToInsert.IsInContentNode())) {
   2028    return Err(NS_ERROR_FAILURE);
   2029  }
   2030  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   2031 
   2032  if (aContentToInsert.NodeType() == nsINode::DOCUMENT_TYPE_NODE ||
   2033      aContentToInsert.NodeType() == nsINode::PROCESSING_INSTRUCTION_NODE) {
   2034    return CreateNodeResultBase<NodeType>::NotHandled();
   2035  }
   2036 
   2037  // Search up the parent chain to find a suitable container.
   2038  EditorDOMPoint pointToInsert(aPointToInsert);
   2039  MOZ_ASSERT(pointToInsert.IsInContentNode());
   2040  while (!HTMLEditUtils::CanNodeContain(*pointToInsert.GetContainer(),
   2041                                        aContentToInsert)) {
   2042    // If the current parent is a root (body or table element)
   2043    // then go no further - we can't insert.
   2044    if (MOZ_UNLIKELY(pointToInsert.IsContainerHTMLElement(nsGkAtoms::body) ||
   2045                     HTMLEditUtils::IsAnyTableElementExceptColumnElement(
   2046                         *pointToInsert.ContainerAs<nsIContent>()))) {
   2047      NS_WARNING(
   2048          "There was no proper container element to insert the content node in "
   2049          "the document");
   2050      return Err(NS_ERROR_FAILURE);
   2051    }
   2052 
   2053    // Get the next point.
   2054    pointToInsert = pointToInsert.ParentPoint();
   2055 
   2056    if (MOZ_UNLIKELY(
   2057            !pointToInsert.IsInContentNode() ||
   2058            !EditorUtils::IsEditableContent(
   2059                *pointToInsert.ContainerAs<nsIContent>(), EditorType::HTML))) {
   2060      NS_WARNING(
   2061          "There was no proper container element to insert the content node in "
   2062          "the editing host");
   2063      return Err(NS_ERROR_FAILURE);
   2064    }
   2065  }
   2066 
   2067  if (pointToInsert != aPointToInsert) {
   2068    // We need to split some levels above the original selection parent.
   2069    MOZ_ASSERT(pointToInsert.GetChild());
   2070    Result<SplitNodeResult, nsresult> splitNodeResult =
   2071        SplitNodeDeepWithTransaction(MOZ_KnownLive(*pointToInsert.GetChild()),
   2072                                     aPointToInsert, aSplitAtEdges);
   2073    if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
   2074      NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed");
   2075      return splitNodeResult.propagateErr();
   2076    }
   2077    pointToInsert =
   2078        splitNodeResult.inspect().template AtSplitPoint<EditorDOMPoint>();
   2079    MOZ_ASSERT(pointToInsert.IsSetAndValidInComposedDoc());
   2080    // Caret should be set by the caller of this method so that we don't
   2081    // need to handle it here.
   2082    splitNodeResult.inspect().IgnoreCaretPointSuggestion();
   2083  }
   2084 
   2085  // Now we can insert the new node.
   2086  Result<CreateNodeResultBase<NodeType>, nsresult> insertContentNodeResult =
   2087      InsertNodeWithTransaction<NodeType>(aContentToInsert, pointToInsert);
   2088  if (MOZ_LIKELY(insertContentNodeResult.isOk()) &&
   2089      MOZ_UNLIKELY(NS_WARN_IF(!aContentToInsert.GetParentNode()) ||
   2090                   NS_WARN_IF(aContentToInsert.GetParentNode() !=
   2091                              pointToInsert.GetContainer()))) {
   2092    NS_WARNING(
   2093        "EditorBase::InsertNodeWithTransaction() succeeded, but the inserted "
   2094        "node was moved or removed by the web app");
   2095    insertContentNodeResult.inspect().IgnoreCaretPointSuggestion();
   2096    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   2097  }
   2098  NS_WARNING_ASSERTION(insertContentNodeResult.isOk(),
   2099                       "EditorBase::InsertNodeWithTransaction() failed");
   2100  return insertContentNodeResult;
   2101 }
   2102 
   2103 NS_IMETHODIMP HTMLEditor::SelectElement(Element* aElement) {
   2104  if (NS_WARN_IF(!aElement)) {
   2105    return NS_ERROR_INVALID_ARG;
   2106  }
   2107 
   2108  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   2109  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2110    return NS_ERROR_NOT_INITIALIZED;
   2111  }
   2112 
   2113  nsresult rv = SelectContentInternal(MOZ_KnownLive(*aElement));
   2114  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2115                       "HTMLEditor::SelectContentInternal() failed");
   2116  return rv;
   2117 }
   2118 
   2119 nsresult HTMLEditor::SelectContentInternal(nsIContent& aContentToSelect) {
   2120  MOZ_ASSERT(IsEditActionDataAvailable());
   2121 
   2122  // Must be sure that element is contained in the editing host
   2123  const RefPtr<Element> editingHost = ComputeEditingHost();
   2124  if (NS_WARN_IF(!editingHost) ||
   2125      NS_WARN_IF(!aContentToSelect.IsInclusiveDescendantOf(editingHost))) {
   2126    return NS_ERROR_FAILURE;
   2127  }
   2128 
   2129  EditorRawDOMPoint newSelectionStart(&aContentToSelect);
   2130  if (NS_WARN_IF(!newSelectionStart.IsSet())) {
   2131    return NS_ERROR_FAILURE;
   2132  }
   2133  EditorRawDOMPoint newSelectionEnd(EditorRawDOMPoint::After(aContentToSelect));
   2134  MOZ_ASSERT(newSelectionEnd.IsSet());
   2135  ErrorResult error;
   2136  SelectionRef().SetStartAndEndInLimiter(newSelectionStart, newSelectionEnd,
   2137                                         error);
   2138  NS_WARNING_ASSERTION(!error.Failed(),
   2139                       "Selection::SetStartAndEndInLimiter() failed");
   2140  return error.StealNSResult();
   2141 }
   2142 
   2143 nsresult HTMLEditor::AppendContentToSelectionAsRange(nsIContent& aContent) {
   2144  MOZ_ASSERT(IsEditActionDataAvailable());
   2145 
   2146  EditorRawDOMPoint atContent(&aContent);
   2147  if (NS_WARN_IF(!atContent.IsSet())) {
   2148    return NS_ERROR_FAILURE;
   2149  }
   2150 
   2151  RefPtr<nsRange> range = nsRange::Create(
   2152      atContent.ToRawRangeBoundary(),
   2153      atContent.NextPoint().ToRawRangeBoundary(), IgnoreErrors());
   2154  if (NS_WARN_IF(!range)) {
   2155    NS_WARNING("nsRange::Create() failed");
   2156    return NS_ERROR_FAILURE;
   2157  }
   2158 
   2159  ErrorResult error;
   2160  SelectionRef().AddRangeAndSelectFramesAndNotifyListeners(*range, error);
   2161  if (NS_WARN_IF(Destroyed())) {
   2162    if (error.Failed()) {
   2163      error.SuppressException();
   2164    }
   2165    return NS_ERROR_EDITOR_DESTROYED;
   2166  }
   2167  NS_WARNING_ASSERTION(!error.Failed(), "Failed to add range to Selection");
   2168  return error.StealNSResult();
   2169 }
   2170 
   2171 nsresult HTMLEditor::ClearSelection() {
   2172  MOZ_ASSERT(IsEditActionDataAvailable());
   2173 
   2174  ErrorResult error;
   2175  SelectionRef().RemoveAllRanges(error);
   2176  if (NS_WARN_IF(Destroyed())) {
   2177    if (error.Failed()) {
   2178      error.SuppressException();
   2179    }
   2180    return NS_ERROR_EDITOR_DESTROYED;
   2181  }
   2182  NS_WARNING_ASSERTION(!error.Failed(), "Selection::RemoveAllRanges() failed");
   2183  return error.StealNSResult();
   2184 }
   2185 
   2186 nsresult HTMLEditor::FormatBlockAsAction(const nsAString& aParagraphFormat,
   2187                                         nsIPrincipal* aPrincipal) {
   2188  if (NS_WARN_IF(aParagraphFormat.IsEmpty())) {
   2189    return NS_ERROR_INVALID_ARG;
   2190  }
   2191 
   2192  AutoEditActionDataSetter editActionData(
   2193      *this, EditAction::eInsertBlockElement, aPrincipal);
   2194  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2195    return NS_ERROR_NOT_INITIALIZED;
   2196  }
   2197 
   2198  const RefPtr<Element> editingHost =
   2199      ComputeEditingHost(LimitInBodyElement::No);
   2200  if (!editingHost || editingHost->IsContentEditablePlainTextOnly()) {
   2201    return NS_SUCCESS_DOM_NO_OPERATION;
   2202  }
   2203 
   2204  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   2205  if (NS_FAILED(rv)) {
   2206    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2207                         "MaybeDispatchBeforeInputEvent(), failed");
   2208    return EditorBase::ToGenericNSResult(rv);
   2209  }
   2210 
   2211  RefPtr<nsAtom> tagName = NS_Atomize(aParagraphFormat);
   2212  MOZ_ASSERT(tagName);
   2213  if (NS_WARN_IF(!tagName->IsStatic()) ||
   2214      NS_WARN_IF(!HTMLEditUtils::IsFormatTagForFormatBlockCommand(
   2215          *tagName->AsStatic()))) {
   2216    return NS_ERROR_INVALID_ARG;
   2217  }
   2218 
   2219  if (tagName == nsGkAtoms::dd || tagName == nsGkAtoms::dt) {
   2220    // MOZ_KnownLive(tagName->AsStatic()) because nsStaticAtom instances live
   2221    // while the process is running.
   2222    Result<EditActionResult, nsresult> result =
   2223        MakeOrChangeListAndListItemAsSubAction(
   2224            MOZ_KnownLive(*tagName->AsStatic()), EmptyString(),
   2225            SelectAllOfCurrentList::No, *editingHost);
   2226    if (MOZ_UNLIKELY(result.isErr())) {
   2227      NS_WARNING(
   2228          "HTMLEditor::MakeOrChangeListAndListItemAsSubAction("
   2229          "SelectAllOfCurrentList::No) failed");
   2230      return EditorBase::ToGenericNSResult(result.unwrapErr());
   2231    }
   2232    return NS_OK;
   2233  }
   2234 
   2235  rv = FormatBlockContainerAsSubAction(MOZ_KnownLive(*tagName->AsStatic()),
   2236                                       FormatBlockMode::HTMLFormatBlockCommand,
   2237                                       *editingHost);
   2238  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2239                       "HTMLEditor::FormatBlockContainerAsSubAction() failed");
   2240  return EditorBase::ToGenericNSResult(rv);
   2241 }
   2242 
   2243 nsresult HTMLEditor::SetParagraphStateAsAction(
   2244    const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal) {
   2245  AutoEditActionDataSetter editActionData(
   2246      *this, EditAction::eInsertBlockElement, aPrincipal);
   2247  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2248    return NS_ERROR_NOT_INITIALIZED;
   2249  }
   2250 
   2251  const RefPtr<Element> editingHost =
   2252      ComputeEditingHost(LimitInBodyElement::No);
   2253  if (!editingHost || editingHost->IsContentEditablePlainTextOnly()) {
   2254    return NS_SUCCESS_DOM_NO_OPERATION;
   2255  }
   2256 
   2257  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   2258  if (NS_FAILED(rv)) {
   2259    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2260                         "MaybeDispatchBeforeInputEvent(), failed");
   2261    return EditorBase::ToGenericNSResult(rv);
   2262  }
   2263 
   2264  // TODO: Computing the editing host here makes the `execCommand` in
   2265  //       docshell/base/crashtests/file_432114-2.xhtml cannot run
   2266  //       `DOMNodeRemoved` event listener with deleting the bogus <br> element.
   2267  //       So that it should be rewritten with different mutation event listener
   2268  //       since we'd like to stop using it.
   2269 
   2270  nsAutoString lowerCaseTagName(aParagraphFormat);
   2271  ToLowerCase(lowerCaseTagName);
   2272  RefPtr<nsAtom> tagName = NS_Atomize(lowerCaseTagName);
   2273  MOZ_ASSERT(tagName);
   2274  if (NS_WARN_IF(!tagName->IsStatic())) {
   2275    return NS_ERROR_INVALID_ARG;
   2276  }
   2277  if (tagName == nsGkAtoms::dd || tagName == nsGkAtoms::dt) {
   2278    // MOZ_KnownLive(tagName->AsStatic()) because nsStaticAtom instances live
   2279    // while the process is running.
   2280    Result<EditActionResult, nsresult> result =
   2281        MakeOrChangeListAndListItemAsSubAction(
   2282            MOZ_KnownLive(*tagName->AsStatic()), EmptyString(),
   2283            SelectAllOfCurrentList::No, *editingHost);
   2284    if (MOZ_UNLIKELY(result.isErr())) {
   2285      NS_WARNING(
   2286          "HTMLEditor::MakeOrChangeListAndListItemAsSubAction("
   2287          "SelectAllOfCurrentList::No) failed");
   2288      return EditorBase::ToGenericNSResult(result.unwrapErr());
   2289    }
   2290    return NS_OK;
   2291  }
   2292 
   2293  rv = FormatBlockContainerAsSubAction(
   2294      MOZ_KnownLive(*tagName->AsStatic()),
   2295      FormatBlockMode::XULParagraphStateCommand, *editingHost);
   2296  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2297                       "HTMLEditor::FormatBlockContainerAsSubAction() failed");
   2298  return EditorBase::ToGenericNSResult(rv);
   2299 }
   2300 
   2301 // static
   2302 bool HTMLEditor::IsFormatElement(FormatBlockMode aFormatBlockMode,
   2303                                 const nsIContent& aContent) {
   2304  // FYI: Optimize for HTML command because it may run too many times.
   2305  return MOZ_LIKELY(aFormatBlockMode == FormatBlockMode::HTMLFormatBlockCommand)
   2306             ? HTMLEditUtils::IsFormatElementForFormatBlockCommand(aContent)
   2307             : (HTMLEditUtils::IsFormatElementForParagraphStateCommand(
   2308                    aContent) &&
   2309                // XXX The XUL paragraph state command treats <dl>, <dd> and
   2310                // <dt> elements but all handlers do not treat them as a format
   2311                // node.  Therefore, we keep the traditional behavior here.
   2312                !aContent.IsAnyOfHTMLElements(nsGkAtoms::dd, nsGkAtoms::dl,
   2313                                              nsGkAtoms::dt));
   2314 }
   2315 
   2316 NS_IMETHODIMP HTMLEditor::GetParagraphState(bool* aMixed,
   2317                                            nsAString& aFirstParagraphState) {
   2318  if (NS_WARN_IF(!aMixed)) {
   2319    return NS_ERROR_INVALID_ARG;
   2320  }
   2321  if (!mInitSucceeded) {
   2322    return NS_ERROR_NOT_INITIALIZED;
   2323  }
   2324 
   2325  ErrorResult error;
   2326  ParagraphStateAtSelection paragraphState(
   2327      *this, FormatBlockMode::XULParagraphStateCommand, error);
   2328  if (error.Failed()) {
   2329    NS_WARNING("ParagraphStateAtSelection failed");
   2330    return error.StealNSResult();
   2331  }
   2332 
   2333  *aMixed = paragraphState.IsMixed();
   2334  if (NS_WARN_IF(!paragraphState.GetFirstParagraphStateAtSelection())) {
   2335    // XXX Odd result, but keep this behavior for now...
   2336    aFirstParagraphState.AssignASCII("x");
   2337  } else {
   2338    paragraphState.GetFirstParagraphStateAtSelection()->ToString(
   2339        aFirstParagraphState);
   2340  }
   2341  return NS_OK;
   2342 }
   2343 
   2344 nsresult HTMLEditor::GetBackgroundColorState(bool* aMixed,
   2345                                             nsAString& aOutColor) {
   2346  if (NS_WARN_IF(!aMixed)) {
   2347    return NS_ERROR_INVALID_ARG;
   2348  }
   2349 
   2350  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   2351  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2352    return NS_ERROR_NOT_INITIALIZED;
   2353  }
   2354 
   2355  if (IsCSSEnabled()) {
   2356    // if we are in CSS mode, we have to check if the containing block defines
   2357    // a background color
   2358    nsresult rv = GetCSSBackgroundColorState(
   2359        aMixed, aOutColor,
   2360        {RetrievingBackgroundColorOption::OnlyBlockBackgroundColor,
   2361         RetrievingBackgroundColorOption::
   2362             DefaultColorIfNoSpecificBackgroundColor});
   2363    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2364                         "HTMLEditor::GetCSSBackgroundColorState() failed");
   2365    return EditorBase::ToGenericNSResult(rv);
   2366  }
   2367  // in HTML mode, we look only at page's background
   2368  nsresult rv = GetHTMLBackgroundColorState(aMixed, aOutColor);
   2369  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2370                       "HTMLEditor::GetCSSBackgroundColorState() failed");
   2371  return EditorBase::ToGenericNSResult(rv);
   2372 }
   2373 
   2374 NS_IMETHODIMP HTMLEditor::GetHighlightColorState(bool* aMixed,
   2375                                                 nsAString& aOutColor) {
   2376  if (NS_WARN_IF(!aMixed)) {
   2377    return NS_ERROR_INVALID_ARG;
   2378  }
   2379 
   2380  *aMixed = false;
   2381  aOutColor.AssignLiteral("transparent");
   2382  if (!IsCSSEnabled() && IsMailEditor()) {
   2383    return NS_OK;
   2384  }
   2385 
   2386  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   2387  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2388    return NS_ERROR_NOT_INITIALIZED;
   2389  }
   2390 
   2391  // in CSS mode, text background can be added by the Text Highlight button
   2392  // we need to query the background of the selection without looking for
   2393  // the block container of the ranges in the selection
   2394  RetrievingBackgroundColorOptions options;
   2395  if (IsMailEditor()) {
   2396    options += RetrievingBackgroundColorOption::StopAtInclusiveAncestorBlock;
   2397  }
   2398  nsresult rv = GetCSSBackgroundColorState(aMixed, aOutColor, options);
   2399  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2400                       "HTMLEditor::GetCSSBackgroundColorState() failed");
   2401  return rv;
   2402 }
   2403 
   2404 nsresult HTMLEditor::GetCSSBackgroundColorState(
   2405    bool* aMixed, nsAString& aOutColor,
   2406    RetrievingBackgroundColorOptions aOptions) {
   2407  MOZ_ASSERT(IsEditActionDataAvailable());
   2408 
   2409  if (NS_WARN_IF(!aMixed)) {
   2410    return NS_ERROR_INVALID_ARG;
   2411  }
   2412 
   2413  *aMixed = false;
   2414  // the default background color is transparent
   2415  aOutColor.AssignLiteral("transparent");
   2416 
   2417  RefPtr<const nsRange> firstRange = SelectionRef().GetRangeAt(0);
   2418  if (NS_WARN_IF(!firstRange)) {
   2419    return NS_ERROR_FAILURE;
   2420  }
   2421 
   2422  nsCOMPtr<nsINode> startContainer = firstRange->GetStartContainer();
   2423  if (NS_WARN_IF(!startContainer) || NS_WARN_IF(!startContainer->IsContent())) {
   2424    return NS_ERROR_FAILURE;
   2425  }
   2426 
   2427  // is the selection collapsed?
   2428  nsIContent* contentToExamine;
   2429  if (SelectionRef().IsCollapsed() || startContainer->IsText()) {
   2430    if (NS_WARN_IF(!startContainer->IsContent())) {
   2431      return NS_ERROR_FAILURE;
   2432    }
   2433    // we want to look at the startContainer and ancestors
   2434    contentToExamine = startContainer->AsContent();
   2435  } else {
   2436    // otherwise we want to look at the first editable node after
   2437    // {startContainer,offset} and its ancestors for divs with alignment on them
   2438    contentToExamine = firstRange->GetChildAtStartOffset();
   2439    // GetNextNode(startContainer, offset, true, address_of(contentToExamine));
   2440  }
   2441 
   2442  if (NS_WARN_IF(!contentToExamine)) {
   2443    return NS_ERROR_FAILURE;
   2444  }
   2445 
   2446  if (aOptions.contains(
   2447          RetrievingBackgroundColorOption::OnlyBlockBackgroundColor)) {
   2448    // we are querying the block background (and not the text background), let's
   2449    // climb to the block container.  Note that background color of ancestor
   2450    // of editing host may be what the caller wants to know.  Therefore, we
   2451    // should ignore the editing host boundaries.
   2452    Element* const closestBlockElement =
   2453        HTMLEditUtils::GetInclusiveAncestorElement(
   2454            *contentToExamine, HTMLEditUtils::ClosestBlockElement,
   2455            BlockInlineCheck::UseComputedDisplayOutsideStyle);
   2456    if (NS_WARN_IF(!closestBlockElement)) {
   2457      return NS_OK;
   2458    }
   2459 
   2460    for (RefPtr<Element> blockElement = closestBlockElement; blockElement;) {
   2461      RefPtr<Element> nextBlockElement = HTMLEditUtils::GetAncestorElement(
   2462          *blockElement, HTMLEditUtils::ClosestBlockElement,
   2463          BlockInlineCheck::UseComputedDisplayOutsideStyle);
   2464      DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetComputedProperty(
   2465          *blockElement, *nsGkAtoms::background_color, aOutColor);
   2466      if (NS_WARN_IF(Destroyed())) {
   2467        return NS_ERROR_EDITOR_DESTROYED;
   2468      }
   2469      if (MaybeNodeRemovalsObservedByDevTools() &&
   2470          NS_WARN_IF(nextBlockElement !=
   2471                     HTMLEditUtils::GetAncestorElement(
   2472                         *blockElement, HTMLEditUtils::ClosestBlockElement,
   2473                         BlockInlineCheck::UseComputedDisplayOutsideStyle))) {
   2474        return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
   2475      }
   2476      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2477                           "CSSEditUtils::GetComputedProperty(nsGkAtoms::"
   2478                           "background_color) failed, but ignored");
   2479      // look at parent if the queried color is transparent and if the node to
   2480      // examine is not the root of the document
   2481      if (!HTMLEditUtils::IsTransparentCSSColor(aOutColor)) {
   2482        return NS_OK;
   2483      }
   2484      if (aOptions.contains(
   2485              RetrievingBackgroundColorOption::StopAtInclusiveAncestorBlock)) {
   2486        aOutColor.AssignLiteral("transparent");
   2487        return NS_OK;
   2488      }
   2489      blockElement = std::move(nextBlockElement);
   2490    }
   2491 
   2492    if (aOptions.contains(RetrievingBackgroundColorOption::
   2493                              DefaultColorIfNoSpecificBackgroundColor) &&
   2494        HTMLEditUtils::IsTransparentCSSColor(aOutColor)) {
   2495      CSSEditUtils::GetDefaultBackgroundColor(aOutColor);
   2496    }
   2497    return NS_OK;
   2498  }
   2499 
   2500  // no, we are querying the text background for the Text Highlight button
   2501  if (contentToExamine->IsText()) {
   2502    // if the node of interest is a text node, let's climb a level
   2503    contentToExamine = contentToExamine->GetParent();
   2504  }
   2505  // Return default value due to no parent node
   2506  if (!contentToExamine) {
   2507    return NS_OK;
   2508  }
   2509 
   2510  for (RefPtr<Element> element :
   2511       contentToExamine->InclusiveAncestorsOfType<Element>()) {
   2512    // is the node to examine a block ?
   2513    if (aOptions.contains(
   2514            RetrievingBackgroundColorOption::StopAtInclusiveAncestorBlock) &&
   2515        HTMLEditUtils::IsBlockElement(
   2516            *element, BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
   2517      // yes it is a block; in that case, the text background color is
   2518      // transparent
   2519      aOutColor.AssignLiteral("transparent");
   2520      break;
   2521    }
   2522 
   2523    // no, it's not; let's retrieve the computed style of background-color
   2524    // for the node to examine
   2525    nsCOMPtr<nsINode> parentNode = element->GetParentNode();
   2526    DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetComputedProperty(
   2527        *element, *nsGkAtoms::background_color, aOutColor);
   2528    if (NS_WARN_IF(Destroyed())) {
   2529      return NS_ERROR_EDITOR_DESTROYED;
   2530    }
   2531    if (NS_WARN_IF(parentNode != element->GetParentNode())) {
   2532      return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
   2533    }
   2534    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2535                         "CSSEditUtils::GetComputedProperty(nsGkAtoms::"
   2536                         "background_color) failed, but ignored");
   2537    if (!HTMLEditUtils::IsTransparentCSSColor(aOutColor)) {
   2538      HTMLEditUtils::GetNormalizedCSSColorValue(
   2539          aOutColor, HTMLEditUtils::ZeroAlphaColor::RGBAValue, aOutColor);
   2540      return NS_OK;
   2541    }
   2542  }
   2543  if (aOptions.contains(RetrievingBackgroundColorOption::
   2544                            DefaultColorIfNoSpecificBackgroundColor) &&
   2545      HTMLEditUtils::IsTransparentCSSColor(aOutColor)) {
   2546    CSSEditUtils::GetDefaultBackgroundColor(aOutColor);
   2547  }
   2548  return NS_OK;
   2549 }
   2550 
   2551 nsresult HTMLEditor::GetHTMLBackgroundColorState(bool* aMixed,
   2552                                                 nsAString& aOutColor) {
   2553  MOZ_ASSERT(IsEditActionDataAvailable());
   2554 
   2555  // TODO: We don't handle "mixed" correctly!
   2556  if (NS_WARN_IF(!aMixed)) {
   2557    return NS_ERROR_INVALID_ARG;
   2558  }
   2559 
   2560  *aMixed = false;
   2561  aOutColor.Truncate();
   2562 
   2563  Result<RefPtr<Element>, nsresult> cellOrRowOrTableElementOrError =
   2564      GetSelectedOrParentTableElement();
   2565  if (cellOrRowOrTableElementOrError.isErr()) {
   2566    NS_WARNING("HTMLEditor::GetSelectedOrParentTableElement() returned error");
   2567    return cellOrRowOrTableElementOrError.unwrapErr();
   2568  }
   2569 
   2570  for (RefPtr<Element> element = cellOrRowOrTableElementOrError.unwrap();
   2571       element; element = element->GetParentElement()) {
   2572    // We are in a cell or selected table
   2573    element->GetAttr(nsGkAtoms::bgcolor, aOutColor);
   2574 
   2575    // Done if we have a color explicitly set
   2576    if (!aOutColor.IsEmpty()) {
   2577      return NS_OK;
   2578    }
   2579 
   2580    // Once we hit the body, we're done
   2581    if (element->IsHTMLElement(nsGkAtoms::body)) {
   2582      return NS_OK;
   2583    }
   2584 
   2585    // No color is set, but we need to report visible color inherited
   2586    // from nested cells/tables, so search up parent chain so that
   2587    // let's keep checking the ancestors.
   2588  }
   2589 
   2590  // If no table or cell found, get page body
   2591  Element* rootElement = GetRoot();
   2592  if (NS_WARN_IF(!rootElement)) {
   2593    return NS_ERROR_FAILURE;
   2594  }
   2595 
   2596  rootElement->GetAttr(nsGkAtoms::bgcolor, aOutColor);
   2597  return NS_OK;
   2598 }
   2599 
   2600 NS_IMETHODIMP HTMLEditor::GetListState(bool* aMixed, bool* aOL, bool* aUL,
   2601                                       bool* aDL) {
   2602  if (NS_WARN_IF(!aMixed) || NS_WARN_IF(!aOL) || NS_WARN_IF(!aUL) ||
   2603      NS_WARN_IF(!aDL)) {
   2604    return NS_ERROR_INVALID_ARG;
   2605  }
   2606  if (!mInitSucceeded) {
   2607    return NS_ERROR_NOT_INITIALIZED;
   2608  }
   2609 
   2610  ErrorResult error;
   2611  ListElementSelectionState state(*this, error);
   2612  if (error.Failed()) {
   2613    NS_WARNING("ListElementSelectionState failed");
   2614    return error.StealNSResult();
   2615  }
   2616 
   2617  *aMixed = state.IsNotOneTypeListElementSelected();
   2618  *aOL = state.IsOLElementSelected();
   2619  *aUL = state.IsULElementSelected();
   2620  *aDL = state.IsDLElementSelected();
   2621  return NS_OK;
   2622 }
   2623 
   2624 NS_IMETHODIMP HTMLEditor::GetListItemState(bool* aMixed, bool* aLI, bool* aDT,
   2625                                           bool* aDD) {
   2626  if (NS_WARN_IF(!aMixed) || NS_WARN_IF(!aLI) || NS_WARN_IF(!aDT) ||
   2627      NS_WARN_IF(!aDD)) {
   2628    return NS_ERROR_INVALID_ARG;
   2629  }
   2630  if (!mInitSucceeded) {
   2631    return NS_ERROR_NOT_INITIALIZED;
   2632  }
   2633 
   2634  ErrorResult error;
   2635  ListItemElementSelectionState state(*this, error);
   2636  if (error.Failed()) {
   2637    NS_WARNING("ListItemElementSelectionState failed");
   2638    return error.StealNSResult();
   2639  }
   2640 
   2641  // XXX Why do we ignore `<li>` element selected state?
   2642  *aMixed = state.IsNotOneTypeDefinitionListItemElementSelected();
   2643  *aLI = state.IsLIElementSelected();
   2644  *aDT = state.IsDTElementSelected();
   2645  *aDD = state.IsDDElementSelected();
   2646  return NS_OK;
   2647 }
   2648 
   2649 NS_IMETHODIMP HTMLEditor::GetAlignment(bool* aMixed,
   2650                                       nsIHTMLEditor::EAlignment* aAlign) {
   2651  if (NS_WARN_IF(!aMixed) || NS_WARN_IF(!aAlign)) {
   2652    return NS_ERROR_INVALID_ARG;
   2653  }
   2654  if (!mInitSucceeded) {
   2655    return NS_ERROR_NOT_INITIALIZED;
   2656  }
   2657 
   2658  ErrorResult error;
   2659  AlignStateAtSelection state(*this, error);
   2660  if (error.Failed()) {
   2661    NS_WARNING("AlignStateAtSelection failed");
   2662    return error.StealNSResult();
   2663  }
   2664 
   2665  *aMixed = false;
   2666  *aAlign = state.AlignmentAtSelectionStart();
   2667  return NS_OK;
   2668 }
   2669 
   2670 NS_IMETHODIMP HTMLEditor::MakeOrChangeList(const nsAString& aListType,
   2671                                           bool aEntireList,
   2672                                           const nsAString& aBulletType) {
   2673  RefPtr<nsAtom> listTagName = NS_Atomize(aListType);
   2674  if (NS_WARN_IF(!listTagName) || NS_WARN_IF(!listTagName->IsStatic())) {
   2675    return NS_ERROR_INVALID_ARG;
   2676  }
   2677  // MOZ_KnownLive(listTagName->AsStatic()) because nsStaticAtom instances live
   2678  // while the process is running.
   2679  nsresult rv = MakeOrChangeListAsAction(
   2680      MOZ_KnownLive(*listTagName->AsStatic()), aBulletType,
   2681      aEntireList ? SelectAllOfCurrentList::Yes : SelectAllOfCurrentList::No);
   2682  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2683                       "HTMLEditor::MakeOrChangeListAsAction() failed");
   2684  return rv;
   2685 }
   2686 
   2687 nsresult HTMLEditor::MakeOrChangeListAsAction(
   2688    const nsStaticAtom& aListElementTagName, const nsAString& aBulletType,
   2689    SelectAllOfCurrentList aSelectAllOfCurrentList, nsIPrincipal* aPrincipal) {
   2690  if (NS_WARN_IF(!mInitSucceeded)) {
   2691    return NS_ERROR_NOT_INITIALIZED;
   2692  }
   2693 
   2694  AutoEditActionDataSetter editActionData(
   2695      *this, HTMLEditUtils::GetEditActionForInsert(aListElementTagName),
   2696      aPrincipal);
   2697  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2698    return NS_ERROR_NOT_INITIALIZED;
   2699  }
   2700 
   2701  const RefPtr<Element> editingHost =
   2702      ComputeEditingHost(LimitInBodyElement::No);
   2703  if (!editingHost || editingHost->IsContentEditablePlainTextOnly()) {
   2704    return NS_SUCCESS_DOM_NO_OPERATION;
   2705  }
   2706 
   2707  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   2708  if (NS_FAILED(rv)) {
   2709    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2710                         "MaybeDispatchBeforeInputEvent(), failed");
   2711    return EditorBase::ToGenericNSResult(rv);
   2712  }
   2713 
   2714  Result<EditActionResult, nsresult> result =
   2715      MakeOrChangeListAndListItemAsSubAction(aListElementTagName, aBulletType,
   2716                                             aSelectAllOfCurrentList,
   2717                                             *editingHost);
   2718  if (MOZ_UNLIKELY(result.isErr())) {
   2719    NS_WARNING("HTMLEditor::MakeOrChangeListAndListItemAsSubAction() failed");
   2720    return EditorBase::ToGenericNSResult(result.unwrapErr());
   2721  }
   2722  return NS_OK;
   2723 }
   2724 
   2725 NS_IMETHODIMP HTMLEditor::RemoveList(const nsAString& aListType) {
   2726  nsresult rv = RemoveListAsAction(aListType);
   2727  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2728                       "HTMLEditor::RemoveListAsAction() failed");
   2729  return rv;
   2730 }
   2731 
   2732 nsresult HTMLEditor::RemoveListAsAction(const nsAString& aListType,
   2733                                        nsIPrincipal* aPrincipal) {
   2734  if (NS_WARN_IF(!mInitSucceeded)) {
   2735    return NS_ERROR_NOT_INITIALIZED;
   2736  }
   2737 
   2738  // Note that we ignore aListType when we actually remove parent list elements.
   2739  // However, we need to set InputEvent.inputType to "insertOrderedList" or
   2740  // "insertedUnorderedList" when this is called for
   2741  // execCommand("insertorderedlist") or execCommand("insertunorderedlist").
   2742  // Otherwise, comm-central UI may call this methods with "dl" or "".
   2743  // So, it's okay to use mismatched EditAction here if this is called in
   2744  // comm-central.
   2745 
   2746  RefPtr<nsAtom> listAtom = NS_Atomize(aListType);
   2747  if (NS_WARN_IF(!listAtom)) {
   2748    return NS_ERROR_INVALID_ARG;
   2749  }
   2750  AutoEditActionDataSetter editActionData(
   2751      *this, HTMLEditUtils::GetEditActionForRemoveList(*listAtom), aPrincipal);
   2752  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   2753  if (NS_FAILED(rv)) {
   2754    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2755                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   2756    return EditorBase::ToGenericNSResult(rv);
   2757  }
   2758 
   2759  const RefPtr<Element> editingHost = ComputeEditingHost();
   2760  if (!editingHost) {
   2761    return NS_SUCCESS_DOM_NO_OPERATION;
   2762  }
   2763 
   2764  rv = RemoveListAtSelectionAsSubAction(*editingHost);
   2765  NS_WARNING_ASSERTION(NS_FAILED(rv),
   2766                       "HTMLEditor::RemoveListAtSelectionAsSubAction() failed");
   2767  return rv;
   2768 }
   2769 
   2770 nsresult HTMLEditor::FormatBlockContainerAsSubAction(
   2771    const nsStaticAtom& aTagName, FormatBlockMode aFormatBlockMode,
   2772    const Element& aEditingHost) {
   2773  MOZ_ASSERT(IsEditActionDataAvailable());
   2774 
   2775  if (NS_WARN_IF(!mInitSucceeded)) {
   2776    return NS_ERROR_NOT_INITIALIZED;
   2777  }
   2778 
   2779  MOZ_ASSERT(&aTagName != nsGkAtoms::dd && &aTagName != nsGkAtoms::dt);
   2780 
   2781  AutoPlaceholderBatch treatAsOneTransaction(
   2782      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   2783  IgnoredErrorResult ignoredError;
   2784  AutoEditSubActionNotifier startToHandleEditSubAction(
   2785      *this, EditSubAction::eCreateOrRemoveBlock, nsIEditor::eNext,
   2786      ignoredError);
   2787  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   2788    return ignoredError.StealNSResult();
   2789  }
   2790  NS_WARNING_ASSERTION(
   2791      !ignoredError.Failed(),
   2792      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   2793 
   2794  {
   2795    Result<EditActionResult, nsresult> result = CanHandleHTMLEditSubAction();
   2796    if (MOZ_UNLIKELY(result.isErr())) {
   2797      NS_WARNING("HTMLEditor::CanHandleHTMLEditSubAction() failed");
   2798      return result.unwrapErr();
   2799    }
   2800    if (result.inspect().Canceled()) {
   2801      return NS_OK;
   2802    }
   2803  }
   2804 
   2805  if (IsSelectionRangeContainerNotContent()) {
   2806    return NS_SUCCESS_DOM_NO_OPERATION;
   2807  }
   2808 
   2809  nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
   2810  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   2811    return NS_ERROR_EDITOR_DESTROYED;
   2812  }
   2813  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2814                       "EditorBase::EnsureNoPaddingBRElementForEmptyEditor() "
   2815                       "failed, but ignored");
   2816 
   2817  if (NS_SUCCEEDED(rv) && SelectionRef().IsCollapsed()) {
   2818    nsresult rv = EnsureCaretNotAfterInvisibleBRElement(aEditingHost);
   2819    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   2820      return NS_ERROR_EDITOR_DESTROYED;
   2821    }
   2822    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2823                         "HTMLEditor::EnsureCaretNotAfterInvisibleBRElement() "
   2824                         "failed, but ignored");
   2825    if (NS_SUCCEEDED(rv)) {
   2826      nsresult rv = PrepareInlineStylesForCaret();
   2827      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   2828        return NS_ERROR_EDITOR_DESTROYED;
   2829      }
   2830      NS_WARNING_ASSERTION(
   2831          NS_SUCCEEDED(rv),
   2832          "HTMLEditor::PrepareInlineStylesForCaret() failed, but ignored");
   2833    }
   2834  }
   2835 
   2836  // FormatBlockContainerWithTransaction() creates AutoSelectionRestorer.
   2837  // Therefore, even if it returns NS_OK, editor might have been destroyed
   2838  // at restoring Selection.
   2839  AutoClonedSelectionRangeArray selectionRanges(SelectionRef());
   2840  Result<RefPtr<Element>, nsresult> suggestBlockElementToPutCaretOrError =
   2841      FormatBlockContainerWithTransaction(selectionRanges, aTagName,
   2842                                          aFormatBlockMode, aEditingHost);
   2843  if (suggestBlockElementToPutCaretOrError.isErr()) {
   2844    NS_WARNING("HTMLEditor::FormatBlockContainerWithTransaction() failed");
   2845    return suggestBlockElementToPutCaretOrError.unwrapErr();
   2846  }
   2847 
   2848  if (selectionRanges.HasSavedRanges()) {
   2849    selectionRanges.RestoreFromSavedRanges();
   2850  }
   2851 
   2852  if (selectionRanges.IsCollapsed()) {
   2853    // FIXME: If we get rid of the legacy mutation events, we should be able to
   2854    // just insert a line break without empty check.
   2855    Result<CreateLineBreakResult, nsresult>
   2856        insertPaddingBRElementResultOrError =
   2857            InsertPaddingBRElementIfInEmptyBlock(
   2858                selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>(),
   2859                eNoStrip);
   2860    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
   2861      NS_WARNING(
   2862          "HTMLEditor::InsertPaddingBRElementIfInEmptyBlock(eNoStrip) failed");
   2863      return insertPaddingBRElementResultOrError.unwrapErr();
   2864    }
   2865    EditorDOMPoint pointToPutCaret;
   2866    insertPaddingBRElementResultOrError.unwrap().MoveCaretPointTo(
   2867        pointToPutCaret, *this,
   2868        {SuggestCaret::OnlyIfHasSuggestion,
   2869         SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
   2870    if (pointToPutCaret.IsSet()) {
   2871      nsresult rv = selectionRanges.Collapse(pointToPutCaret);
   2872      if (NS_FAILED(rv)) {
   2873        NS_WARNING("AutoClonedRangeArray::Collapse() failed");
   2874        return rv;
   2875      }
   2876    }
   2877  }
   2878 
   2879  if (!suggestBlockElementToPutCaretOrError.inspect() ||
   2880      !selectionRanges.IsCollapsed()) {
   2881    nsresult rv = selectionRanges.ApplyTo(SelectionRef());
   2882    if (NS_WARN_IF(Destroyed())) {
   2883      return NS_ERROR_EDITOR_DESTROYED;
   2884    }
   2885    NS_WARNING_ASSERTION(
   2886        NS_SUCCEEDED(rv),
   2887        "AutoClonedSelectionRangeArray::ApplyTo() failed, but ignored");
   2888    return rv;
   2889  }
   2890 
   2891  const auto firstSelectionStartPoint =
   2892      selectionRanges.GetFirstRangeStartPoint<EditorRawDOMPoint>();
   2893  if (NS_WARN_IF(!firstSelectionStartPoint.IsSetAndValidInComposedDoc())) {
   2894    return NS_ERROR_FAILURE;
   2895  }
   2896  Result<EditorRawDOMPoint, nsresult> pointInBlockElementOrError =
   2897      HTMLEditUtils::ComputePointToPutCaretInElementIfOutside<
   2898          EditorRawDOMPoint>(*suggestBlockElementToPutCaretOrError.inspect(),
   2899                             firstSelectionStartPoint);
   2900  NS_WARNING_ASSERTION(
   2901      pointInBlockElementOrError.isOk(),
   2902      "HTMLEditUtils::ComputePointToPutCaretInElementIfOutside() failed, but "
   2903      "ignored");
   2904  // Note that if the point is unset, it means that firstSelectionStartPoint is
   2905  // in the block element.
   2906  if (MOZ_LIKELY(pointInBlockElementOrError.isOk()) &&
   2907      pointInBlockElementOrError.inspect().IsSet()) {
   2908    nsresult rv =
   2909        selectionRanges.Collapse(pointInBlockElementOrError.inspect());
   2910    if (NS_FAILED(rv)) {
   2911      NS_WARNING("AutoClonedRangeArray::Collapse() failed");
   2912      return rv;
   2913    }
   2914  }
   2915 
   2916  rv = selectionRanges.ApplyTo(SelectionRef());
   2917  if (NS_WARN_IF(Destroyed())) {
   2918    return NS_ERROR_EDITOR_DESTROYED;
   2919  }
   2920  NS_WARNING_ASSERTION(
   2921      NS_SUCCEEDED(rv),
   2922      "AutoClonedSelectionRangeArray::ApplyTo() failed, but ignored");
   2923  return rv;
   2924 }
   2925 
   2926 nsresult HTMLEditor::IndentAsAction(nsIPrincipal* aPrincipal) {
   2927  if (NS_WARN_IF(!mInitSucceeded)) {
   2928    return NS_ERROR_NOT_INITIALIZED;
   2929  }
   2930 
   2931  AutoEditActionDataSetter editActionData(*this, EditAction::eIndent,
   2932                                          aPrincipal);
   2933  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2934    return NS_ERROR_NOT_INITIALIZED;
   2935  }
   2936 
   2937  const RefPtr<Element> editingHost =
   2938      ComputeEditingHost(LimitInBodyElement::No);
   2939  if (!editingHost || editingHost->IsContentEditablePlainTextOnly()) {
   2940    return NS_SUCCESS_DOM_NO_OPERATION;
   2941  }
   2942 
   2943  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   2944  if (NS_FAILED(rv)) {
   2945    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2946                         "MaybeDispatchBeforeInputEvent(), failed");
   2947    return EditorBase::ToGenericNSResult(rv);
   2948  }
   2949 
   2950  Result<EditActionResult, nsresult> result = IndentAsSubAction(*editingHost);
   2951  if (MOZ_UNLIKELY(result.isErr())) {
   2952    NS_WARNING("HTMLEditor::IndentAsSubAction() failed");
   2953    return EditorBase::ToGenericNSResult(result.unwrapErr());
   2954  }
   2955  return NS_OK;
   2956 }
   2957 
   2958 nsresult HTMLEditor::OutdentAsAction(nsIPrincipal* aPrincipal) {
   2959  if (NS_WARN_IF(!mInitSucceeded)) {
   2960    return NS_ERROR_NOT_INITIALIZED;
   2961  }
   2962 
   2963  AutoEditActionDataSetter editActionData(*this, EditAction::eOutdent,
   2964                                          aPrincipal);
   2965  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2966    return NS_ERROR_NOT_INITIALIZED;
   2967  }
   2968 
   2969  const RefPtr<Element> editingHost =
   2970      ComputeEditingHost(LimitInBodyElement::No);
   2971  if (!editingHost || editingHost->IsContentEditablePlainTextOnly()) {
   2972    return NS_SUCCESS_DOM_NO_OPERATION;
   2973  }
   2974 
   2975  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   2976  if (NS_FAILED(rv)) {
   2977    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   2978                         "MaybeDispatchBeforeInputEvent(), failed");
   2979    return EditorBase::ToGenericNSResult(rv);
   2980  }
   2981 
   2982  Result<EditActionResult, nsresult> result = OutdentAsSubAction(*editingHost);
   2983  if (MOZ_UNLIKELY(result.isErr())) {
   2984    NS_WARNING("HTMLEditor::OutdentAsSubAction() failed");
   2985    return EditorBase::ToGenericNSResult(result.unwrapErr());
   2986  }
   2987  return NS_OK;
   2988 }
   2989 
   2990 // TODO: IMPLEMENT ALIGNMENT!
   2991 
   2992 nsresult HTMLEditor::AlignAsAction(const nsAString& aAlignType,
   2993                                   nsIPrincipal* aPrincipal) {
   2994  AutoEditActionDataSetter editActionData(
   2995      *this, HTMLEditUtils::GetEditActionForAlignment(aAlignType), aPrincipal);
   2996  if (NS_WARN_IF(!editActionData.CanHandle())) {
   2997    return NS_ERROR_NOT_INITIALIZED;
   2998  }
   2999 
   3000  const RefPtr<Element> editingHost =
   3001      ComputeEditingHost(LimitInBodyElement::No);
   3002  if (!editingHost || editingHost->IsContentEditablePlainTextOnly()) {
   3003    return NS_SUCCESS_DOM_NO_OPERATION;
   3004  }
   3005 
   3006  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   3007  if (NS_FAILED(rv)) {
   3008    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   3009                         "MaybeDispatchBeforeInputEvent(), failed");
   3010    return EditorBase::ToGenericNSResult(rv);
   3011  }
   3012 
   3013  Result<EditActionResult, nsresult> result =
   3014      AlignAsSubAction(aAlignType, *editingHost);
   3015  if (MOZ_UNLIKELY(result.isErr())) {
   3016    NS_WARNING("HTMLEditor::AlignAsSubAction() failed");
   3017    return EditorBase::ToGenericNSResult(result.unwrapErr());
   3018  }
   3019  return NS_OK;
   3020 }
   3021 
   3022 Element* HTMLEditor::GetInclusiveAncestorByTagName(const nsStaticAtom& aTagName,
   3023                                                   nsIContent& aContent) const {
   3024  MOZ_ASSERT(&aTagName != nsGkAtoms::_empty);
   3025 
   3026  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   3027  if (NS_WARN_IF(!editActionData.CanHandle())) {
   3028    return nullptr;
   3029  }
   3030 
   3031  return GetInclusiveAncestorByTagNameInternal(aTagName, aContent);
   3032 }
   3033 
   3034 Element* HTMLEditor::GetInclusiveAncestorByTagNameAtSelection(
   3035    const nsStaticAtom& aTagName) const {
   3036  MOZ_ASSERT(IsEditActionDataAvailable());
   3037  MOZ_ASSERT(&aTagName != nsGkAtoms::_empty);
   3038 
   3039  // If no node supplied, get it from anchor node of current selection
   3040  const EditorRawDOMPoint atAnchor(SelectionRef().AnchorRef());
   3041  if (NS_WARN_IF(!atAnchor.IsInContentNode())) {
   3042    return nullptr;
   3043  }
   3044 
   3045  // Try to get the actual selected node
   3046  nsIContent* content = nullptr;
   3047  if (atAnchor.GetContainer()->HasChildNodes() &&
   3048      atAnchor.ContainerAs<nsIContent>()) {
   3049    content = atAnchor.GetChild();
   3050  }
   3051  // Anchor node is probably a text node - just use that
   3052  if (!content) {
   3053    content = atAnchor.ContainerAs<nsIContent>();
   3054    if (NS_WARN_IF(!content)) {
   3055      return nullptr;
   3056    }
   3057  }
   3058  return GetInclusiveAncestorByTagNameInternal(aTagName, *content);
   3059 }
   3060 
   3061 Element* HTMLEditor::GetInclusiveAncestorByTagNameInternal(
   3062    const nsStaticAtom& aTagName, const nsIContent& aContent) const {
   3063  MOZ_ASSERT(&aTagName != nsGkAtoms::_empty);
   3064 
   3065  Element* currentElement = aContent.GetAsElementOrParentElement();
   3066  if (NS_WARN_IF(!currentElement)) {
   3067    MOZ_ASSERT(!aContent.GetParentNode());
   3068    return nullptr;
   3069  }
   3070 
   3071  bool lookForLink = IsLinkTag(aTagName);
   3072  bool lookForNamedAnchor = IsNamedAnchorTag(aTagName);
   3073  for (Element* const element :
   3074       currentElement->InclusiveAncestorsOfType<Element>()) {
   3075    // Stop searching if parent is a body element.  Note: Originally used
   3076    // IsRoot() to/ stop at table cells, but that's too messy when you are
   3077    // trying to find the parent table.
   3078    if (element->IsHTMLElement(nsGkAtoms::body)) {
   3079      return nullptr;
   3080    }
   3081    if (lookForLink) {
   3082      // Test if we have a link (an anchor with href set)
   3083      if (HTMLEditUtils::IsHyperlinkElement(*element)) {
   3084        return element;
   3085      }
   3086    } else if (lookForNamedAnchor) {
   3087      // Test if we have a named anchor (an anchor with name set)
   3088      if (HTMLEditUtils::IsNamedAnchorElement(*element)) {
   3089        return element;
   3090      }
   3091    } else if (&aTagName == nsGkAtoms::list) {
   3092      // Match "ol", "ul", or "dl" for lists
   3093      if (HTMLEditUtils::IsListElement(*element)) {
   3094        return element;
   3095      }
   3096    } else if (&aTagName == nsGkAtoms::td) {
   3097      // Table cells are another special case: match either "td" or "th"
   3098      if (HTMLEditUtils::IsTableCellElement(*element)) {
   3099        return element;
   3100      }
   3101    } else if (&aTagName == element->NodeInfo()->NameAtom()) {
   3102      return element;
   3103    }
   3104  }
   3105  return nullptr;
   3106 }
   3107 
   3108 NS_IMETHODIMP HTMLEditor::GetElementOrParentByTagName(const nsAString& aTagName,
   3109                                                      nsINode* aNode,
   3110                                                      Element** aReturn) {
   3111  if (NS_WARN_IF(aTagName.IsEmpty()) || NS_WARN_IF(!aReturn)) {
   3112    return NS_ERROR_INVALID_ARG;
   3113  }
   3114 
   3115  nsStaticAtom* tagName = EditorUtils::GetTagNameAtom(aTagName);
   3116  if (NS_WARN_IF(!tagName)) {
   3117    // We don't need to support custom elements since this is an internal API.
   3118    return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
   3119  }
   3120  if (NS_WARN_IF(tagName == nsGkAtoms::_empty)) {
   3121    return NS_ERROR_INVALID_ARG;
   3122  }
   3123 
   3124  if (!aNode) {
   3125    AutoEditActionDataSetter dummyEditAction(*this, EditAction::eNotEditing);
   3126    if (NS_WARN_IF(!dummyEditAction.CanHandle())) {
   3127      return NS_ERROR_NOT_AVAILABLE;
   3128    }
   3129    RefPtr<Element> parentElement =
   3130        GetInclusiveAncestorByTagNameAtSelection(*tagName);
   3131    if (!parentElement) {
   3132      return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
   3133    }
   3134    parentElement.forget(aReturn);
   3135    return NS_OK;
   3136  }
   3137 
   3138  if (!aNode->IsContent() || !aNode->GetAsElementOrParentElement()) {
   3139    return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
   3140  }
   3141 
   3142  RefPtr<Element> parentElement =
   3143      GetInclusiveAncestorByTagName(*tagName, *aNode->AsContent());
   3144  if (!parentElement) {
   3145    return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
   3146  }
   3147  parentElement.forget(aReturn);
   3148  return NS_OK;
   3149 }
   3150 
   3151 NS_IMETHODIMP HTMLEditor::GetSelectedElement(const nsAString& aTagName,
   3152                                             nsISupports** aReturn) {
   3153  if (NS_WARN_IF(!aReturn)) {
   3154    return NS_ERROR_INVALID_ARG;
   3155  }
   3156  *aReturn = nullptr;
   3157 
   3158  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   3159  if (NS_WARN_IF(!editActionData.CanHandle())) {
   3160    return NS_ERROR_NOT_INITIALIZED;
   3161  }
   3162 
   3163  ErrorResult error;
   3164  nsStaticAtom* tagName = EditorUtils::GetTagNameAtom(aTagName);
   3165  if (!aTagName.IsEmpty() && !tagName) {
   3166    // We don't need to support custom elements becaus of internal API.
   3167    return NS_OK;
   3168  }
   3169  RefPtr<nsINode> selectedNode = GetSelectedElement(tagName, error);
   3170  NS_WARNING_ASSERTION(!error.Failed(),
   3171                       "HTMLEditor::GetSelectedElement() failed");
   3172  selectedNode.forget(aReturn);
   3173  return error.StealNSResult();
   3174 }
   3175 
   3176 already_AddRefed<Element> HTMLEditor::GetSelectedElement(const nsAtom* aTagName,
   3177                                                         ErrorResult& aRv) {
   3178  MOZ_ASSERT(IsEditActionDataAvailable());
   3179 
   3180  MOZ_ASSERT(!aRv.Failed());
   3181 
   3182  // If there is no Selection or two or more selection ranges, that means that
   3183  // not only one element is selected so that return nullptr.
   3184  if (SelectionRef().RangeCount() != 1) {
   3185    return nullptr;
   3186  }
   3187 
   3188  bool isLinkTag = aTagName && IsLinkTag(*aTagName);
   3189  bool isNamedAnchorTag = aTagName && IsNamedAnchorTag(*aTagName);
   3190 
   3191  RefPtr<nsRange> firstRange = SelectionRef().GetRangeAt(0);
   3192  MOZ_ASSERT(firstRange);
   3193 
   3194  const RangeBoundary& startRef = firstRange->StartRef();
   3195  if (NS_WARN_IF(!startRef.IsSet())) {
   3196    aRv.Throw(NS_ERROR_FAILURE);
   3197    return nullptr;
   3198  }
   3199  const RangeBoundary& endRef = firstRange->EndRef();
   3200  if (NS_WARN_IF(!endRef.IsSet())) {
   3201    aRv.Throw(NS_ERROR_FAILURE);
   3202    return nullptr;
   3203  }
   3204 
   3205  // Optimization for a single selected element
   3206  if (startRef.GetContainer() == endRef.GetContainer()) {
   3207    nsIContent* const startContent = startRef.GetChildAtOffset();
   3208    nsIContent* const endContent = endRef.GetChildAtOffset();
   3209    if (startContent && endContent &&
   3210        startContent->GetNextSibling() == endContent) {
   3211      if (!aTagName) {
   3212        if (!startContent->IsElement()) {
   3213          // This means only a text node or something is selected.  We should
   3214          // return nullptr in this case since no other elements are selected.
   3215          return nullptr;
   3216        }
   3217        return do_AddRef(startContent->AsElement());
   3218      }
   3219      // Test for appropriate node type requested
   3220      if (aTagName == startContent->NodeInfo()->NameAtom() ||
   3221          (isLinkTag && HTMLEditUtils::IsHyperlinkElement(*startContent)) ||
   3222          (isNamedAnchorTag &&
   3223           HTMLEditUtils::IsNamedAnchorElement(*startContent))) {
   3224        MOZ_ASSERT(startContent->IsElement());
   3225        return do_AddRef(startContent->AsElement());
   3226      }
   3227    }
   3228  }
   3229 
   3230  if (isLinkTag && startRef.GetContainer()->IsContent() &&
   3231      endRef.GetContainer()->IsContent()) {
   3232    // Link node must be the same for both ends of selection.
   3233    Element* parentLinkOfStart = GetInclusiveAncestorByTagNameInternal(
   3234        *nsGkAtoms::href, *startRef.GetContainer()->AsContent());
   3235    if (parentLinkOfStart) {
   3236      if (SelectionRef().IsCollapsed()) {
   3237        // We have just a caret in the link.
   3238        return do_AddRef(parentLinkOfStart);
   3239      }
   3240      // Link node must be the same for both ends of selection.
   3241      Element* parentLinkOfEnd = GetInclusiveAncestorByTagNameInternal(
   3242          *nsGkAtoms::href, *endRef.GetContainer()->AsContent());
   3243      if (parentLinkOfStart == parentLinkOfEnd) {
   3244        return do_AddRef(parentLinkOfStart);
   3245      }
   3246    }
   3247  }
   3248 
   3249  if (SelectionRef().IsCollapsed()) {
   3250    return nullptr;
   3251  }
   3252 
   3253  PostContentIterator postOrderIter;
   3254  nsresult rv = postOrderIter.Init(firstRange);
   3255  if (NS_FAILED(rv)) {
   3256    aRv.Throw(rv);
   3257    return nullptr;
   3258  }
   3259 
   3260  RefPtr<Element> lastElementInRange;
   3261  for (nsINode* lastNodeInRange = nullptr; !postOrderIter.IsDone();
   3262       postOrderIter.Next()) {
   3263    if (lastElementInRange) {
   3264      // When any node follows an element node, not only one element is
   3265      // selected so that return nullptr.
   3266      return nullptr;
   3267    }
   3268 
   3269    // This loop ignors any non-element nodes before first element node.
   3270    // Its purpose must be that this method treats this case as selecting
   3271    // the <b> element:
   3272    // - <p>abc <b>d[ef</b>}</p>
   3273    // because children of an element node is listed up before the element.
   3274    // However, this case must not be expected by the initial developer:
   3275    // - <p>a[bc <b>def</b>}</p>
   3276    // When we meet non-parent and non-next-sibling node of previous node,
   3277    // it means that the range across element boundary (open tag in HTML
   3278    // source).  So, in this case, we should not say only the following
   3279    // element is selected.
   3280    nsINode* currentNode = postOrderIter.GetCurrentNode();
   3281    MOZ_ASSERT(currentNode);
   3282    if (lastNodeInRange && lastNodeInRange->GetParentNode() != currentNode &&
   3283        lastNodeInRange->GetNextSibling() != currentNode) {
   3284      return nullptr;
   3285    }
   3286 
   3287    lastNodeInRange = currentNode;
   3288 
   3289    lastElementInRange = Element::FromNodeOrNull(lastNodeInRange);
   3290    if (!lastElementInRange) {
   3291      continue;
   3292    }
   3293 
   3294    // And also, if it's followed by a <br> element, we shouldn't treat the
   3295    // the element is selected like this case:
   3296    // - <p><b>[def</b>}<br></p>
   3297    // Note that we don't need special handling for <a href> because double
   3298    // clicking it selects the element and we use the first path to handle it.
   3299    // Additionally, we have this case too:
   3300    // - <p><b>[def</b><b>}<br></b></p>
   3301    // In these cases, the <br> element is not listed up by PostContentIterator.
   3302    // So, we should return nullptr if next sibling is a `<br>` element or
   3303    // next sibling starts with `<br>` element.
   3304    if (nsIContent* nextSibling = lastElementInRange->GetNextSibling()) {
   3305      if (nextSibling->IsHTMLElement(nsGkAtoms::br)) {
   3306        return nullptr;
   3307      }
   3308      nsIContent* firstEditableLeaf = HTMLEditUtils::GetFirstLeafContent(
   3309          *nextSibling, {LeafNodeType::OnlyLeafNode});
   3310      if (firstEditableLeaf &&
   3311          firstEditableLeaf->IsHTMLElement(nsGkAtoms::br)) {
   3312        return nullptr;
   3313      }
   3314    }
   3315 
   3316    if (!aTagName) {
   3317      continue;
   3318    }
   3319 
   3320    if (isLinkTag && HTMLEditUtils::IsHyperlinkElement(*lastElementInRange)) {
   3321      continue;
   3322    }
   3323 
   3324    if (isNamedAnchorTag &&
   3325        HTMLEditUtils::IsNamedAnchorElement(*lastElementInRange)) {
   3326      continue;
   3327    }
   3328 
   3329    if (aTagName == lastElementInRange->NodeInfo()->NameAtom()) {
   3330      continue;
   3331    }
   3332 
   3333    // First element in the range does not match what the caller is looking
   3334    // for.
   3335    return nullptr;
   3336  }
   3337  return lastElementInRange.forget();
   3338 }
   3339 
   3340 Result<CreateElementResult, nsresult> HTMLEditor::CreateAndInsertElement(
   3341    WithTransaction aWithTransaction, const nsAtom& aTagName,
   3342    const EditorDOMPoint& aPointToInsert,
   3343    const InitializeInsertingElement& aInitializer) {
   3344  MOZ_ASSERT(IsEditActionDataAvailable());
   3345  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   3346 
   3347  // XXX We need offset at new node for RangeUpdaterRef().  Therefore, we need
   3348  //     to compute the offset now but this is expensive.  So, if it's possible,
   3349  //     we need to redesign RangeUpdaterRef() as avoiding using indices.
   3350  (void)aPointToInsert.Offset();
   3351 
   3352  IgnoredErrorResult ignoredError;
   3353  AutoEditSubActionNotifier startToHandleEditSubAction(
   3354      *this, EditSubAction::eCreateNode, nsIEditor::eNext, ignoredError);
   3355  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   3356    return Err(NS_ERROR_EDITOR_DESTROYED);
   3357  }
   3358  NS_WARNING_ASSERTION(
   3359      !ignoredError.Failed(),
   3360      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   3361 
   3362  // TODO: This method should have a callback function which is called
   3363  //       immediately after creating an element but before it's inserted into
   3364  //       the DOM tree.  Then, caller can init the new element's attributes
   3365  //       and children **without** transactions (it'll reduce the number of
   3366  //       legacy mutation events).  Finally, we can get rid of
   3367  //       CreatElementTransaction since we can use InsertNodeTransaction
   3368  //       instead.
   3369 
   3370  auto createNewElementResult =
   3371      [&]() MOZ_CAN_RUN_SCRIPT -> Result<CreateElementResult, nsresult> {
   3372    RefPtr<Element> newElement = CreateHTMLContent(&aTagName);
   3373    if (MOZ_UNLIKELY(!newElement)) {
   3374      NS_WARNING("EditorBase::CreateHTMLContent() failed");
   3375      return Err(NS_ERROR_FAILURE);
   3376    }
   3377    nsresult rv = MarkElementDirty(*newElement);
   3378    if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
   3379      NS_WARNING("EditorBase::MarkElementDirty() caused destroying the editor");
   3380      return Err(NS_ERROR_EDITOR_DESTROYED);
   3381    }
   3382    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3383                         "EditorBase::MarkElementDirty() failed, but ignored");
   3384    rv = aInitializer(*this, *newElement, aPointToInsert);
   3385    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "aInitializer failed");
   3386    if (NS_WARN_IF(Destroyed())) {
   3387      return Err(NS_ERROR_EDITOR_DESTROYED);
   3388    }
   3389    RefPtr<InsertNodeTransaction> transaction =
   3390        InsertNodeTransaction::Create(*this, *newElement, aPointToInsert);
   3391    rv = aWithTransaction == WithTransaction::Yes
   3392             ? DoTransactionInternal(transaction)
   3393             : transaction->DoTransaction();
   3394    // FYI: Transaction::DoTransaction never returns NS_ERROR_EDITOR_*.
   3395    if (MOZ_UNLIKELY(Destroyed())) {
   3396      NS_WARNING(
   3397          "InsertNodeTransaction::DoTransaction() caused destroying the "
   3398          "editor");
   3399      return Err(NS_ERROR_EDITOR_DESTROYED);
   3400    }
   3401    if (NS_FAILED(rv)) {
   3402      NS_WARNING("InsertNodeTransaction::DoTransaction() failed");
   3403      return Err(rv);
   3404    }
   3405    // Override the success code if new element was moved by the web apps.
   3406    if (newElement &&
   3407        newElement->GetParentNode() != aPointToInsert.GetContainer()) {
   3408      NS_WARNING("The new element was not inserted into the expected node");
   3409      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   3410    }
   3411    return CreateElementResult(
   3412        std::move(newElement),
   3413        transaction->SuggestPointToPutCaret<EditorDOMPoint>());
   3414  }();
   3415 
   3416  if (MOZ_UNLIKELY(createNewElementResult.isErr())) {
   3417    NS_WARNING("EditorBase::DoTransactionInternal() failed");
   3418    // XXX Why do we do this even when DoTransaction() returned error?
   3419    DebugOnly<nsresult> rvIgnored =
   3420        RangeUpdaterRef().SelAdjCreateNode(aPointToInsert);
   3421    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   3422                         "RangeUpdater::SelAdjCreateNode() failed");
   3423    return createNewElementResult;
   3424  }
   3425 
   3426  // If we succeeded to create and insert new element, we need to adjust
   3427  // ranges in RangeUpdaterRef().  It currently requires offset of the new
   3428  // node.  So, let's call it with original offset.  Note that if
   3429  // aPointToInsert stores child node, it may not be at the offset since new
   3430  // element must be inserted before the old child.  Although, mutation
   3431  // observer can do anything, but currently, we don't check it.
   3432  DebugOnly<nsresult> rvIgnored =
   3433      RangeUpdaterRef().SelAdjCreateNode(EditorRawDOMPoint(
   3434          aPointToInsert.GetContainer(), aPointToInsert.Offset()));
   3435  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   3436                       "RangeUpdater::SelAdjCreateNode() failed, but ignored");
   3437  if (MOZ_LIKELY(createNewElementResult.inspect().GetNewNode())) {
   3438    TopLevelEditSubActionDataRef().DidCreateElement(
   3439        *this, *createNewElementResult.inspect().GetNewNode());
   3440  }
   3441 
   3442  return createNewElementResult;
   3443 }
   3444 
   3445 nsresult HTMLEditor::CopyAttributes(WithTransaction aWithTransaction,
   3446                                    Element& aDestElement, Element& aSrcElement,
   3447                                    const AttributeFilter& aFilterFunc) {
   3448  if (!aSrcElement.GetAttrCount()) {
   3449    return NS_OK;
   3450  }
   3451  struct MOZ_STACK_CLASS AttrCache {
   3452    int32_t mNamespaceID;
   3453    const OwningNonNull<nsAtom> mName;
   3454    nsString mValue;
   3455  };
   3456  AutoTArray<AttrCache, 16> srcAttrs;
   3457  srcAttrs.SetCapacity(aSrcElement.GetAttrCount());
   3458  for (const uint32_t i : IntegerRange(aSrcElement.GetAttrCount())) {
   3459    const BorrowedAttrInfo attrInfo = aSrcElement.GetAttrInfoAt(i);
   3460    if (const nsAttrName* attrName = attrInfo.mName) {
   3461      MOZ_ASSERT(attrName->LocalName());
   3462      MOZ_ASSERT(attrInfo.mValue);
   3463      nsString attrValue;
   3464      attrInfo.mValue->ToString(attrValue);
   3465      srcAttrs.AppendElement(AttrCache{attrInfo.mName->NamespaceID(),
   3466                                       *attrName->LocalName(), attrValue});
   3467    }
   3468  }
   3469  if (aWithTransaction == WithTransaction::No) {
   3470    for (auto& attr : srcAttrs) {
   3471      if (!aFilterFunc(*this, aSrcElement, aDestElement, attr.mNamespaceID,
   3472                       attr.mName, attr.mValue)) {
   3473        continue;
   3474      }
   3475      DebugOnly<nsresult> rvIgnored =
   3476          AutoElementAttrAPIWrapper(*this, aDestElement)
   3477              .SetAttr(MOZ_KnownLive(attr.mName), attr.mValue, false);
   3478      NS_WARNING_ASSERTION(
   3479          NS_SUCCEEDED(rvIgnored) && rvIgnored != NS_ERROR_EDITOR_DESTROYED,
   3480          "AutoElementAttrAPIWrapper::SetAttr() failed, but ignored");
   3481    }
   3482    if (NS_WARN_IF(Destroyed())) {
   3483      return NS_ERROR_EDITOR_DESTROYED;
   3484    }
   3485    return NS_OK;
   3486  }
   3487  MOZ_ASSERT_UNREACHABLE("Not implemented yet, but you try to use this");
   3488  return NS_ERROR_NOT_IMPLEMENTED;
   3489 }
   3490 
   3491 already_AddRefed<Element> HTMLEditor::CreateElementWithDefaults(
   3492    const nsAtom& aTagName) {
   3493  // NOTE: Despite of public method, this can be called for internal use.
   3494 
   3495  // Although this creates an element, but won't change the DOM tree nor
   3496  // transaction.  So, EditAction::eNotEditing is proper value here.  If
   3497  // this is called for internal when there is already AutoEditActionDataSetter
   3498  // instance, this would be initialized with its EditAction value.
   3499  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   3500  if (NS_WARN_IF(!editActionData.CanHandle())) {
   3501    return nullptr;
   3502  }
   3503 
   3504  const nsAtom* realTagName = IsLinkTag(aTagName) || IsNamedAnchorTag(aTagName)
   3505                                  ? nsGkAtoms::a
   3506                                  : &aTagName;
   3507 
   3508  // We don't use editor's CreateElement because we don't want to go through
   3509  // the transaction system
   3510 
   3511  // New call to use instead to get proper HTML element, bug 39919
   3512  RefPtr<Element> newElement = CreateHTMLContent(realTagName);
   3513  if (!newElement) {
   3514    return nullptr;
   3515  }
   3516 
   3517  // Mark the new element dirty, so it will be formatted
   3518  // XXX Don't we need to check the error result of setting _moz_dirty attr?
   3519  IgnoredErrorResult ignoredError;
   3520  newElement->SetAttribute(u"_moz_dirty"_ns, u""_ns, ignoredError);
   3521  NS_WARNING_ASSERTION(!ignoredError.Failed(),
   3522                       "Element::SetAttribute(_moz_dirty) failed, but ignored");
   3523  ignoredError.SuppressException();
   3524 
   3525  // Set default values for new elements
   3526  if (realTagName == nsGkAtoms::table) {
   3527    newElement->SetAttr(nsGkAtoms::cellpadding, u"2"_ns, ignoredError);
   3528    if (ignoredError.Failed()) {
   3529      NS_WARNING("Element::SetAttr(nsGkAtoms::cellpadding, 2) failed");
   3530      return nullptr;
   3531    }
   3532    ignoredError.SuppressException();
   3533 
   3534    newElement->SetAttr(nsGkAtoms::cellspacing, u"2"_ns, ignoredError);
   3535    if (ignoredError.Failed()) {
   3536      NS_WARNING("Element::SetAttr(nsGkAtoms::cellspacing, 2) failed");
   3537      return nullptr;
   3538    }
   3539    ignoredError.SuppressException();
   3540 
   3541    newElement->SetAttr(nsGkAtoms::border, u"1"_ns, ignoredError);
   3542    if (ignoredError.Failed()) {
   3543      NS_WARNING("Element::SetAttr(nsGkAtoms::border, 1) failed");
   3544      return nullptr;
   3545    }
   3546  } else if (realTagName == nsGkAtoms::td) {
   3547    nsresult rv = SetAttributeOrEquivalent(newElement, nsGkAtoms::valign,
   3548                                           u"top"_ns, true);
   3549    if (NS_FAILED(rv)) {
   3550      NS_WARNING(
   3551          "HTMLEditor::SetAttributeOrEquivalent(nsGkAtoms::valign, top) "
   3552          "failed");
   3553      return nullptr;
   3554    }
   3555  }
   3556  // ADD OTHER TAGS HERE
   3557 
   3558  return newElement.forget();
   3559 }
   3560 
   3561 NS_IMETHODIMP HTMLEditor::CreateElementWithDefaults(const nsAString& aTagName,
   3562                                                    Element** aReturn) {
   3563  if (NS_WARN_IF(aTagName.IsEmpty()) || NS_WARN_IF(!aReturn)) {
   3564    return NS_ERROR_INVALID_ARG;
   3565  }
   3566 
   3567  *aReturn = nullptr;
   3568 
   3569  nsStaticAtom* tagName = EditorUtils::GetTagNameAtom(aTagName);
   3570  if (NS_WARN_IF(!tagName)) {
   3571    return NS_ERROR_INVALID_ARG;
   3572  }
   3573  RefPtr<Element> newElement =
   3574      CreateElementWithDefaults(MOZ_KnownLive(*tagName));
   3575  if (!newElement) {
   3576    NS_WARNING("HTMLEditor::CreateElementWithDefaults() failed");
   3577    return NS_ERROR_FAILURE;
   3578  }
   3579  newElement.forget(aReturn);
   3580  return NS_OK;
   3581 }
   3582 
   3583 NS_IMETHODIMP HTMLEditor::InsertLinkAroundSelection(Element* aAnchorElement) {
   3584  nsresult rv = InsertLinkAroundSelectionAsAction(aAnchorElement);
   3585  NS_WARNING_ASSERTION(
   3586      NS_SUCCEEDED(rv),
   3587      "HTMLEditor::InsertLinkAroundSelectionAsAction() failed");
   3588  return rv;
   3589 }
   3590 
   3591 nsresult HTMLEditor::InsertLinkAroundSelectionAsAction(
   3592    Element* aAnchorElement, nsIPrincipal* aPrincipal) {
   3593  if (NS_WARN_IF(!aAnchorElement)) {
   3594    return NS_ERROR_INVALID_ARG;
   3595  }
   3596 
   3597  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLinkElement,
   3598                                          aPrincipal);
   3599  if (NS_WARN_IF(!editActionData.CanHandle())) {
   3600    return NS_ERROR_NOT_INITIALIZED;
   3601  }
   3602 
   3603  RefPtr<Element> const editingHost =
   3604      ComputeEditingHost(LimitInBodyElement::No);
   3605  if (NS_WARN_IF(!editingHost)) {
   3606    return NS_ERROR_FAILURE;
   3607  }
   3608 
   3609  if (IsPlaintextMailComposer() ||
   3610      editingHost->IsContentEditablePlainTextOnly()) {
   3611    return NS_SUCCESS_DOM_NO_OPERATION;
   3612  }
   3613 
   3614  if (SelectionRef().IsCollapsed()) {
   3615    NS_WARNING("Selection was collapsed");
   3616    return NS_OK;
   3617  }
   3618 
   3619  // Be sure we were given an anchor element
   3620  RefPtr<HTMLAnchorElement> anchor =
   3621      HTMLAnchorElement::FromNodeOrNull(aAnchorElement);
   3622  if (!anchor) {
   3623    return NS_OK;
   3624  }
   3625 
   3626  nsAutoString rawHref;
   3627  anchor->GetAttr(nsGkAtoms::href, rawHref);
   3628  editActionData.SetData(rawHref);
   3629 
   3630  nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
   3631  if (NS_FAILED(rv)) {
   3632    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   3633                         "MaybeDispatchBeforeInputEvent(), failed");
   3634    return EditorBase::ToGenericNSResult(rv);
   3635  }
   3636 
   3637  // XXX Is this ok? Does this just want to check that we're a link? If so
   3638  // there are faster ways to do this.
   3639  {
   3640    nsAutoCString href;
   3641    anchor->GetHref(href);
   3642    if (href.IsEmpty()) {
   3643      return NS_OK;
   3644    }
   3645  }
   3646 
   3647  AutoPlaceholderBatch treatAsOneTransaction(
   3648      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   3649 
   3650  // Set all attributes found on the supplied anchor element
   3651  // TODO: We should stop using this loop for adding attributes to newly created
   3652  //       `<a href="...">` elements.  Then, we can avoid to increate the ref-
   3653  //       counter of attribute names since we can use nsStaticAtom if we don't
   3654  //       need to support unknown attributes.
   3655  AutoTArray<EditorInlineStyleAndValue, 32> stylesToSet;
   3656  if (const uint32_t attrCount = anchor->GetAttrCount()) {
   3657    stylesToSet.SetCapacity(attrCount);
   3658    for (const uint32_t i : IntegerRange(attrCount)) {
   3659      const BorrowedAttrInfo attrInfo = anchor->GetAttrInfoAt(i);
   3660      if (const nsAttrName* attrName = attrInfo.mName) {
   3661        // We don't need to handle _moz_dirty attribute.  If it's required, the
   3662        // handler should add it to the new element.
   3663        if (attrName->IsAtom() && attrName->Equals(nsGkAtoms::mozdirty)) {
   3664          continue;
   3665        }
   3666        RefPtr<nsAtom> attributeName = attrName->LocalName();
   3667        MOZ_ASSERT(attrInfo.mValue);
   3668        nsString attrValue;
   3669        attrInfo.mValue->ToString(attrValue);
   3670        stylesToSet.AppendElement(EditorInlineStyleAndValue(
   3671            *nsGkAtoms::a, std::move(attributeName), std::move(attrValue)));
   3672      }
   3673    }
   3674  }
   3675  rv = SetInlinePropertiesAsSubAction(stylesToSet, *editingHost);
   3676  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3677                       "HTMLEditor::SetInlinePropertiesAsSubAction() failed");
   3678  return rv;
   3679 }
   3680 
   3681 nsresult HTMLEditor::SetHTMLBackgroundColorWithTransaction(
   3682    const nsAString& aColor) {
   3683  MOZ_ASSERT(IsEditActionDataAvailable());
   3684 
   3685  // Find a selected or enclosing table element to set background on
   3686  bool isCellSelected = false;
   3687  Result<RefPtr<Element>, nsresult> cellOrRowOrTableElementOrError =
   3688      GetSelectedOrParentTableElement(&isCellSelected);
   3689  if (cellOrRowOrTableElementOrError.isErr()) {
   3690    NS_WARNING("HTMLEditor::GetSelectedOrParentTableElement() failed");
   3691    return cellOrRowOrTableElementOrError.unwrapErr();
   3692  }
   3693 
   3694  bool setColor = !aColor.IsEmpty();
   3695  RefPtr<Element> rootElementOfBackgroundColor =
   3696      cellOrRowOrTableElementOrError.unwrap();
   3697  if (rootElementOfBackgroundColor) {
   3698    // Needs to set or remove background color of each selected cell elements.
   3699    // Therefore, just the cell contains selection range, we don't need to
   3700    // do this.  Note that users can select each cell, but with Selection API,
   3701    // web apps can select <tr> and <td> at same time. With <table>, looks
   3702    // odd, though.
   3703    if (isCellSelected || rootElementOfBackgroundColor->IsAnyOfHTMLElements(
   3704                              nsGkAtoms::table, nsGkAtoms::tr)) {
   3705      SelectedTableCellScanner scanner(SelectionRef());
   3706      if (scanner.IsInTableCellSelectionMode()) {
   3707        if (setColor) {
   3708          for (const OwningNonNull<Element>& cellElement :
   3709               scanner.ElementsRef()) {
   3710            // `MOZ_KnownLive(cellElement)` is safe because of `scanner`
   3711            // is stack only class and keeps grabbing it until it's destroyed.
   3712            nsresult rv = SetAttributeWithTransaction(
   3713                MOZ_KnownLive(cellElement), *nsGkAtoms::bgcolor, aColor);
   3714            if (NS_WARN_IF(Destroyed())) {
   3715              return NS_ERROR_EDITOR_DESTROYED;
   3716            }
   3717            if (NS_FAILED(rv)) {
   3718              NS_WARNING(
   3719                  "EditorBase::::SetAttributeWithTransaction(nsGkAtoms::"
   3720                  "bgcolor) failed");
   3721              return rv;
   3722            }
   3723          }
   3724          return NS_OK;
   3725        }
   3726        for (const OwningNonNull<Element>& cellElement :
   3727             scanner.ElementsRef()) {
   3728          // `MOZ_KnownLive(cellElement)` is safe because of `scanner`
   3729          // is stack only class and keeps grabbing it until it's destroyed.
   3730          nsresult rv = RemoveAttributeWithTransaction(
   3731              MOZ_KnownLive(cellElement), *nsGkAtoms::bgcolor);
   3732          if (NS_FAILED(rv)) {
   3733            NS_WARNING(
   3734                "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::bgcolor)"
   3735                " failed");
   3736            return rv;
   3737          }
   3738        }
   3739        return NS_OK;
   3740      }
   3741    }
   3742    // If we failed to find a cell, fall through to use originally-found element
   3743  } else {
   3744    // No table element -- set the background color on the body tag
   3745    rootElementOfBackgroundColor = GetRoot();
   3746    if (NS_WARN_IF(!rootElementOfBackgroundColor)) {
   3747      return NS_ERROR_FAILURE;
   3748    }
   3749  }
   3750  // Use the editor method that goes through the transaction system
   3751  if (setColor) {
   3752    nsresult rv = SetAttributeWithTransaction(*rootElementOfBackgroundColor,
   3753                                              *nsGkAtoms::bgcolor, aColor);
   3754    NS_WARNING_ASSERTION(
   3755        NS_SUCCEEDED(rv),
   3756        "EditorBase::SetAttributeWithTransaction(nsGkAtoms::bgcolor) failed");
   3757    return rv;
   3758  }
   3759  nsresult rv = RemoveAttributeWithTransaction(*rootElementOfBackgroundColor,
   3760                                               *nsGkAtoms::bgcolor);
   3761  NS_WARNING_ASSERTION(
   3762      NS_SUCCEEDED(rv),
   3763      "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::bgcolor) failed");
   3764  return rv;
   3765 }
   3766 
   3767 Result<CaretPoint, nsresult>
   3768 HTMLEditor::DeleteEmptyInclusiveAncestorInlineElements(
   3769    nsIContent& aContent, const Element& aEditingHost) {
   3770  MOZ_ASSERT(IsEditActionDataAvailable());
   3771  MOZ_ASSERT(HTMLEditUtils::IsRemovableFromParentNode(aContent));
   3772 
   3773  constexpr static HTMLEditUtils::EmptyCheckOptions kOptionsToCheckInline =
   3774      HTMLEditUtils::EmptyCheckOptions{
   3775          EmptyCheckOption::TreatBlockAsVisible,
   3776          EmptyCheckOption::TreatListItemAsVisible,
   3777          EmptyCheckOption::TreatSingleBRElementAsVisible,
   3778          EmptyCheckOption::TreatTableCellAsVisible};
   3779 
   3780  if (&aContent == &aEditingHost ||
   3781      HTMLEditUtils::IsBlockElement(
   3782          aContent, BlockInlineCheck::UseComputedDisplayOutsideStyle) ||
   3783      !HTMLEditUtils::IsRemovableFromParentNode(aContent) ||
   3784      !aContent.GetParent() ||
   3785      !HTMLEditUtils::IsEmptyNode(aContent, kOptionsToCheckInline)) {
   3786    return CaretPoint(EditorDOMPoint());
   3787  }
   3788 
   3789  OwningNonNull<nsIContent> content = aContent;
   3790  for (nsIContent* parentContent : aContent.AncestorsOfType<nsIContent>()) {
   3791    if (HTMLEditUtils::IsBlockElement(
   3792            *parentContent, BlockInlineCheck::UseComputedDisplayStyle) ||
   3793        !HTMLEditUtils::IsRemovableFromParentNode(*parentContent) ||
   3794        parentContent == &aEditingHost) {
   3795      break;
   3796    }
   3797    bool parentIsEmpty = true;
   3798    if (parentContent->GetChildCount() > 1) {
   3799      for (nsIContent* sibling = parentContent->GetFirstChild(); sibling;
   3800           sibling = sibling->GetNextSibling()) {
   3801        if (sibling == content) {
   3802          continue;
   3803        }
   3804        if (!HTMLEditUtils::IsEmptyNode(*sibling, kOptionsToCheckInline)) {
   3805          parentIsEmpty = false;
   3806          break;
   3807        }
   3808      }
   3809    }
   3810    if (!parentIsEmpty) {
   3811      break;
   3812    }
   3813    content = *parentContent;
   3814  }
   3815 
   3816  const nsCOMPtr<nsIContent> nextSibling = content->GetNextSibling();
   3817  const nsCOMPtr<nsINode> parentNode = content->GetParentNode();
   3818  MOZ_ASSERT(parentNode);
   3819  nsresult rv = DeleteNodeWithTransaction(content);
   3820  if (NS_FAILED(rv)) {
   3821    NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   3822    return Err(rv);
   3823  }
   3824  if (NS_WARN_IF(nextSibling && nextSibling->GetParentNode() != parentNode) ||
   3825      NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*parentNode))) {
   3826    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   3827  }
   3828  // Note that even if nextSibling is not editable, we can put caret before it
   3829  // unless parentNode is not editable.
   3830  return CaretPoint(nextSibling ? EditorDOMPoint(nextSibling)
   3831                                : EditorDOMPoint::AtEndOf(*parentNode));
   3832 }
   3833 
   3834 nsresult HTMLEditor::DeleteAllChildrenWithTransaction(Element& aElement) {
   3835  MOZ_ASSERT(IsEditActionDataAvailable());
   3836 
   3837  // Prevent rules testing until we're done
   3838  IgnoredErrorResult ignoredError;
   3839  AutoEditSubActionNotifier startToHandleEditSubAction(
   3840      *this, EditSubAction::eDeleteNode, nsIEditor::eNext, ignoredError);
   3841  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   3842    return ignoredError.StealNSResult();
   3843  }
   3844  NS_WARNING_ASSERTION(
   3845      !ignoredError.Failed(),
   3846      "OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   3847 
   3848  while (nsCOMPtr<nsIContent> child = aElement.GetLastChild()) {
   3849    nsresult rv = DeleteNodeWithTransaction(*child);
   3850    if (NS_FAILED(rv)) {
   3851      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   3852      return rv;
   3853    }
   3854  }
   3855  return NS_OK;
   3856 }
   3857 
   3858 NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode, bool aPreserveSelection,
   3859                                     uint8_t aOptionalArgCount) {
   3860  if (NS_WARN_IF(!aNode) || NS_WARN_IF(!aNode->IsContent())) {
   3861    return NS_ERROR_INVALID_ARG;
   3862  }
   3863 
   3864  AutoEditActionDataSetter editActionData(*this, EditAction::eRemoveNode);
   3865  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   3866  if (NS_FAILED(rv)) {
   3867    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   3868                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   3869    return EditorBase::ToGenericNSResult(rv);
   3870  }
   3871 
   3872  // Make dispatch `input` event after stopping preserving selection.
   3873  AutoPlaceholderBatch treatAsOneTransaction(
   3874      *this,
   3875      ScrollSelectionIntoView::No,  // not a user interaction
   3876      __FUNCTION__);
   3877 
   3878  Maybe<AutoTransactionsConserveSelection> preserveSelection;
   3879  if (aOptionalArgCount && aPreserveSelection) {
   3880    preserveSelection.emplace(*this);
   3881  }
   3882 
   3883  rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode->AsContent()));
   3884  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3885                       "EditorBase::DeleteNodeWithTransaction() failed");
   3886  return rv;
   3887 }
   3888 
   3889 Result<CaretPoint, nsresult> HTMLEditor::DeleteTextWithTransaction(
   3890    Text& aTextNode, uint32_t aOffset, uint32_t aLength) {
   3891  if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aTextNode))) {
   3892    return Err(NS_ERROR_FAILURE);
   3893  }
   3894 
   3895  Result<CaretPoint, nsresult> caretPointOrError =
   3896      EditorBase::DeleteTextWithTransaction(aTextNode, aOffset, aLength);
   3897  NS_WARNING_ASSERTION(caretPointOrError.isOk(),
   3898                       "EditorBase::DeleteTextWithTransaction() failed");
   3899  return caretPointOrError;
   3900 }
   3901 
   3902 Result<InsertTextResult, nsresult> HTMLEditor::ReplaceTextWithTransaction(
   3903    dom::Text& aTextNode, const ReplaceWhiteSpacesData& aData) {
   3904  Result<InsertTextResult, nsresult> insertTextResultOrError =
   3905      ReplaceTextWithTransaction(aTextNode, aData.mReplaceStartOffset,
   3906                                 aData.ReplaceLength(),
   3907                                 aData.mNormalizedString);
   3908  if (MOZ_UNLIKELY(insertTextResultOrError.isErr()) ||
   3909      aData.mNewOffsetAfterReplace > aTextNode.TextDataLength()) {
   3910    return insertTextResultOrError;
   3911  }
   3912  InsertTextResult insertTextResult = insertTextResultOrError.unwrap();
   3913  insertTextResult.IgnoreCaretPointSuggestion();
   3914  EditorDOMPoint pointToPutCaret(&aTextNode, aData.mNewOffsetAfterReplace);
   3915  return InsertTextResult(std::move(insertTextResult),
   3916                          std::move(pointToPutCaret));
   3917 }
   3918 
   3919 Result<InsertTextResult, nsresult> HTMLEditor::ReplaceTextWithTransaction(
   3920    Text& aTextNode, uint32_t aOffset, uint32_t aLength,
   3921    const nsAString& aStringToInsert) {
   3922  MOZ_ASSERT(IsEditActionDataAvailable());
   3923  MOZ_ASSERT(aLength > 0 || !aStringToInsert.IsEmpty());
   3924 
   3925  if (aStringToInsert.IsEmpty()) {
   3926    Result<CaretPoint, nsresult> caretPointOrError =
   3927        DeleteTextWithTransaction(aTextNode, aOffset, aLength);
   3928    if (MOZ_UNLIKELY(caretPointOrError.isErr())) {
   3929      NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
   3930      return caretPointOrError.propagateErr();
   3931    }
   3932    return InsertTextResult(EditorDOMPoint(&aTextNode, aOffset),
   3933                            caretPointOrError.unwrap());
   3934  }
   3935 
   3936  if (!aLength) {
   3937    Result<InsertTextResult, nsresult> insertTextResult =
   3938        InsertTextWithTransaction(aStringToInsert,
   3939                                  EditorDOMPoint(&aTextNode, aOffset),
   3940                                  InsertTextTo::ExistingTextNodeIfAvailable);
   3941    NS_WARNING_ASSERTION(insertTextResult.isOk(),
   3942                         "HTMLEditor::InsertTextWithTransaction() failed");
   3943    return insertTextResult;
   3944  }
   3945 
   3946  if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aTextNode))) {
   3947    return Err(NS_ERROR_FAILURE);
   3948  }
   3949 
   3950  // This should emulates inserting text for better undo/redo behavior.
   3951  IgnoredErrorResult ignoredError;
   3952  AutoEditSubActionNotifier startToHandleEditSubAction(
   3953      *this, EditSubAction::eInsertText, nsIEditor::eNext, ignoredError);
   3954  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   3955    return Err(NS_ERROR_EDITOR_DESTROYED);
   3956  }
   3957  NS_WARNING_ASSERTION(
   3958      !ignoredError.Failed(),
   3959      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   3960 
   3961  // FYI: Create the insertion point before changing the DOM tree because
   3962  //      the point may become invalid offset after that.
   3963  EditorDOMPointInText pointToInsert(&aTextNode, aOffset);
   3964 
   3965  RefPtr<ReplaceTextTransaction> transaction = ReplaceTextTransaction::Create(
   3966      *this, aStringToInsert, aTextNode, aOffset, aLength);
   3967  MOZ_ASSERT(transaction);
   3968 
   3969  if (aLength && !mActionListeners.IsEmpty()) {
   3970    for (auto& listener : mActionListeners.Clone()) {
   3971      DebugOnly<nsresult> rvIgnored =
   3972          listener->WillDeleteText(&aTextNode, aOffset, aLength);
   3973      NS_WARNING_ASSERTION(
   3974          NS_SUCCEEDED(rvIgnored),
   3975          "nsIEditActionListener::WillDeleteText() failed, but ignored");
   3976    }
   3977  }
   3978 
   3979  nsresult rv = DoTransactionInternal(transaction);
   3980  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   3981                       "EditorBase::DoTransactionInternal() failed");
   3982 
   3983  // Don't check whether we've been destroyed here because we need to notify
   3984  // listeners and observers below even if we've already destroyed.
   3985 
   3986  EditorDOMPoint endOfInsertedText(&aTextNode,
   3987                                   aOffset + aStringToInsert.Length());
   3988 
   3989  if (pointToInsert.IsSet()) {
   3990    auto [begin, end] = ComputeInsertedRange(pointToInsert, aStringToInsert);
   3991    if (begin.IsSet() && end.IsSet()) {
   3992      TopLevelEditSubActionDataRef().DidDeleteText(
   3993          *this, begin.RefOrTo<EditorRawDOMPoint>());
   3994      TopLevelEditSubActionDataRef().DidInsertText(
   3995          *this, begin.RefOrTo<EditorRawDOMPoint>(),
   3996          end.RefOrTo<EditorRawDOMPoint>());
   3997    }
   3998 
   3999    // XXX Should we update endOfInsertedText here?
   4000  }
   4001 
   4002  if (!mActionListeners.IsEmpty()) {
   4003    for (auto& listener : mActionListeners.Clone()) {
   4004      DebugOnly<nsresult> rvIgnored =
   4005          listener->DidInsertText(&aTextNode, aOffset, aStringToInsert, rv);
   4006      NS_WARNING_ASSERTION(
   4007          NS_SUCCEEDED(rvIgnored),
   4008          "nsIEditActionListener::DidInsertText() failed, but ignored");
   4009    }
   4010  }
   4011 
   4012  if (NS_WARN_IF(Destroyed())) {
   4013    return Err(NS_ERROR_EDITOR_DESTROYED);
   4014  }
   4015 
   4016  return InsertTextResult(
   4017      std::move(endOfInsertedText),
   4018      transaction->SuggestPointToPutCaret<EditorDOMPoint>());
   4019 }
   4020 
   4021 Result<InsertTextResult, nsresult>
   4022 HTMLEditor::InsertOrReplaceTextWithTransaction(
   4023    const EditorDOMPoint& aPointToInsert,
   4024    const NormalizedStringToInsertText& aData) {
   4025  MOZ_ASSERT(aPointToInsert.IsInContentNodeAndValid());
   4026  MOZ_ASSERT_IF(aData.ReplaceLength(), aPointToInsert.IsInTextNode());
   4027 
   4028  Result<InsertTextResult, nsresult> insertTextResultOrError =
   4029      !aData.ReplaceLength()
   4030          ? InsertTextWithTransaction(aData.mNormalizedString, aPointToInsert,
   4031                                      InsertTextTo::SpecifiedPoint)
   4032          : ReplaceTextWithTransaction(
   4033                MOZ_KnownLive(*aPointToInsert.ContainerAs<Text>()),
   4034                aData.mReplaceStartOffset, aData.ReplaceLength(),
   4035                aData.mNormalizedString);
   4036  if (MOZ_UNLIKELY(insertTextResultOrError.isErr())) {
   4037    NS_WARNING(!aData.ReplaceLength()
   4038                   ? "HTMLEditor::InsertTextWithTransaction() failed"
   4039                   : "HTMLEditor::ReplaceTextWithTransaction() failed");
   4040    return insertTextResultOrError;
   4041  }
   4042  InsertTextResult insertTextResult = insertTextResultOrError.unwrap();
   4043  if (!aData.ReplaceLength()) {
   4044    auto pointToPutCaret = [&]() -> EditorDOMPoint {
   4045      return insertTextResult.HasCaretPointSuggestion()
   4046                 ? insertTextResult.UnwrapCaretPoint()
   4047                 : insertTextResult.EndOfInsertedTextRef();
   4048    }();
   4049    return InsertTextResult(std::move(insertTextResult),
   4050                            std::move(pointToPutCaret));
   4051  }
   4052  insertTextResult.IgnoreCaretPointSuggestion();
   4053  Text* const insertedTextNode =
   4054      insertTextResult.EndOfInsertedTextRef().GetContainerAs<Text>();
   4055  if (NS_WARN_IF(!insertedTextNode) ||
   4056      NS_WARN_IF(!insertedTextNode->IsInComposedDoc()) ||
   4057      NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*insertedTextNode))) {
   4058    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   4059  }
   4060  const uint32_t expectedEndOffset = aData.EndOffsetOfInsertedText();
   4061  if (NS_WARN_IF(expectedEndOffset > insertedTextNode->TextDataLength())) {
   4062    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   4063  }
   4064  // We need to return end point of the insertion string instead of end of
   4065  // replaced following white-spaces.
   4066  EditorDOMPoint endOfNewString(insertedTextNode, expectedEndOffset);
   4067  EditorDOMPoint pointToPutCaret = endOfNewString;
   4068  return InsertTextResult(std::move(endOfNewString),
   4069                          CaretPoint(std::move(pointToPutCaret)));
   4070 }
   4071 
   4072 Result<InsertTextResult, nsresult> HTMLEditor::InsertTextWithTransaction(
   4073    const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert,
   4074    InsertTextTo aInsertTextTo) {
   4075  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
   4076    return Err(NS_ERROR_INVALID_ARG);
   4077  }
   4078 
   4079  // Do nothing if the node is read-only
   4080  if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(
   4081          *aPointToInsert.GetContainer()))) {
   4082    return Err(NS_ERROR_FAILURE);
   4083  }
   4084 
   4085  return EditorBase::InsertTextWithTransaction(aStringToInsert, aPointToInsert,
   4086                                               aInsertTextTo);
   4087 }
   4088 
   4089 Result<EditorDOMPoint, nsresult> HTMLEditor::PrepareToInsertLineBreak(
   4090    LineBreakType aLineBreakType, const EditorDOMPoint& aPointToInsert) {
   4091  MOZ_ASSERT(IsEditActionDataAvailable());
   4092 
   4093  if (NS_WARN_IF(!aPointToInsert.IsInContentNode())) {
   4094    return Err(NS_ERROR_FAILURE);
   4095  }
   4096 
   4097  const auto CanInsertLineBreak = [aLineBreakType](const nsIContent& aContent) {
   4098    if (aLineBreakType == LineBreakType::BRElement) {
   4099      return HTMLEditUtils::CanNodeContain(aContent, *nsGkAtoms::br);
   4100    }
   4101    MOZ_ASSERT(aLineBreakType == LineBreakType::Linefeed);
   4102    return HTMLEditUtils::CanNodeContain(aContent, *nsGkAtoms::textTagName) &&
   4103           EditorUtils::IsNewLinePreformatted(aContent);
   4104  };
   4105 
   4106  // If we're being initialized, we cannot normalize white-spaces because the
   4107  // normalizer may remove invisible `Text`, but it's not allowed during the
   4108  // initialization.
   4109  // FIXME: Anyway, we should not do this at initialization. This is required
   4110  // only for making users can put caret into empty table cells and list items.
   4111  const bool canNormalizeWhiteSpaces = mInitSucceeded;
   4112 
   4113  if (!aPointToInsert.IsInTextNode()) {
   4114    if (NS_WARN_IF(
   4115            !CanInsertLineBreak(*aPointToInsert.ContainerAs<nsIContent>()))) {
   4116      return Err(NS_ERROR_FAILURE);
   4117    }
   4118    if (!canNormalizeWhiteSpaces) {
   4119      return aPointToInsert;
   4120    }
   4121    Result<EditorDOMPoint, nsresult> pointToInsertOrError =
   4122        WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
   4123            *this, aPointToInsert,
   4124            {WhiteSpaceVisibilityKeeper::NormalizeOption::
   4125                 StopIfPrecedingWhiteSpacesEndsWithNBP});
   4126    if (NS_WARN_IF(pointToInsertOrError.isErr())) {
   4127      return pointToInsertOrError.propagateErr();
   4128    }
   4129    return pointToInsertOrError.unwrap();
   4130  }
   4131 
   4132  // If the text node is not in an element node, we cannot insert a line break
   4133  // around the text node.
   4134  const Element* const containerOrNewLineBreak =
   4135      aPointToInsert.GetContainerParentAs<Element>();
   4136  if (NS_WARN_IF(!containerOrNewLineBreak) ||
   4137      NS_WARN_IF(!CanInsertLineBreak(*containerOrNewLineBreak))) {
   4138    return Err(NS_ERROR_FAILURE);
   4139  }
   4140 
   4141  Result<EditorDOMPoint, nsresult> pointToInsertOrError =
   4142      canNormalizeWhiteSpaces
   4143          ? WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
   4144                *this, aPointToInsert,
   4145                {WhiteSpaceVisibilityKeeper::NormalizeOption::
   4146                     StopIfPrecedingWhiteSpacesEndsWithNBP})
   4147          : aPointToInsert;
   4148  if (NS_WARN_IF(pointToInsertOrError.isErr())) {
   4149    return pointToInsertOrError.propagateErr();
   4150  }
   4151  const EditorDOMPoint pointToInsert = pointToInsertOrError.unwrap();
   4152  if (!pointToInsert.IsInTextNode()) {
   4153    return pointToInsert.ParentPoint();
   4154  }
   4155 
   4156  if (pointToInsert.IsStartOfContainer()) {
   4157    // Insert before the text node.
   4158    return pointToInsert.ParentPoint();
   4159  }
   4160 
   4161  if (pointToInsert.IsEndOfContainer()) {
   4162    // Insert after the text node.
   4163    return EditorDOMPoint::After(*pointToInsert.ContainerAs<Text>());
   4164  }
   4165 
   4166  MOZ_DIAGNOSTIC_ASSERT(pointToInsert.IsSetAndValid());
   4167 
   4168  // Unfortunately, we need to split the text node at the offset.
   4169  Result<SplitNodeResult, nsresult> splitTextNodeResult =
   4170      SplitNodeWithTransaction(pointToInsert);
   4171  if (MOZ_UNLIKELY(splitTextNodeResult.isErr())) {
   4172    NS_WARNING("HTMLEditor::SplitNodeWithTransaction() failed");
   4173    return splitTextNodeResult.propagateErr();
   4174  }
   4175 
   4176  // TODO: Stop updating `Selection`.
   4177  nsresult rv = splitTextNodeResult.inspect().SuggestCaretPointTo(
   4178      *this, {SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
   4179  if (NS_FAILED(rv)) {
   4180    NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
   4181    return Err(rv);
   4182  }
   4183 
   4184  // Insert new line break before the right node.
   4185  auto atNextContent =
   4186      splitTextNodeResult.inspect().AtNextContent<EditorDOMPoint>();
   4187  if (MOZ_UNLIKELY(!atNextContent.IsInContentNode())) {
   4188    NS_WARNING("The next node seems not in the DOM tree");
   4189    return Err(NS_ERROR_FAILURE);
   4190  }
   4191  return atNextContent;
   4192 }
   4193 
   4194 Maybe<HTMLEditor::LineBreakType> HTMLEditor::GetPreferredLineBreakType(
   4195    const nsINode& aNode, const Element& aEditingHost) const {
   4196  const Element* const container = aNode.GetAsElementOrParentElement();
   4197  if (MOZ_UNLIKELY(!container)) {
   4198    return Nothing();
   4199  }
   4200  // For backward compatibility, we should not insert a linefeed if
   4201  // paragraph separator is set to "br" which is Gecko-specific mode.
   4202  if (GetDefaultParagraphSeparator() == ParagraphSeparator::br) {
   4203    return Some(LineBreakType::BRElement);
   4204  }
   4205  // And also if we're the mail composer, the content needs to be serialized.
   4206  // Therefore, we should always use <br> for the serializer.
   4207  if (IsMailEditor() || IsPlaintextMailComposer()) {
   4208    return Some(LineBreakType::BRElement);
   4209  }
   4210  if (HTMLEditUtils::ShouldInsertLinefeedCharacter(EditorDOMPoint(container, 0),
   4211                                                   aEditingHost) &&
   4212      HTMLEditUtils::CanNodeContain(*container, *nsGkAtoms::textTagName)) {
   4213    return Some(LineBreakType::Linefeed);
   4214  }
   4215  if (MOZ_UNLIKELY(
   4216          !HTMLEditUtils::CanNodeContain(*container, *nsGkAtoms::br))) {
   4217    return Nothing();
   4218  }
   4219  return Some(LineBreakType::BRElement);
   4220 }
   4221 
   4222 Result<CreateLineBreakResult, nsresult> HTMLEditor::InsertLineBreak(
   4223    WithTransaction aWithTransaction, LineBreakType aLineBreakType,
   4224    const EditorDOMPoint& aPointToInsert, EDirection aSelect /* = eNone */) {
   4225  MOZ_ASSERT(IsEditActionDataAvailable());
   4226 
   4227  Result<EditorDOMPoint, nsresult> pointToInsertOrError =
   4228      PrepareToInsertLineBreak(aLineBreakType, aPointToInsert);
   4229  if (MOZ_UNLIKELY(pointToInsertOrError.isErr())) {
   4230    NS_WARNING(
   4231        nsPrintfCString("HTMLEditor::PrepareToInsertLineBreak(%s) failed",
   4232                        ToString(aWithTransaction).c_str())
   4233            .get());
   4234    return pointToInsertOrError.propagateErr();
   4235  }
   4236  EditorDOMPoint pointToInsert = pointToInsertOrError.unwrap();
   4237  MOZ_ASSERT(pointToInsert.IsInContentNode());
   4238  MOZ_ASSERT(pointToInsert.IsSetAndValid());
   4239 
   4240  auto lineBreakOrError = [&]() MOZ_NEVER_INLINE_DEBUG MOZ_CAN_RUN_SCRIPT
   4241      -> Result<EditorLineBreak, nsresult> {
   4242    if (aLineBreakType == LineBreakType::BRElement) {
   4243      Result<CreateElementResult, nsresult> insertBRElementResultOrError =
   4244          InsertBRElement(aWithTransaction, BRElementType::Normal,
   4245                          pointToInsert);
   4246      if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   4247        NS_WARNING(
   4248            nsPrintfCString(
   4249                "EditorBase::InsertBRElement(%s, BRElementType::Normal) failed",
   4250                ToString(aWithTransaction).c_str())
   4251                .get());
   4252        return insertBRElementResultOrError.propagateErr();
   4253      }
   4254      CreateElementResult insertBRElementResult =
   4255          insertBRElementResultOrError.unwrap();
   4256      MOZ_ASSERT(insertBRElementResult.Handled());
   4257      insertBRElementResult.IgnoreCaretPointSuggestion();
   4258      return EditorLineBreak(insertBRElementResult.UnwrapNewNode());
   4259    }
   4260    MOZ_ASSERT(aLineBreakType == LineBreakType::Linefeed);
   4261    RefPtr<Text> newTextNode = CreateTextNode(u"\n"_ns);
   4262    if (NS_WARN_IF(!newTextNode)) {
   4263      return Err(NS_ERROR_FAILURE);
   4264    }
   4265    if (aWithTransaction == WithTransaction::Yes) {
   4266      Result<CreateTextResult, nsresult> insertTextNodeResult =
   4267          InsertNodeWithTransaction<Text>(*newTextNode, pointToInsert);
   4268      if (MOZ_UNLIKELY(insertTextNodeResult.isErr())) {
   4269        NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   4270        return insertTextNodeResult.propagateErr();
   4271      }
   4272      insertTextNodeResult.unwrap().IgnoreCaretPointSuggestion();
   4273    } else {
   4274      (void)pointToInsert.Offset();
   4275      RefPtr<InsertNodeTransaction> transaction =
   4276          InsertNodeTransaction::Create(*this, *newTextNode, pointToInsert);
   4277      nsresult rv = transaction->DoTransaction();
   4278      if (NS_WARN_IF(Destroyed())) {
   4279        return Err(NS_ERROR_EDITOR_DESTROYED);
   4280      }
   4281      if (NS_FAILED(rv)) {
   4282        NS_WARNING("InsertNodeTransaction::DoTransaction() failed");
   4283        return Err(rv);
   4284      }
   4285      if (NS_WARN_IF(newTextNode->GetParentNode() !=
   4286                     pointToInsert.GetContainer())) {
   4287        return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   4288      }
   4289      RangeUpdaterRef().SelAdjInsertNode(EditorRawDOMPoint(
   4290          pointToInsert.GetContainer(), pointToInsert.Offset()));
   4291    }
   4292    if (NS_WARN_IF(!newTextNode->TextDataLength() ||
   4293                   newTextNode->DataBuffer().CharAt(0) != '\n')) {
   4294      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   4295    }
   4296    return EditorLineBreak(std::move(newTextNode), 0u);
   4297  }();
   4298  if (MOZ_UNLIKELY(lineBreakOrError.isErr())) {
   4299    return lineBreakOrError.propagateErr();
   4300  }
   4301  EditorLineBreak lineBreak = lineBreakOrError.unwrap();
   4302  auto pointToPutCaret = [&]() -> EditorDOMPoint {
   4303    switch (aSelect) {
   4304      case eNext: {
   4305        return lineBreak.After<EditorDOMPoint>();
   4306      }
   4307      case ePrevious: {
   4308        return lineBreak.Before<EditorDOMPoint>();
   4309      }
   4310      default:
   4311        NS_WARNING(
   4312            "aSelect has invalid value, the caller need to set selection "
   4313            "by itself");
   4314        [[fallthrough]];
   4315      case eNone:
   4316        return lineBreak.To<EditorDOMPoint>();
   4317    }
   4318  }();
   4319  return CreateLineBreakResult(std::move(lineBreak),
   4320                               std::move(pointToPutCaret));
   4321 }
   4322 
   4323 nsresult HTMLEditor::EnsureNoFollowingUnnecessaryLineBreak(
   4324    const EditorDOMPoint& aNextOrAfterModifiedPoint) {
   4325  MOZ_ASSERT(aNextOrAfterModifiedPoint.IsInContentNode());
   4326 
   4327  // If the point is in a mailcite in plaintext mail composer (it is a <span>
   4328  // styled as block), we should not treat its padding <br> as unnecessary
   4329  // because it's required by the serializer to give next content of the
   4330  // mailcite has its own line.
   4331  if (IsPlaintextMailComposer()) {
   4332    const Element* const blockElement =
   4333        HTMLEditUtils::GetInclusiveAncestorElement(
   4334            *aNextOrAfterModifiedPoint.ContainerAs<nsIContent>(),
   4335            HTMLEditUtils::ClosestEditableBlockElement,
   4336            BlockInlineCheck::UseComputedDisplayStyle);
   4337    if (blockElement && HTMLEditUtils::IsMailCiteElement(*blockElement) &&
   4338        HTMLEditUtils::IsInlineContent(*blockElement,
   4339                                       BlockInlineCheck::UseHTMLDefaultStyle)) {
   4340      return NS_OK;
   4341    }
   4342  }
   4343 
   4344  const bool isWhiteSpacePreformatted = EditorUtils::IsWhiteSpacePreformatted(
   4345      *aNextOrAfterModifiedPoint.ContainerAs<nsIContent>());
   4346  const DebugOnly<bool> isNewLinePreformatted =
   4347      EditorUtils::IsNewLinePreformatted(
   4348          *aNextOrAfterModifiedPoint.ContainerAs<nsIContent>());
   4349 
   4350  const Maybe<EditorLineBreak> unnecessaryLineBreak =
   4351      HTMLEditUtils::GetFollowingUnnecessaryLineBreak<EditorLineBreak>(
   4352          aNextOrAfterModifiedPoint);
   4353  if (MOZ_LIKELY(unnecessaryLineBreak.isNothing() ||
   4354                 !unnecessaryLineBreak->IsDeletableFromComposedDoc())) {
   4355    return NS_OK;
   4356  }
   4357  if (unnecessaryLineBreak->IsHTMLBRElement()) {
   4358    // If the found unnecessary <br> is a preceding one of a mailcite which is a
   4359    // <span> styled as block, we need to preserve the <br> element for the
   4360    // serializer to cause a line break before the mailcite.
   4361    if (IsPlaintextMailComposer()) {
   4362      const WSScanResult nextThing =
   4363          WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   4364              {}, unnecessaryLineBreak->After<EditorRawDOMPoint>());
   4365      if (nextThing.ReachedOtherBlockElement() &&
   4366          HTMLEditUtils::IsMailCiteElement(*nextThing.ElementPtr()) &&
   4367          HTMLEditUtils::IsInlineContent(
   4368              *nextThing.ElementPtr(), BlockInlineCheck::UseHTMLDefaultStyle)) {
   4369        return NS_OK;
   4370      }
   4371    }
   4372    // If the invisible break is a placeholder of ancestor inline elements, we
   4373    // should not delete it to allow users to insert text with the format
   4374    // specified by them.
   4375    if (HTMLEditUtils::GetMostDistantAncestorEditableEmptyInlineElement(
   4376            unnecessaryLineBreak->BRElementRef(),
   4377            BlockInlineCheck::UseComputedDisplayStyle)) {
   4378      return NS_OK;
   4379    }
   4380    nsresult rv = DeleteNodeWithTransaction(
   4381        MOZ_KnownLive(unnecessaryLineBreak->BRElementRef()));
   4382    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4383                         "EditorBase::DeleteNodeWithTransaction() failed "
   4384                         "to delete unnecessary <br>");
   4385    return rv;
   4386  }
   4387  MOZ_ASSERT(isNewLinePreformatted);
   4388  const auto IsVisibleChar = [&](char16_t aChar) {
   4389    switch (aChar) {
   4390      case HTMLEditUtils::kNewLine:
   4391        return true;
   4392      case HTMLEditUtils::kSpace:
   4393      case HTMLEditUtils::kTab:
   4394      case HTMLEditUtils::kCarriageReturn:
   4395        return isWhiteSpacePreformatted;
   4396      default:
   4397        return true;
   4398    }
   4399  };
   4400  const CharacterDataBuffer& characterDataBuffer =
   4401      unnecessaryLineBreak->TextRef().DataBuffer();
   4402  const uint32_t length = characterDataBuffer.GetLength();
   4403  const DebugOnly<const char16_t> lastChar =
   4404      characterDataBuffer.CharAt(length - 1);
   4405  MOZ_ASSERT(lastChar == HTMLEditUtils::kNewLine);
   4406  const bool textNodeHasVisibleChar = [&]() {
   4407    if (length == 1u) {
   4408      return false;
   4409    }
   4410    for (const uint32_t offset : Reversed(IntegerRange(length - 1))) {
   4411      if (IsVisibleChar(characterDataBuffer.CharAt(offset))) {
   4412        return true;
   4413      }
   4414    }
   4415    return false;
   4416  }();
   4417  if (!textNodeHasVisibleChar) {
   4418    // If the invisible break is a placeholder of ancestor inline elements, we
   4419    // should not delete it to allow users to insert text with the format
   4420    // specified by them.
   4421    if (HTMLEditUtils::GetMostDistantAncestorEditableEmptyInlineElement(
   4422            unnecessaryLineBreak->TextRef(),
   4423            BlockInlineCheck::UseComputedDisplayStyle)) {
   4424      return NS_OK;
   4425    }
   4426    nsresult rv = DeleteNodeWithTransaction(
   4427        MOZ_KnownLive(unnecessaryLineBreak->TextRef()));
   4428    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4429                         "EditorBase::DeleteNodeWithTransaction() failed "
   4430                         "to delete unnecessary Text node");
   4431    return rv;
   4432  }
   4433  Result<CaretPoint, nsresult> result =
   4434      DeleteTextWithTransaction(MOZ_KnownLive(unnecessaryLineBreak->TextRef()),
   4435                                unnecessaryLineBreak->Offset(), 1);
   4436  if (MOZ_UNLIKELY(result.isErr())) {
   4437    NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
   4438    return result.unwrapErr();
   4439  }
   4440  result.unwrap().IgnoreCaretPointSuggestion();
   4441  return NS_OK;
   4442 }
   4443 
   4444 Result<CreateElementResult, nsresult>
   4445 HTMLEditor::InsertContainerWithTransaction(
   4446    nsIContent& aContentToBeWrapped, const nsAtom& aWrapperTagName,
   4447    const InitializeInsertingElement& aInitializer) {
   4448  EditorDOMPoint pointToInsertNewContainer(&aContentToBeWrapped);
   4449  if (NS_WARN_IF(!pointToInsertNewContainer.IsSet())) {
   4450    return Err(NS_ERROR_FAILURE);
   4451  }
   4452  // aContentToBeWrapped will be moved to the new container before inserting the
   4453  // new container.  So, when we insert the container, the insertion point is
   4454  // before the next sibling of aContentToBeWrapped.
   4455  // XXX If pointerToInsertNewContainer stores offset here, the offset and
   4456  //     referring child node become mismatched.  Although, currently this
   4457  //     is not a problem since InsertNodeTransaction refers only child node.
   4458  MOZ_ALWAYS_TRUE(pointToInsertNewContainer.AdvanceOffset());
   4459 
   4460  // Create new container.
   4461  RefPtr<Element> newContainer = CreateHTMLContent(&aWrapperTagName);
   4462  if (NS_WARN_IF(!newContainer)) {
   4463    return Err(NS_ERROR_FAILURE);
   4464  }
   4465 
   4466  if (&aInitializer != &HTMLEditor::DoNothingForNewElement) {
   4467    nsresult rv = aInitializer(*this, *newContainer,
   4468                               EditorDOMPoint(&aContentToBeWrapped));
   4469    if (NS_WARN_IF(Destroyed())) {
   4470      return Err(NS_ERROR_EDITOR_DESTROYED);
   4471    }
   4472    if (NS_FAILED(rv)) {
   4473      NS_WARNING("aInitializer() failed");
   4474      return Err(rv);
   4475    }
   4476  }
   4477 
   4478  // Notify our internal selection state listener
   4479  AutoInsertContainerSelNotify selNotify(RangeUpdaterRef());
   4480 
   4481  // Put aNode in the new container, first.
   4482  // XXX Perhaps, we should not remove the container if it's not editable.
   4483  nsresult rv = DeleteNodeWithTransaction(aContentToBeWrapped);
   4484  if (NS_FAILED(rv)) {
   4485    NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   4486    return Err(rv);
   4487  }
   4488 
   4489  {
   4490    // TODO: Remove AutoTransactionsConserveSelection here.  It's not necessary
   4491    //       in normal cases.  However, it may be required for nested edit
   4492    //       actions which may be caused by legacy mutation event listeners or
   4493    //       chrome script.
   4494    AutoTransactionsConserveSelection conserveSelection(*this);
   4495    Result<CreateContentResult, nsresult> insertContentNodeResult =
   4496        InsertNodeWithTransaction(aContentToBeWrapped,
   4497                                  EditorDOMPoint(newContainer, 0u));
   4498    if (MOZ_UNLIKELY(insertContentNodeResult.isErr())) {
   4499      NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
   4500      return insertContentNodeResult.propagateErr();
   4501    }
   4502    insertContentNodeResult.inspect().IgnoreCaretPointSuggestion();
   4503  }
   4504 
   4505  // Put the new container where aNode was.
   4506  Result<CreateElementResult, nsresult> insertNewContainerElementResult =
   4507      InsertNodeWithTransaction<Element>(*newContainer,
   4508                                         pointToInsertNewContainer);
   4509  NS_WARNING_ASSERTION(insertNewContainerElementResult.isOk(),
   4510                       "EditorBase::InsertNodeWithTransaction() failed");
   4511  return insertNewContainerElementResult;
   4512 }
   4513 
   4514 Result<CreateElementResult, nsresult>
   4515 HTMLEditor::ReplaceContainerWithTransactionInternal(
   4516    Element& aOldContainer, const nsAtom& aTagName, const nsAtom& aAttribute,
   4517    const nsAString& aAttributeValue, bool aCloneAllAttributes) {
   4518  MOZ_ASSERT(IsEditActionDataAvailable());
   4519 
   4520  if (NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(aOldContainer)) ||
   4521      NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aOldContainer))) {
   4522    return Err(NS_ERROR_FAILURE);
   4523  }
   4524 
   4525  // If we're replacing <dd> or <dt> with different type of element, we need to
   4526  // split the parent <dl>.
   4527  OwningNonNull<Element> containerElementToDelete = aOldContainer;
   4528  if (aOldContainer.IsAnyOfHTMLElements(nsGkAtoms::dd, nsGkAtoms::dt) &&
   4529      &aTagName != nsGkAtoms::dt && &aTagName != nsGkAtoms::dd &&
   4530      // aOldContainer always has a parent node because of removable.
   4531      aOldContainer.GetParentNode()->IsHTMLElement(nsGkAtoms::dl)) {
   4532    OwningNonNull<Element> const dlElement = *aOldContainer.GetParentElement();
   4533    if (NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(dlElement)) ||
   4534        NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(dlElement))) {
   4535      return Err(NS_ERROR_FAILURE);
   4536    }
   4537    Result<SplitRangeOffFromNodeResult, nsresult> splitDLElementResult =
   4538        SplitRangeOffFromElement(dlElement, aOldContainer, aOldContainer);
   4539    if (MOZ_UNLIKELY(splitDLElementResult.isErr())) {
   4540      NS_WARNING("HTMLEditor::SplitRangeOffFromElement() failed");
   4541      return splitDLElementResult.propagateErr();
   4542    }
   4543    splitDLElementResult.inspect().IgnoreCaretPointSuggestion();
   4544    RefPtr<Element> middleDLElement = aOldContainer.GetParentElement();
   4545    if (NS_WARN_IF(!middleDLElement) ||
   4546        NS_WARN_IF(!middleDLElement->IsHTMLElement(nsGkAtoms::dl)) ||
   4547        NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(*middleDLElement))) {
   4548      NS_WARNING("The parent <dl> was lost at splitting it");
   4549      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   4550    }
   4551    containerElementToDelete = std::move(middleDLElement);
   4552  }
   4553 
   4554  const RefPtr<Element> newContainer = CreateHTMLContent(&aTagName);
   4555  if (NS_WARN_IF(!newContainer)) {
   4556    return Err(NS_ERROR_FAILURE);
   4557  }
   4558 
   4559  // Set or clone attribute if needed.
   4560  // FIXME: What should we do attributes of <dl> elements if we removed it
   4561  // above?
   4562  if (aCloneAllAttributes) {
   4563    MOZ_ASSERT(&aAttribute == nsGkAtoms::_empty);
   4564    CloneAttributesWithTransaction(*newContainer, aOldContainer);
   4565  } else if (&aAttribute != nsGkAtoms::_empty) {
   4566    nsresult rv = newContainer->SetAttr(kNameSpaceID_None,
   4567                                        const_cast<nsAtom*>(&aAttribute),
   4568                                        aAttributeValue, true);
   4569    if (NS_FAILED(rv)) {
   4570      NS_WARNING("Element::SetAttr() failed");
   4571      return Err(NS_ERROR_FAILURE);
   4572    }
   4573  }
   4574 
   4575  const OwningNonNull<nsINode> parentNode =
   4576      *containerElementToDelete->GetParentNode();
   4577  const nsCOMPtr<nsINode> referenceNode =
   4578      containerElementToDelete->GetNextSibling();
   4579  AutoReplaceContainerSelNotify selStateNotify(RangeUpdaterRef(), aOldContainer,
   4580                                               *newContainer);
   4581  if (aOldContainer.HasChildren()) {
   4582    // TODO: Remove AutoTransactionsConserveSelection here.  It's not necessary
   4583    //       in normal cases.  However, it may be required for nested edit
   4584    //       actions which may be caused by legacy mutation event listeners or
   4585    //       chrome script.
   4586    // Move non-editable children too because its container, aElement, is
   4587    // editable so that all children must be removable node.
   4588    const OwningNonNull<nsIContent> firstChild = *aOldContainer.GetFirstChild();
   4589    const OwningNonNull<nsIContent> lastChild = *aOldContainer.GetLastChild();
   4590    Result<MoveNodeResult, nsresult> moveChildrenResultOrError =
   4591        MoveSiblingsWithTransaction(firstChild, lastChild,
   4592                                    EditorDOMPoint(newContainer, 0));
   4593    if (MOZ_UNLIKELY(moveChildrenResultOrError.isErr())) {
   4594      NS_WARNING("HTMLEditor::MoveSiblingsWithTransaction() failed");
   4595      return moveChildrenResultOrError.propagateErr();
   4596    }
   4597    // We'll suggest new caret point which is suggested by new container
   4598    // element insertion result.  Therefore, we need to do nothing here.
   4599    moveChildrenResultOrError.inspect().IgnoreCaretPointSuggestion();
   4600  }
   4601 
   4602  // Delete containerElementToDelete from the DOM tree to make it not referred
   4603  // by InsertNodeTransaction.
   4604  nsresult rv = DeleteNodeWithTransaction(containerElementToDelete);
   4605  if (NS_FAILED(rv)) {
   4606    NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   4607    return Err(rv);
   4608  }
   4609 
   4610  if (referenceNode && (!referenceNode->GetParentNode() ||
   4611                        parentNode != referenceNode->GetParentNode())) {
   4612    NS_WARNING(
   4613        "The reference node for insertion has been moved to different parent, "
   4614        "so we got lost the insertion point");
   4615    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   4616  }
   4617 
   4618  // Finally, insert the new node to where probably aOldContainer was.
   4619  Result<CreateElementResult, nsresult> insertNewContainerElementResult =
   4620      InsertNodeWithTransaction<Element>(
   4621          *newContainer, referenceNode ? EditorDOMPoint(referenceNode)
   4622                                       : EditorDOMPoint::AtEndOf(*parentNode));
   4623  NS_WARNING_ASSERTION(insertNewContainerElementResult.isOk(),
   4624                       "EditorBase::InsertNodeWithTransaction() failed");
   4625  MOZ_ASSERT_IF(
   4626      insertNewContainerElementResult.isOk(),
   4627      insertNewContainerElementResult.inspect().GetNewNode() == newContainer);
   4628  return insertNewContainerElementResult;
   4629 }
   4630 
   4631 Result<EditorDOMPoint, nsresult> HTMLEditor::RemoveContainerWithTransaction(
   4632    Element& aElement) {
   4633  MOZ_ASSERT(IsEditActionDataAvailable());
   4634 
   4635  if (NS_WARN_IF(!HTMLEditUtils::IsRemovableNode(aElement)) ||
   4636      NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aElement))) {
   4637    return Err(NS_ERROR_FAILURE);
   4638  }
   4639 
   4640  // Notify our internal selection state listener.
   4641  AutoRemoveContainerSelNotify selNotify(RangeUpdaterRef(),
   4642                                         EditorRawDOMPoint(&aElement));
   4643  const nsCOMPtr<nsINode> parentNode = aElement.GetParentNode();
   4644  const nsCOMPtr<nsIContent> nextSibling = aElement.GetNextSibling();
   4645  EditorDOMPoint pointToPutCaret;
   4646  // FIXME: If we'd improve the range updater to be able to track moving nodes
   4647  // after removing the container first, we should do that because
   4648  // IMEContentObserver can run faster.
   4649  if (aElement.HasChildren()) {
   4650    const OwningNonNull<nsIContent> firstChild = *aElement.GetFirstChild();
   4651    const OwningNonNull<nsIContent> lastChild = *aElement.GetLastChild();
   4652    Result<MoveNodeResult, nsresult> moveChildrenResultOrError =
   4653        MoveSiblingsWithTransaction(firstChild, lastChild,
   4654                                    nextSibling
   4655                                        ? EditorDOMPoint(nextSibling)
   4656                                        : EditorDOMPoint::AtEndOf(*parentNode));
   4657    if (MOZ_UNLIKELY(moveChildrenResultOrError.isErr())) {
   4658      NS_WARNING("HTMLEditor::MoveSiblingsWithTransaction() failed");
   4659      return moveChildrenResultOrError.propagateErr();
   4660    }
   4661    pointToPutCaret = moveChildrenResultOrError.unwrap().UnwrapCaretPoint();
   4662  }
   4663  {
   4664    AutoTrackDOMPoint trackPointToPutCaret(RangeUpdaterRef(), &pointToPutCaret);
   4665    nsresult rv = DeleteNodeWithTransaction(aElement);
   4666    if (NS_FAILED(rv)) {
   4667      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   4668      return Err(rv);
   4669    }
   4670    if (NS_WARN_IF(nextSibling && nextSibling->GetParentNode() != parentNode)) {
   4671      return Err(NS_ERROR_EDITOR_DESTROYED);
   4672    }
   4673  }
   4674  if (pointToPutCaret.IsSetAndValidInComposedDoc()) {
   4675    return pointToPutCaret;
   4676  }
   4677  return nextSibling && nextSibling->GetParentNode() == parentNode
   4678             ? EditorDOMPoint(nextSibling)
   4679             : EditorDOMPoint::AtEndOf(*parentNode);
   4680 }
   4681 
   4682 nsresult HTMLEditor::SelectEntireDocument() {
   4683  MOZ_ASSERT(IsEditActionDataAvailable());
   4684 
   4685  if (!mInitSucceeded) {
   4686    return NS_ERROR_NOT_INITIALIZED;
   4687  }
   4688 
   4689  // XXX It's odd to select all of the document body if an contenteditable
   4690  //     element has focus.
   4691  RefPtr<Element> bodyOrDocumentElement = GetRoot();
   4692  if (NS_WARN_IF(!bodyOrDocumentElement)) {
   4693    return NS_ERROR_NOT_INITIALIZED;
   4694  }
   4695 
   4696  // If we're empty, don't select all children because that would select the
   4697  // padding <br> element for empty editor.
   4698  if (IsEmpty()) {
   4699    nsresult rv = CollapseSelectionToStartOf(*bodyOrDocumentElement);
   4700    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4701                         "EditorBase::CollapseSelectionToStartOf() failed");
   4702    return rv;
   4703  }
   4704 
   4705  // Otherwise, select all children.
   4706  ErrorResult error;
   4707  SelectionRef().SelectAllChildren(*bodyOrDocumentElement, error);
   4708  if (NS_WARN_IF(Destroyed())) {
   4709    error.SuppressException();
   4710    return NS_ERROR_EDITOR_DESTROYED;
   4711  }
   4712  NS_WARNING_ASSERTION(!error.Failed(),
   4713                       "Selection::SelectAllChildren() failed");
   4714  return error.StealNSResult();
   4715 }
   4716 
   4717 nsresult HTMLEditor::SelectAllInternal() {
   4718  MOZ_ASSERT(IsEditActionDataAvailable());
   4719 
   4720  CommitComposition();
   4721  const RefPtr<Document> doc = GetDocument();
   4722  if (NS_WARN_IF(!doc)) {
   4723    // The root caller should not use HTMLEditor in this case (mDocument won't
   4724    // be cleared except by the cycle collector).
   4725    return NS_ERROR_NOT_AVAILABLE;
   4726  }
   4727 
   4728  auto GetBodyElementIfElementIsParentOfHTMLBody =
   4729      [](const Element& aElement) -> Element* {
   4730    if (!aElement.OwnerDoc()->IsHTMLDocument()) {
   4731      return const_cast<Element*>(&aElement);
   4732    }
   4733    HTMLBodyElement* bodyElement = aElement.OwnerDoc()->GetBodyElement();
   4734    return bodyElement && nsContentUtils::ContentIsFlattenedTreeDescendantOf(
   4735                              bodyElement, &aElement)
   4736               ? bodyElement
   4737               : const_cast<Element*>(&aElement);
   4738  };
   4739 
   4740  const nsCOMPtr<nsIContent> selectionRootContent =
   4741      [&]() MOZ_CAN_RUN_SCRIPT -> nsIContent* {
   4742    const RefPtr<Element> elementForComputingSelectionRoot = [&]() -> Element* {
   4743      // If there is at least one selection range, we should compute the
   4744      // selection root from the anchor node.
   4745      if (SelectionRef().RangeCount()) {
   4746        if (nsIContent* content =
   4747                nsIContent::FromNodeOrNull(SelectionRef().GetAnchorNode())) {
   4748          if (content->IsElement()) {
   4749            return content->AsElement();
   4750          }
   4751          if (Element* parentElement =
   4752                  content->GetParentElementCrossingShadowRoot()) {
   4753            return parentElement;
   4754          }
   4755        }
   4756      }
   4757      // If no element contains a selection range, we should select all children
   4758      // of the focused element at least.
   4759      if (Element* focusedElement = GetFocusedElement()) {
   4760        return focusedElement;
   4761      }
   4762      // Okay, there is no selection range and no focused element, let's select
   4763      // all of the body or the document element.
   4764      if (Element* const bodyElement = GetBodyElement()) {
   4765        return bodyElement;
   4766      }
   4767      return doc->GetDocumentElement();
   4768    }();
   4769    if (MOZ_UNLIKELY(!elementForComputingSelectionRoot)) {
   4770      return nullptr;
   4771    }
   4772 
   4773    // Then, compute the selection root content to select all including
   4774    // elementToBeSelected.
   4775    RefPtr<PresShell> presShell = GetPresShell();
   4776    nsIContent* computedSelectionRootContent =
   4777        elementForComputingSelectionRoot->GetSelectionRootContent(
   4778            presShell, nsINode::IgnoreOwnIndependentSelection::Yes,
   4779            nsINode::AllowCrossShadowBoundary::No);
   4780    if (NS_WARN_IF(!computedSelectionRootContent)) {
   4781      return nullptr;
   4782    }
   4783    if (MOZ_UNLIKELY(!computedSelectionRootContent->IsElement())) {
   4784      return computedSelectionRootContent;
   4785    }
   4786    return GetBodyElementIfElementIsParentOfHTMLBody(
   4787        *computedSelectionRootContent->AsElement());
   4788  }();
   4789  if (MOZ_UNLIKELY(!selectionRootContent)) {
   4790    // If there is no element in the document, the document node can have
   4791    // selection range whose container is the document node. However, Chrome
   4792    // does not handle "Select All" command in this case (if there is no
   4793    // selection range, no new range is created and if there is a collapsed
   4794    // range, the range won't be extended to select all children of the
   4795    // Document).  Let's follow it.
   4796    return NS_OK;
   4797  }
   4798 
   4799  Maybe<Selection::AutoUserInitiated> userSelection;
   4800  // XXX Do we need to mark it as "user initiated" for
   4801  //     `Document.execCommand("selectAll")`?
   4802  if (!selectionRootContent->IsEditable()) {
   4803    userSelection.emplace(SelectionRef());
   4804  }
   4805  ErrorResult error;
   4806  SelectionRef().SelectAllChildren(*selectionRootContent, error);
   4807  NS_WARNING_ASSERTION(!error.Failed(),
   4808                       "Selection::SelectAllChildren() failed");
   4809  return error.StealNSResult();
   4810 }
   4811 
   4812 bool HTMLEditor::SetCaretInTableCell(Element* aElement) {
   4813  MOZ_ASSERT(IsEditActionDataAvailable());
   4814 
   4815  if (!aElement || !aElement->IsHTMLElement() ||
   4816      !HTMLEditUtils::IsAnyTableElementExceptColumnElement(*aElement)) {
   4817    return false;
   4818  }
   4819  const RefPtr<Element> editingHost = ComputeEditingHost();
   4820  if (!editingHost || !aElement->IsInclusiveDescendantOf(editingHost)) {
   4821    return false;
   4822  }
   4823 
   4824  nsCOMPtr<nsIContent> deepestFirstChild = aElement;
   4825  while (deepestFirstChild->HasChildren()) {
   4826    deepestFirstChild = deepestFirstChild->GetFirstChild();
   4827  }
   4828 
   4829  // Set selection at beginning of the found node
   4830  nsresult rv = CollapseSelectionToStartOf(*deepestFirstChild);
   4831  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4832                       "EditorBase::CollapseSelectionToStartOf() failed");
   4833  return NS_SUCCEEDED(rv);
   4834 }
   4835 
   4836 /**
   4837 * This method scans the selection for adjacent text nodes
   4838 * and collapses them into a single text node.
   4839 * "adjacent" means literally adjacent siblings of the same parent.
   4840 * Uses HTMLEditor::JoinNodesWithTransaction() so action is undoable.
   4841 * Should be called within the context of a batch transaction.
   4842 */
   4843 nsresult HTMLEditor::CollapseAdjacentTextNodes(nsRange& aRange) {
   4844  AutoTransactionsConserveSelection dontChangeMySelection(*this);
   4845 
   4846  // we can't actually do anything during iteration, so store the text nodes in
   4847  // an array first.
   4848  DOMSubtreeIterator subtreeIter;
   4849  if (NS_FAILED(subtreeIter.Init(aRange))) {
   4850    NS_WARNING("DOMSubtreeIterator::Init() failed");
   4851    return NS_ERROR_FAILURE;
   4852  }
   4853  AutoTArray<OwningNonNull<Text>, 8> textNodes;
   4854  subtreeIter.AppendNodesToArray(
   4855      +[](nsINode& aNode, void*) -> bool {
   4856        return EditorUtils::IsEditableContent(*aNode.AsText(),
   4857                                              EditorType::HTML);
   4858      },
   4859      textNodes);
   4860 
   4861  if (textNodes.Length() < 2) {
   4862    return NS_OK;
   4863  }
   4864 
   4865  OwningNonNull<Text> leftTextNode = textNodes[0];
   4866  for (size_t rightTextNodeIndex = 1; rightTextNodeIndex < textNodes.Length();
   4867       rightTextNodeIndex++) {
   4868    OwningNonNull<Text>& rightTextNode = textNodes[rightTextNodeIndex];
   4869    // If the leftTextNode has only preformatted line break, keep it as-is.
   4870    if (HTMLEditUtils::TextHasOnlyOnePreformattedLinefeed(leftTextNode)) {
   4871      leftTextNode = rightTextNode;
   4872      continue;
   4873    }
   4874    // If the rightTextNode has only preformatted line break, keep it as-is, and
   4875    // advance the loop next to the rightTextNode.
   4876    if (HTMLEditUtils::TextHasOnlyOnePreformattedLinefeed(rightTextNode)) {
   4877      if (++rightTextNodeIndex == textNodes.Length()) {
   4878        break;
   4879      }
   4880      leftTextNode = textNodes[rightTextNodeIndex];
   4881      continue;
   4882    }
   4883    // If the text nodes are not direct siblings, we shouldn't join them, and
   4884    // we don't need to handle the left one anymore.
   4885    if (leftTextNode->GetNextSibling() != rightTextNode) {
   4886      leftTextNode = rightTextNode;
   4887      continue;
   4888    }
   4889    Result<JoinNodesResult, nsresult> joinNodesResultOrError =
   4890        JoinTextNodesWithNormalizeWhiteSpaces(MOZ_KnownLive(leftTextNode),
   4891                                              MOZ_KnownLive(rightTextNode));
   4892    if (MOZ_UNLIKELY(joinNodesResultOrError.isErr())) {
   4893      NS_WARNING("HTMLEditor::JoinTextNodesWithNormalizeWhiteSpaces() failed");
   4894      return joinNodesResultOrError.unwrapErr();
   4895    }
   4896  }
   4897 
   4898  return NS_OK;
   4899 }
   4900 
   4901 nsresult HTMLEditor::SetSelectionAtDocumentStart() {
   4902  MOZ_ASSERT(IsEditActionDataAvailable());
   4903 
   4904  RefPtr<Element> rootElement = GetRoot();
   4905  if (NS_WARN_IF(!rootElement)) {
   4906    return NS_ERROR_FAILURE;
   4907  }
   4908 
   4909  nsresult rv = CollapseSelectionToStartOf(*rootElement);
   4910  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   4911                       "EditorBase::CollapseSelectionToStartOf() failed");
   4912  return rv;
   4913 }
   4914 
   4915 /**
   4916 * Remove aNode, re-parenting any children into the parent of aNode.  In
   4917 * addition, insert any br's needed to preserve identity of removed block.
   4918 */
   4919 Result<EditorDOMPoint, nsresult>
   4920 HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
   4921  MOZ_ASSERT(IsEditActionDataAvailable());
   4922 
   4923  // Two possibilities: the container could be empty of editable content.  If
   4924  // that is the case, we need to compare what is before and after aNode to
   4925  // determine if we need a br.
   4926  //
   4927  // Or it could be not empty, in which case we have to compare previous
   4928  // sibling and first child to determine if we need a leading br, and compare
   4929  // following sibling and last child to determine if we need a trailing br.
   4930 
   4931  const RefPtr<Element> parentElement = aElement.GetParentElement();
   4932  if (NS_WARN_IF((!parentElement))) {
   4933    return Err(NS_ERROR_FAILURE);
   4934  }
   4935  EditorDOMPoint pointToPutCaret;
   4936  if (HTMLEditUtils::CanNodeContain(*parentElement, *nsGkAtoms::br)) {
   4937    if (nsCOMPtr<nsIContent> child = HTMLEditUtils::GetFirstChild(
   4938            aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
   4939      // The case of aNode not being empty.  We need a br at start unless:
   4940      // 1) previous sibling of aNode is a block, OR
   4941      // 2) previous sibling of aNode is a br, OR
   4942      // 3) first child of aNode is a block OR
   4943      // 4) either is null
   4944 
   4945      if (nsIContent* previousSibling = HTMLEditUtils::GetPreviousSibling(
   4946              aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
   4947        if (!HTMLEditUtils::IsBlockElement(
   4948                *previousSibling,
   4949                BlockInlineCheck::UseComputedDisplayOutsideStyle) &&
   4950            !previousSibling->IsHTMLElement(nsGkAtoms::br) &&
   4951            !HTMLEditUtils::IsBlockElement(
   4952                *child, BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
   4953          Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
   4954              InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement,
   4955                              EditorDOMPoint(&aElement));
   4956          if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   4957            NS_WARNING(
   4958                "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   4959                "LineBreakType::BRElement) failed");
   4960            return insertBRElementResultOrError.propagateErr();
   4961          }
   4962          CreateLineBreakResult insertBRElementResult =
   4963              insertBRElementResultOrError.unwrap();
   4964          MOZ_ASSERT(insertBRElementResult.Handled());
   4965          insertBRElementResult.IgnoreCaretPointSuggestion();
   4966          pointToPutCaret = EditorDOMPoint(&aElement, 0);
   4967        }
   4968      }
   4969 
   4970      // We need a br at end unless:
   4971      // 1) following sibling of aNode is a block, OR
   4972      // 2) last child of aNode is a block, OR
   4973      // 3) last child of aNode is a br OR
   4974      // 4) either is null
   4975 
   4976      if (nsIContent* nextSibling = HTMLEditUtils::GetNextSibling(
   4977              aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
   4978        if (nextSibling &&
   4979            !HTMLEditUtils::IsBlockElement(
   4980                *nextSibling, BlockInlineCheck::UseComputedDisplayStyle)) {
   4981          if (nsIContent* lastChild = HTMLEditUtils::GetLastChild(
   4982                  aElement, {WalkTreeOption::IgnoreNonEditableNode},
   4983                  BlockInlineCheck::Unused)) {
   4984            if (!HTMLEditUtils::IsBlockElement(
   4985                    *lastChild, BlockInlineCheck::UseComputedDisplayStyle) &&
   4986                !lastChild->IsHTMLElement(nsGkAtoms::br)) {
   4987              Result<CreateLineBreakResult, nsresult>
   4988                  insertBRElementResultOrError = InsertLineBreak(
   4989                      WithTransaction::Yes, LineBreakType::BRElement,
   4990                      EditorDOMPoint::After(aElement));
   4991              if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   4992                NS_WARNING(
   4993                    "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   4994                    "LineBreakType::BRElement) failed");
   4995                return insertBRElementResultOrError.propagateErr();
   4996              }
   4997              CreateLineBreakResult insertBRElementResult =
   4998                  insertBRElementResultOrError.unwrap();
   4999              MOZ_ASSERT(insertBRElementResult.Handled());
   5000              insertBRElementResult.IgnoreCaretPointSuggestion();
   5001              pointToPutCaret = EditorDOMPoint::AtEndOf(aElement);
   5002            }
   5003          }
   5004        }
   5005      }
   5006    } else if (nsIContent* previousSibling = HTMLEditUtils::GetPreviousSibling(
   5007                   aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
   5008      // The case of aNode being empty.  We need a br at start unless:
   5009      // 1) previous sibling of aNode is a block, OR
   5010      // 2) previous sibling of aNode is a br, OR
   5011      // 3) following sibling of aNode is a block, OR
   5012      // 4) following sibling of aNode is a br OR
   5013      // 5) either is null
   5014      if (!HTMLEditUtils::IsBlockElement(
   5015              *previousSibling, BlockInlineCheck::UseComputedDisplayStyle) &&
   5016          !previousSibling->IsHTMLElement(nsGkAtoms::br)) {
   5017        if (nsIContent* nextSibling = HTMLEditUtils::GetNextSibling(
   5018                aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
   5019          if (!HTMLEditUtils::IsBlockElement(
   5020                  *nextSibling, BlockInlineCheck::UseComputedDisplayStyle) &&
   5021              !nextSibling->IsHTMLElement(nsGkAtoms::br)) {
   5022            Result<CreateLineBreakResult, nsresult>
   5023                insertBRElementResultOrError = InsertLineBreak(
   5024                    WithTransaction::Yes, LineBreakType::BRElement,
   5025                    EditorDOMPoint(&aElement));
   5026            if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   5027              NS_WARNING(
   5028                  "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   5029                  "LineBreakType::BRElement) failed");
   5030              return insertBRElementResultOrError.propagateErr();
   5031            }
   5032            CreateLineBreakResult insertBRElementResult =
   5033                insertBRElementResultOrError.unwrap();
   5034            MOZ_ASSERT(insertBRElementResult.Handled());
   5035            insertBRElementResult.IgnoreCaretPointSuggestion();
   5036            pointToPutCaret = EditorDOMPoint(&aElement, 0);
   5037          }
   5038        }
   5039      }
   5040    }
   5041  }
   5042 
   5043  // Now remove container
   5044  AutoTrackDOMPoint trackPointToPutCaret(RangeUpdaterRef(), &pointToPutCaret);
   5045  Result<EditorDOMPoint, nsresult> unwrapBlockElementResult =
   5046      RemoveContainerWithTransaction(aElement);
   5047  if (MOZ_UNLIKELY(unwrapBlockElementResult.isErr())) {
   5048    NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
   5049    return unwrapBlockElementResult;
   5050  }
   5051  trackPointToPutCaret.FlushAndStopTracking();
   5052  if (AllowsTransactionsToChangeSelection() &&
   5053      unwrapBlockElementResult.inspect().IsSet()) {
   5054    pointToPutCaret = unwrapBlockElementResult.unwrap();
   5055  }
   5056  return pointToPutCaret;  // May be unset
   5057 }
   5058 
   5059 Result<SplitNodeResult, nsresult> HTMLEditor::SplitNodeWithTransaction(
   5060    const EditorDOMPoint& aStartOfRightNode) {
   5061  MOZ_ASSERT(IsEditActionDataAvailable());
   5062 
   5063  if (NS_WARN_IF(!aStartOfRightNode.IsInContentNode())) {
   5064    return Err(NS_ERROR_INVALID_ARG);
   5065  }
   5066  MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
   5067 
   5068  if (NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(
   5069          *aStartOfRightNode.ContainerAs<nsIContent>()))) {
   5070    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   5071  }
   5072 
   5073  IgnoredErrorResult ignoredError;
   5074  AutoEditSubActionNotifier startToHandleEditSubAction(
   5075      *this, EditSubAction::eSplitNode, nsIEditor::eNext, ignoredError);
   5076  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   5077    return Err(NS_ERROR_EDITOR_DESTROYED);
   5078  }
   5079  NS_WARNING_ASSERTION(
   5080      !ignoredError.Failed(),
   5081      "OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   5082 
   5083  RefPtr<SplitNodeTransaction> transaction =
   5084      SplitNodeTransaction::Create(*this, aStartOfRightNode);
   5085  nsresult rv = DoTransactionInternal(transaction);
   5086  if (NS_WARN_IF(Destroyed())) {
   5087    NS_WARNING(
   5088        "EditorBase::DoTransactionInternal() caused destroying the editor");
   5089    return Err(NS_ERROR_EDITOR_DESTROYED);
   5090  }
   5091  if (NS_FAILED(rv)) {
   5092    NS_WARNING("EditorBase::DoTransactionInternal() failed");
   5093    return Err(rv);
   5094  }
   5095 
   5096  nsIContent* newContent = transaction->GetNewContent();
   5097  nsIContent* splitContent = transaction->GetSplitContent();
   5098  if (NS_WARN_IF(!newContent) || NS_WARN_IF(!splitContent)) {
   5099    return Err(NS_ERROR_FAILURE);
   5100  }
   5101  TopLevelEditSubActionDataRef().DidSplitContent(*this, *splitContent,
   5102                                                 *newContent);
   5103  if (NS_WARN_IF(!newContent->IsInComposedDoc()) ||
   5104      NS_WARN_IF(!splitContent->IsInComposedDoc())) {
   5105    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   5106  }
   5107 
   5108  return SplitNodeResult(*newContent, *splitContent);
   5109 }
   5110 
   5111 Result<SplitNodeResult, nsresult> HTMLEditor::SplitNodeDeepWithTransaction(
   5112    nsIContent& aMostAncestorToSplit,
   5113    const EditorDOMPoint& aDeepestStartOfRightNode,
   5114    SplitAtEdges aSplitAtEdges) {
   5115  MOZ_ASSERT(aDeepestStartOfRightNode.IsSetAndValidInComposedDoc());
   5116  MOZ_ASSERT(
   5117      aDeepestStartOfRightNode.GetContainer() == &aMostAncestorToSplit ||
   5118      EditorUtils::IsDescendantOf(*aDeepestStartOfRightNode.GetContainer(),
   5119                                  aMostAncestorToSplit));
   5120 
   5121  if (NS_WARN_IF(!aDeepestStartOfRightNode.IsInComposedDoc())) {
   5122    return Err(NS_ERROR_INVALID_ARG);
   5123  }
   5124 
   5125  nsCOMPtr<nsIContent> newLeftNodeOfMostAncestor;
   5126  EditorDOMPoint atStartOfRightNode(aDeepestStartOfRightNode);
   5127  // lastResult is as explained by its name, the last result which may not be
   5128  // split a node actually.
   5129  SplitNodeResult lastResult = SplitNodeResult::NotHandled(atStartOfRightNode);
   5130  MOZ_ASSERT(lastResult.AtSplitPoint<EditorRawDOMPoint>()
   5131                 .IsSetAndValidInComposedDoc());
   5132 
   5133  while (true) {
   5134    // Need to insert rules code call here to do things like not split a list
   5135    // if you are after the last <li> or before the first, etc.  For now we
   5136    // just have some smarts about unnecessarily splitting text nodes, which
   5137    // should be universal enough to put straight in this EditorBase routine.
   5138    auto* splittingContent = atStartOfRightNode.GetContainerAs<nsIContent>();
   5139    if (NS_WARN_IF(!splittingContent)) {
   5140      lastResult.IgnoreCaretPointSuggestion();
   5141      return Err(NS_ERROR_FAILURE);
   5142    }
   5143    // If we meet an orphan node before meeting aMostAncestorToSplit, we need
   5144    // to stop splitting.  This is a bug of the caller.
   5145    if (NS_WARN_IF(splittingContent != &aMostAncestorToSplit &&
   5146                   !atStartOfRightNode.GetContainerParentAs<nsIContent>())) {
   5147      lastResult.IgnoreCaretPointSuggestion();
   5148      return Err(NS_ERROR_FAILURE);
   5149    }
   5150    // If the container is not splitable node such as comment node, atomic
   5151    // element, etc, we should keep it as-is, and try to split its parents.
   5152    if (!HTMLEditUtils::IsSplittableNode(*splittingContent)) {
   5153      if (splittingContent == &aMostAncestorToSplit) {
   5154        return lastResult;
   5155      }
   5156      atStartOfRightNode.Set(splittingContent);
   5157      continue;
   5158    }
   5159 
   5160    // If the split point is middle of the node or the node is not a text node
   5161    // and we're allowed to create empty element node, split it.
   5162    if ((aSplitAtEdges == SplitAtEdges::eAllowToCreateEmptyContainer &&
   5163         !atStartOfRightNode.IsInTextNode()) ||
   5164        (!atStartOfRightNode.IsStartOfContainer() &&
   5165         !atStartOfRightNode.IsEndOfContainer())) {
   5166      Result<SplitNodeResult, nsresult> splitNodeResult =
   5167          SplitNodeWithTransaction(atStartOfRightNode);
   5168      if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
   5169        lastResult.IgnoreCaretPointSuggestion();
   5170        return splitNodeResult;
   5171      }
   5172      lastResult = SplitNodeResult::MergeWithDeeperSplitNodeResult(
   5173          splitNodeResult.unwrap(), lastResult);
   5174      if (NS_WARN_IF(!lastResult.AtSplitPoint<EditorRawDOMPoint>()
   5175                          .IsInComposedDoc())) {
   5176        return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   5177      }
   5178      MOZ_ASSERT(lastResult.HasCaretPointSuggestion());
   5179      MOZ_ASSERT(lastResult.GetOriginalContent() == splittingContent);
   5180      if (splittingContent == &aMostAncestorToSplit) {
   5181        // Actually, we split aMostAncestorToSplit.
   5182        return lastResult;
   5183      }
   5184 
   5185      // Then, try to split its parent before current node.
   5186      atStartOfRightNode = lastResult.AtNextContent<EditorDOMPoint>();
   5187    }
   5188    // If the split point is end of the node and it is a text node or we're not
   5189    // allowed to create empty container node, try to split its parent after it.
   5190    else if (!atStartOfRightNode.IsStartOfContainer()) {
   5191      lastResult = SplitNodeResult::HandledButDidNotSplitDueToEndOfContainer(
   5192          *splittingContent, &lastResult);
   5193      MOZ_ASSERT(lastResult.AtSplitPoint<EditorRawDOMPoint>()
   5194                     .IsSetAndValidInComposedDoc());
   5195      if (splittingContent == &aMostAncestorToSplit) {
   5196        return lastResult;
   5197      }
   5198 
   5199      // Try to split its parent after current node.
   5200      atStartOfRightNode.SetAfter(splittingContent);
   5201    }
   5202    // If the split point is start of the node and it is a text node or we're
   5203    // not allowed to create empty container node, try to split its parent.
   5204    else {
   5205      if (splittingContent == &aMostAncestorToSplit) {
   5206        return SplitNodeResult::HandledButDidNotSplitDueToStartOfContainer(
   5207            *splittingContent, &lastResult);
   5208      }
   5209 
   5210      // Try to split its parent before current node.
   5211      // XXX This is logically wrong.  If we've already split something but
   5212      //     this is the last splitable content node in the limiter, this
   5213      //     method will return "not handled".
   5214      lastResult = SplitNodeResult::NotHandled(atStartOfRightNode, &lastResult);
   5215      MOZ_ASSERT(lastResult.AtSplitPoint<EditorRawDOMPoint>()
   5216                     .IsSetAndValidInComposedDoc());
   5217      atStartOfRightNode.Set(splittingContent);
   5218      MOZ_ASSERT(atStartOfRightNode.IsSetAndValidInComposedDoc());
   5219    }
   5220  }
   5221 
   5222  // Not reached because while (true) loop never breaks.
   5223 }
   5224 
   5225 Result<SplitNodeResult, nsresult> HTMLEditor::DoSplitNode(
   5226    const EditorDOMPoint& aStartOfRightNode, nsIContent& aNewNode) {
   5227  // Ensure computing the offset if it's initialized with a child content node.
   5228  (void)aStartOfRightNode.Offset();
   5229 
   5230  // XXX Perhaps, aStartOfRightNode may be invalid if this is a redo
   5231  //     operation after modifying DOM node with JS.
   5232  if (NS_WARN_IF(!aStartOfRightNode.IsInContentNode())) {
   5233    return Err(NS_ERROR_INVALID_ARG);
   5234  }
   5235  MOZ_DIAGNOSTIC_ASSERT(aStartOfRightNode.IsSetAndValid());
   5236 
   5237  // Remember all selection points.
   5238  AutoTArray<SavedRange, 10> savedRanges;
   5239  for (SelectionType selectionType : kPresentSelectionTypes) {
   5240    SavedRange savingRange;
   5241    savingRange.mSelection = GetSelection(selectionType);
   5242    if (NS_WARN_IF(!savingRange.mSelection &&
   5243                   selectionType == SelectionType::eNormal)) {
   5244      return Err(NS_ERROR_FAILURE);
   5245    }
   5246    if (!savingRange.mSelection) {
   5247      // For non-normal selections, skip over the non-existing ones.
   5248      continue;
   5249    }
   5250 
   5251    for (uint32_t j : IntegerRange(savingRange.mSelection->RangeCount())) {
   5252      const nsRange* r = savingRange.mSelection->GetRangeAt(j);
   5253      MOZ_ASSERT(r);
   5254      MOZ_ASSERT(r->IsPositioned());
   5255      // XXX Looks like that SavedRange should have mStart and mEnd which
   5256      //     are RangeBoundary.  Then, we can avoid to compute offset here.
   5257      savingRange.mStartContainer = r->GetStartContainer();
   5258      savingRange.mStartOffset = r->StartOffset();
   5259      savingRange.mEndContainer = r->GetEndContainer();
   5260      savingRange.mEndOffset = r->EndOffset();
   5261 
   5262      savedRanges.AppendElement(savingRange);
   5263    }
   5264  }
   5265 
   5266  const nsCOMPtr<nsINode> containerParentNode =
   5267      aStartOfRightNode.GetContainerParent();
   5268  if (NS_WARN_IF(!containerParentNode)) {
   5269    return Err(NS_ERROR_FAILURE);
   5270  }
   5271 
   5272  // For the performance of IMEContentObserver, we should move all data into
   5273  // aNewNode first because IMEContentObserver needs to compute moved content
   5274  // length only once when aNewNode is connected.
   5275 
   5276  // If we are splitting a text node, we need to move its some data to the
   5277  // new text node.
   5278  MOZ_DIAGNOSTIC_ASSERT_IF(aStartOfRightNode.IsInTextNode(), aNewNode.IsText());
   5279  MOZ_DIAGNOSTIC_ASSERT_IF(!aStartOfRightNode.IsInTextNode(),
   5280                           !aNewNode.IsText());
   5281  const nsCOMPtr<nsIContent> firstChildOfRightNode =
   5282      aStartOfRightNode.GetChild();
   5283  nsresult rv = [&]() MOZ_NEVER_INLINE_DEBUG MOZ_CAN_RUN_SCRIPT {
   5284    if (aStartOfRightNode.IsEndOfContainer()) {
   5285      return NS_OK;  // No content which should be moved into aNewNode.
   5286    }
   5287    if (aStartOfRightNode.IsInTextNode()) {
   5288      Text* originalTextNode = aStartOfRightNode.ContainerAs<Text>();
   5289      Text* newTextNode = aNewNode.AsText();
   5290      nsAutoString movingText;
   5291      const uint32_t cutStartOffset = aStartOfRightNode.Offset();
   5292      const uint32_t cutLength =
   5293          originalTextNode->Length() - aStartOfRightNode.Offset();
   5294      IgnoredErrorResult error;
   5295      originalTextNode->SubstringData(cutStartOffset, cutLength, movingText,
   5296                                      error);
   5297      NS_WARNING_ASSERTION(!error.Failed(),
   5298                           "Text::SubstringData() failed, but ignored");
   5299      error.SuppressException();
   5300 
   5301      // XXX This call may destroy us.
   5302      DoDeleteText(MOZ_KnownLive(*originalTextNode), cutStartOffset, cutLength,
   5303                   error);
   5304      NS_WARNING_ASSERTION(!error.Failed(),
   5305                           "EditorBase::DoDeleteText() failed, but ignored");
   5306      error.SuppressException();
   5307 
   5308      // XXX This call may destroy us.
   5309      DoSetText(MOZ_KnownLive(*newTextNode), movingText, error);
   5310      NS_WARNING_ASSERTION(!error.Failed(),
   5311                           "EditorBase::DoSetText() failed, but ignored");
   5312      return NS_OK;
   5313    }
   5314 
   5315    // If the right node is new one and splitting at start of the container,
   5316    // we need to move all children to the new right node.
   5317    if (!firstChildOfRightNode->GetPreviousSibling()) {
   5318      // XXX Why do we ignore an error while moving nodes from the right
   5319      //     node to the left node?
   5320      nsresult rv = MoveAllChildren(
   5321          // MOZ_KnownLive because aStartOfRightNode grabs the container.
   5322          MOZ_KnownLive(*aStartOfRightNode.GetContainer()),
   5323          EditorRawDOMPoint(&aNewNode, 0u));
   5324      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5325                           "HTMLEditor::MoveAllChildren() failed");
   5326      return rv;
   5327    }
   5328 
   5329    // If the right node is new one and splitting at middle of the node, we need
   5330    // to move inclusive next siblings of the split point to the new right node.
   5331    // XXX Why do we ignore an error while moving nodes from the right node
   5332    //     to the left node?
   5333    nsresult rv = MoveInclusiveNextSiblings(*firstChildOfRightNode,
   5334                                            EditorRawDOMPoint(&aNewNode, 0u));
   5335    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   5336                         "HTMLEditor::MoveInclusiveNextSiblings() failed");
   5337    return rv;
   5338  }();
   5339 
   5340  // To avoid a dataloss bug, we should try to insert aNewNode even if we've
   5341  // already been destroyed.
   5342  if (NS_WARN_IF(!aStartOfRightNode.GetContainerParent())) {
   5343    return NS_WARN_IF(Destroyed()) ? Err(NS_ERROR_EDITOR_DESTROYED)
   5344                                   : Err(NS_ERROR_FAILURE);
   5345  }
   5346 
   5347  // Finally, we should insert aNewNode which already has proper data or
   5348  // children.
   5349  {
   5350    const nsCOMPtr<nsIContent> nextSibling =
   5351        aStartOfRightNode.GetContainer()->GetNextSibling();
   5352    AutoNodeAPIWrapper nodeWrapper(*this, *containerParentNode);
   5353    nsresult rv = nodeWrapper.InsertBefore(aNewNode, nextSibling);
   5354    if (NS_FAILED(rv)) {
   5355      NS_WARNING("AutoNodeAPIWrapper::InsertBefore() failed");
   5356      return Err(rv);
   5357    }
   5358    NS_WARNING_ASSERTION(
   5359        nodeWrapper.IsExpectedResult(),
   5360        "Inserting new node caused other mutations, but ignored");
   5361  }
   5362  if (NS_FAILED(rv)) {
   5363    NS_WARNING("Moving children from left node to right node failed");
   5364    return Err(rv);
   5365  }
   5366 
   5367  // Handle selection
   5368  // TODO: Stop doing this, this shouldn't be necessary to update selection.
   5369  if (RefPtr<PresShell> presShell = GetPresShell()) {
   5370    presShell->FlushPendingNotifications(FlushType::Frames);
   5371  }
   5372  NS_WARNING_ASSERTION(!Destroyed(),
   5373                       "The editor is destroyed during splitting a node");
   5374 
   5375  const bool allowedTransactionsToChangeSelection =
   5376      AllowsTransactionsToChangeSelection();
   5377 
   5378  IgnoredErrorResult error;
   5379  RefPtr<Selection> previousSelection;
   5380  for (SavedRange& savedRange : savedRanges) {
   5381    // If we have not seen the selection yet, clear all of its ranges.
   5382    if (savedRange.mSelection != previousSelection) {
   5383      MOZ_KnownLive(savedRange.mSelection)->RemoveAllRanges(error);
   5384      if (MOZ_UNLIKELY(error.Failed())) {
   5385        NS_WARNING("Selection::RemoveAllRanges() failed");
   5386        return Err(error.StealNSResult());
   5387      }
   5388      previousSelection = savedRange.mSelection;
   5389    }
   5390 
   5391    // XXX Looks like that we don't need to modify normal selection here
   5392    //     because selection will be modified by the caller if
   5393    //     AllowsTransactionsToChangeSelection() will return true.
   5394    if (allowedTransactionsToChangeSelection &&
   5395        savedRange.mSelection->Type() == SelectionType::eNormal) {
   5396      // If the editor should adjust the selection, don't bother restoring
   5397      // the ranges for the normal selection here.
   5398      continue;
   5399    }
   5400 
   5401    auto AdjustDOMPoint = [&](nsCOMPtr<nsINode>& aContainer,
   5402                              uint32_t& aOffset) {
   5403      if (aContainer != aStartOfRightNode.GetContainer()) {
   5404        return;
   5405      }
   5406 
   5407      // If the container is the left node and offset is after the split
   5408      // point, the content was moved from the right node to aNewNode.
   5409      // So, we need to change the container to aNewNode and decrease the
   5410      // offset.
   5411      if (aOffset >= aStartOfRightNode.Offset()) {
   5412        aContainer = &aNewNode;
   5413        aOffset -= aStartOfRightNode.Offset();
   5414      }
   5415    };
   5416    AdjustDOMPoint(savedRange.mStartContainer, savedRange.mStartOffset);
   5417    AdjustDOMPoint(savedRange.mEndContainer, savedRange.mEndOffset);
   5418 
   5419    RefPtr<nsRange> newRange =
   5420        nsRange::Create(savedRange.mStartContainer, savedRange.mStartOffset,
   5421                        savedRange.mEndContainer, savedRange.mEndOffset, error);
   5422    if (MOZ_UNLIKELY(error.Failed())) {
   5423      NS_WARNING("nsRange::Create() failed");
   5424      return Err(error.StealNSResult());
   5425    }
   5426    // The `MOZ_KnownLive` annotation is only necessary because of a bug
   5427    // (https://bugzilla.mozilla.org/show_bug.cgi?id=1622253) in the
   5428    // static analyzer.
   5429    MOZ_KnownLive(savedRange.mSelection)
   5430        ->AddRangeAndSelectFramesAndNotifyListeners(*newRange, error);
   5431    if (MOZ_UNLIKELY(error.Failed())) {
   5432      NS_WARNING(
   5433          "Selection::AddRangeAndSelectFramesAndNotifyListeners() failed");
   5434      return Err(error.StealNSResult());
   5435    }
   5436  }
   5437 
   5438  // We don't need to set selection here because the caller should do that
   5439  // in any case.
   5440 
   5441  // If splitting the node causes running mutation event listener and we've
   5442  // got unexpected result, we should return error because callers will
   5443  // continue to do their work without complicated DOM tree result.
   5444  // NOTE: Perhaps, we shouldn't do this immediately after each DOM tree change
   5445  //       because stopping handling it causes some data loss.  E.g., user
   5446  //       may loose the text which is moved to the new text node.
   5447  // XXX We cannot check all descendants in the right node and the new left
   5448  //     node for performance reason.  I think that if caller needs to access
   5449  //     some of the descendants, they should check by themselves.
   5450  if (NS_WARN_IF(containerParentNode !=
   5451                 aStartOfRightNode.GetContainer()->GetParentNode()) ||
   5452      NS_WARN_IF(containerParentNode != aNewNode.GetParentNode()) ||
   5453      NS_WARN_IF(aNewNode.GetPreviousSibling() !=
   5454                 aStartOfRightNode.GetContainer())) {
   5455    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   5456  }
   5457 
   5458  DebugOnly<nsresult> rvIgnored = RangeUpdaterRef().SelAdjSplitNode(
   5459      *aStartOfRightNode.ContainerAs<nsIContent>(), aStartOfRightNode.Offset(),
   5460      aNewNode);
   5461  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   5462                       "RangeUpdater::SelAdjSplitNode() failed, but ignored");
   5463 
   5464  return SplitNodeResult(aNewNode,
   5465                         *aStartOfRightNode.ContainerAs<nsIContent>());
   5466 }
   5467 
   5468 Result<JoinNodesResult, nsresult> HTMLEditor::JoinNodesWithTransaction(
   5469    nsIContent& aLeftContent, nsIContent& aRightContent) {
   5470  MOZ_ASSERT(IsEditActionDataAvailable());
   5471  MOZ_ASSERT(&aLeftContent != &aRightContent);
   5472  MOZ_ASSERT(aLeftContent.GetParentNode());
   5473  MOZ_ASSERT(aRightContent.GetParentNode());
   5474  MOZ_ASSERT(aLeftContent.GetParentNode() == aRightContent.GetParentNode());
   5475 
   5476  IgnoredErrorResult ignoredError;
   5477  AutoEditSubActionNotifier startToHandleEditSubAction(
   5478      *this, EditSubAction::eJoinNodes, nsIEditor::ePrevious, ignoredError);
   5479  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   5480    return Err(ignoredError.StealNSResult());
   5481  }
   5482  NS_WARNING_ASSERTION(
   5483      !ignoredError.Failed(),
   5484      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   5485 
   5486  if (NS_WARN_IF(!aRightContent.GetParentNode())) {
   5487    return Err(NS_ERROR_FAILURE);
   5488  }
   5489 
   5490  RefPtr<JoinNodesTransaction> transaction =
   5491      JoinNodesTransaction::MaybeCreate(*this, aLeftContent, aRightContent);
   5492  if (MOZ_UNLIKELY(!transaction)) {
   5493    NS_WARNING("JoinNodesTransaction::MaybeCreate() failed");
   5494    return Err(NS_ERROR_FAILURE);
   5495  }
   5496 
   5497  const nsresult rv = DoTransactionInternal(transaction);
   5498  // FYI: Now, DidJoinNodesTransaction() must have been run if succeeded.
   5499  if (NS_WARN_IF(Destroyed())) {
   5500    return Err(NS_ERROR_EDITOR_DESTROYED);
   5501  }
   5502 
   5503  // This shouldn't occur unless the cycle collector runs by chrome script
   5504  // forcibly.
   5505  if (NS_WARN_IF(!transaction->GetRemovedContent()) ||
   5506      NS_WARN_IF(!transaction->GetExistingContent())) {
   5507    return Err(NS_ERROR_UNEXPECTED);
   5508  }
   5509 
   5510  // If joined node is moved to different place, offset may not have any
   5511  // meaning.  In this case, the web app modified the DOM tree takes on the
   5512  // responsibility for the remaning things.
   5513  if (NS_WARN_IF(transaction->GetExistingContent()->GetParent() !=
   5514                 transaction->GetParentNode())) {
   5515    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   5516  }
   5517 
   5518  if (NS_FAILED(rv)) {
   5519    NS_WARNING("EditorBase::DoTransactionInternal() failed");
   5520    return Err(rv);
   5521  }
   5522 
   5523  return JoinNodesResult(transaction->CreateJoinedPoint<EditorDOMPoint>(),
   5524                         *transaction->GetRemovedContent());
   5525 }
   5526 
   5527 void HTMLEditor::DidJoinNodesTransaction(
   5528    const JoinNodesTransaction& aTransaction, nsresult aDoJoinNodesResult) {
   5529  // This shouldn't occur unless the cycle collector runs by chrome script
   5530  // forcibly.
   5531  if (MOZ_UNLIKELY(NS_WARN_IF(!aTransaction.GetRemovedContent()) ||
   5532                   NS_WARN_IF(!aTransaction.GetExistingContent()))) {
   5533    return;
   5534  }
   5535 
   5536  // If joined node is moved to different place, offset may not have any
   5537  // meaning.  In this case, the web app modified the DOM tree takes on the
   5538  // responsibility for the remaning things.
   5539  if (MOZ_UNLIKELY(aTransaction.GetExistingContent()->GetParentNode() !=
   5540                   aTransaction.GetParentNode())) {
   5541    return;
   5542  }
   5543 
   5544  // Be aware, the joined point should be created for each call because
   5545  // they may refer the child node, but some of them may change the DOM tree
   5546  // after that, thus we need to avoid invalid point (Although it shouldn't
   5547  // occur).
   5548  TopLevelEditSubActionDataRef().DidJoinContents(
   5549      *this, aTransaction.CreateJoinedPoint<EditorRawDOMPoint>());
   5550 
   5551  if (NS_SUCCEEDED(aDoJoinNodesResult)) {
   5552    if (RefPtr<TextServicesDocument> textServicesDocument =
   5553            mTextServicesDocument) {
   5554      textServicesDocument->DidJoinContents(
   5555          aTransaction.CreateJoinedPoint<EditorRawDOMPoint>(),
   5556          *aTransaction.GetRemovedContent());
   5557    }
   5558  }
   5559 
   5560  if (!mActionListeners.IsEmpty()) {
   5561    for (auto& listener : mActionListeners.Clone()) {
   5562      DebugOnly<nsresult> rvIgnored = listener->DidJoinContents(
   5563          aTransaction.CreateJoinedPoint<EditorRawDOMPoint>(),
   5564          aTransaction.GetRemovedContent());
   5565      NS_WARNING_ASSERTION(
   5566          NS_SUCCEEDED(rvIgnored),
   5567          "nsIEditActionListener::DidJoinContents() failed, but ignored");
   5568    }
   5569  }
   5570 }
   5571 
   5572 nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
   5573                                 nsIContent& aContentToRemove) {
   5574  MOZ_ASSERT(IsEditActionDataAvailable());
   5575 
   5576  const uint32_t keepingContentLength = aContentToKeep.Length();
   5577  const EditorDOMPoint oldPointAtRightContent(&aContentToRemove);
   5578  if (MOZ_LIKELY(oldPointAtRightContent.IsSet())) {
   5579    (void)oldPointAtRightContent.Offset();  // Fix the offset
   5580  }
   5581 
   5582  // Remember all selection points.
   5583  // XXX Do we need to restore all types of selections by ourselves?  Normal
   5584  //     selection should be modified later as result of handling edit action.
   5585  //     IME selections shouldn't be there when nodes are joined.  Spellcheck
   5586  //     selections should be recreated with newer text.  URL selections
   5587  //     shouldn't be there because of used only by the URL bar.
   5588  AutoTArray<SavedRange, 10> savedRanges;
   5589  {
   5590    EditorRawDOMPoint atRemovingNode(&aContentToRemove);
   5591    EditorRawDOMPoint atNodeToKeep(&aContentToKeep);
   5592    for (SelectionType selectionType : kPresentSelectionTypes) {
   5593      SavedRange savingRange;
   5594      savingRange.mSelection = GetSelection(selectionType);
   5595      if (selectionType == SelectionType::eNormal) {
   5596        if (NS_WARN_IF(!savingRange.mSelection)) {
   5597          return NS_ERROR_FAILURE;
   5598        }
   5599      } else if (!savingRange.mSelection) {
   5600        // For non-normal selections, skip over the non-existing ones.
   5601        continue;
   5602      }
   5603 
   5604      const uint32_t rangeCount = savingRange.mSelection->RangeCount();
   5605      for (const uint32_t j : IntegerRange(rangeCount)) {
   5606        MOZ_ASSERT(savingRange.mSelection->RangeCount() == rangeCount);
   5607        const RefPtr<nsRange> r = savingRange.mSelection->GetRangeAt(j);
   5608        MOZ_ASSERT(r);
   5609        MOZ_ASSERT(r->IsPositioned());
   5610        savingRange.mStartContainer = r->GetStartContainer();
   5611        savingRange.mStartOffset = r->StartOffset();
   5612        savingRange.mEndContainer = r->GetEndContainer();
   5613        savingRange.mEndOffset = r->EndOffset();
   5614 
   5615        // If selection endpoint is between the nodes, remember it as being
   5616        // in the one that is going away instead.  This simplifies later
   5617        // selection adjustment logic at end of this method.
   5618        if (savingRange.mStartContainer) {
   5619          MOZ_ASSERT(savingRange.mEndContainer);
   5620          auto AdjustDOMPoint = [&](nsCOMPtr<nsINode>& aContainer,
   5621                                    uint32_t& aOffset) {
   5622            // If range boundary points aContentToRemove and aContentToKeep is
   5623            // its left node, remember it as being at end of aContentToKeep.
   5624            // Then, it will point start of the first content of moved content
   5625            // from aContentToRemove.
   5626            if (aContainer == atRemovingNode.GetContainer() &&
   5627                atNodeToKeep.Offset() < aOffset &&
   5628                aOffset <= atRemovingNode.Offset()) {
   5629              aContainer = &aContentToKeep;
   5630              aOffset = keepingContentLength;
   5631            }
   5632          };
   5633          AdjustDOMPoint(savingRange.mStartContainer, savingRange.mStartOffset);
   5634          AdjustDOMPoint(savingRange.mEndContainer, savingRange.mEndOffset);
   5635        }
   5636 
   5637        savedRanges.AppendElement(savingRange);
   5638      }
   5639    }
   5640  }
   5641 
   5642  // OK, ready to do join now.
   5643  nsresult rv = [&]() MOZ_NEVER_INLINE_DEBUG MOZ_CAN_RUN_SCRIPT {
   5644    // If it's a text node, just shuffle around some text.
   5645    if (aContentToKeep.IsText() && aContentToRemove.IsText()) {
   5646      nsAutoString rightText;
   5647      aContentToRemove.AsText()->GetData(rightText);
   5648      // Delete the node first to minimize the text change range from
   5649      // IMEContentObserver of view.
   5650      {
   5651        AutoNodeAPIWrapper nodeWrapper(*this, aContentToRemove);
   5652        if (NS_FAILED(nodeWrapper.Remove())) {
   5653          NS_WARNING("AutoNodeAPIWrapper::Remove() failed, but ignored");
   5654        } else {
   5655          NS_WARNING_ASSERTION(
   5656              nodeWrapper.IsExpectedResult(),
   5657              "Deleting node caused other mutations, but ignored");
   5658        }
   5659      }
   5660      // Even if we've already destroyed, let's update aContentToKeep for
   5661      // avoiding a dataloss bug.
   5662      IgnoredErrorResult ignoredError;
   5663      DoInsertText(MOZ_KnownLive(*aContentToKeep.AsText()),
   5664                   aContentToKeep.AsText()->TextDataLength(), rightText,
   5665                   ignoredError);
   5666      if (NS_WARN_IF(Destroyed())) {
   5667        return NS_ERROR_EDITOR_DESTROYED;
   5668      }
   5669      NS_WARNING_ASSERTION(!ignoredError.Failed(),
   5670                           "EditorBase::DoSetText() failed, but ignored");
   5671      return NS_OK;
   5672    }
   5673    // Otherwise it's an interior node, so shuffle around the children.
   5674    AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfChildContents;
   5675    HTMLEditUtils::CollectAllChildren(aContentToRemove, arrayOfChildContents);
   5676    // Delete the node first to minimize the text change range from
   5677    // IMEContentObserver of view.
   5678    {
   5679      AutoNodeAPIWrapper nodeWrapper(*this, aContentToRemove);
   5680      if (NS_FAILED(nodeWrapper.Remove())) {
   5681        NS_WARNING("AutoNodeAPIWrapper::Remove() failed, but ignored");
   5682      } else {
   5683        NS_WARNING_ASSERTION(
   5684            nodeWrapper.IsExpectedResult(),
   5685            "Deleting node caused other mutations, but ignored");
   5686      }
   5687    }
   5688    // Even if we've already destroyed, let's update aContentToKeep for avoiding
   5689    // a dataloss bug.
   5690    nsresult rv = NS_OK;
   5691    for (const OwningNonNull<nsIContent>& child : arrayOfChildContents) {
   5692      AutoNodeAPIWrapper nodeWrapper(*this, aContentToKeep);
   5693      nsresult rvInner = nodeWrapper.AppendChild(MOZ_KnownLive(child));
   5694      if (NS_FAILED(rvInner)) {
   5695        NS_WARNING("AutoNodeAPIWrapper::AppendChild() failed");
   5696        rv = rvInner;
   5697      } else {
   5698        NS_WARNING_ASSERTION(
   5699            nodeWrapper.IsExpectedResult(),
   5700            "Appending child caused other mutations, but ignored");
   5701      }
   5702    }
   5703    if (NS_WARN_IF(Destroyed())) {
   5704      return NS_ERROR_EDITOR_DESTROYED;
   5705    }
   5706    return rv;
   5707  }();
   5708 
   5709  if (MOZ_LIKELY(oldPointAtRightContent.IsSet())) {
   5710    DebugOnly<nsresult> rvIgnored = RangeUpdaterRef().SelAdjJoinNodes(
   5711        EditorRawDOMPoint(&aContentToKeep, std::min(keepingContentLength,
   5712                                                    aContentToKeep.Length())),
   5713        aContentToRemove, oldPointAtRightContent);
   5714    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   5715                         "RangeUpdater::SelAdjJoinNodes() failed, but ignored");
   5716  }
   5717  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
   5718    return rv;
   5719  }
   5720 
   5721  const bool allowedTransactionsToChangeSelection =
   5722      AllowsTransactionsToChangeSelection();
   5723 
   5724  // And adjust the selection if needed.
   5725  RefPtr<Selection> previousSelection;
   5726  for (SavedRange& savedRange : savedRanges) {
   5727    // If we have not seen the selection yet, clear all of its ranges.
   5728    if (savedRange.mSelection != previousSelection) {
   5729      IgnoredErrorResult error;
   5730      MOZ_KnownLive(savedRange.mSelection)->RemoveAllRanges(error);
   5731      if (NS_WARN_IF(Destroyed())) {
   5732        return NS_ERROR_EDITOR_DESTROYED;
   5733      }
   5734      if (error.Failed()) {
   5735        NS_WARNING("Selection::RemoveAllRanges() failed");
   5736        return error.StealNSResult();
   5737      }
   5738      previousSelection = savedRange.mSelection;
   5739    }
   5740 
   5741    if (allowedTransactionsToChangeSelection &&
   5742        savedRange.mSelection->Type() == SelectionType::eNormal) {
   5743      // If the editor should adjust the selection, don't bother restoring
   5744      // the ranges for the normal selection here.
   5745      continue;
   5746    }
   5747 
   5748    auto AdjustDOMPoint = [&](nsCOMPtr<nsINode>& aContainer,
   5749                              uint32_t& aOffset) {
   5750      // Now, all content of aContentToRemove are moved to end of
   5751      // aContentToKeep.  Therefore, if a range boundary was in
   5752      // aContentToRemove, we need to change the container to aContentToKeep and
   5753      // adjust the offset to after the original content of aContentToKeep.
   5754      if (aContainer == &aContentToRemove) {
   5755        aContainer = &aContentToKeep;
   5756        aOffset += keepingContentLength;
   5757      }
   5758    };
   5759    AdjustDOMPoint(savedRange.mStartContainer, savedRange.mStartOffset);
   5760    AdjustDOMPoint(savedRange.mEndContainer, savedRange.mEndOffset);
   5761 
   5762    const RefPtr<nsRange> newRange = nsRange::Create(
   5763        savedRange.mStartContainer, savedRange.mStartOffset,
   5764        savedRange.mEndContainer, savedRange.mEndOffset, IgnoreErrors());
   5765    if (!newRange) {
   5766      NS_WARNING("nsRange::Create() failed");
   5767      return NS_ERROR_FAILURE;
   5768    }
   5769 
   5770    IgnoredErrorResult error;
   5771    // The `MOZ_KnownLive` annotation is only necessary because of a bug
   5772    // (https://bugzilla.mozilla.org/show_bug.cgi?id=1622253) in the
   5773    // static analyzer.
   5774    MOZ_KnownLive(savedRange.mSelection)
   5775        ->AddRangeAndSelectFramesAndNotifyListeners(*newRange, error);
   5776    if (NS_WARN_IF(Destroyed())) {
   5777      return NS_ERROR_EDITOR_DESTROYED;
   5778    }
   5779    if (NS_WARN_IF(error.Failed())) {
   5780      return error.StealNSResult();
   5781    }
   5782  }
   5783 
   5784  if (allowedTransactionsToChangeSelection) {
   5785    // Editor wants us to set selection at join point.
   5786    DebugOnly<nsresult> rvIgnored = CollapseSelectionToStartOf(aContentToKeep);
   5787    if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
   5788      NS_WARNING(
   5789          "EditorBase::CollapseSelectionTo() caused destroying the editor");
   5790      return NS_ERROR_EDITOR_DESTROYED;
   5791    }
   5792    NS_WARNING_ASSERTION(
   5793        NS_SUCCEEDED(rv),
   5794        "EditorBases::CollapseSelectionTos() failed, but ignored");
   5795  }
   5796 
   5797  return NS_OK;
   5798 }
   5799 
   5800 Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeWithTransaction(
   5801    nsIContent& aContentToMove, const EditorDOMPoint& aPointToInsert) {
   5802  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   5803 
   5804  EditorDOMPoint oldPoint(&aContentToMove);
   5805  if (NS_WARN_IF(!oldPoint.IsSet())) {
   5806    return Err(NS_ERROR_FAILURE);
   5807  }
   5808 
   5809  // Don't do anything if it's already in right place.
   5810  if (aPointToInsert == oldPoint) {
   5811    return MoveNodeResult::IgnoredResult(aPointToInsert.NextPoint());
   5812  }
   5813 
   5814  RefPtr<MoveNodeTransaction> moveNodeTransaction =
   5815      MoveNodeTransaction::MaybeCreate(*this, aContentToMove, aPointToInsert);
   5816  if (MOZ_UNLIKELY(!moveNodeTransaction)) {
   5817    NS_WARNING("MoveNodeTransaction::MaybeCreate() failed");
   5818    return Err(NS_ERROR_FAILURE);
   5819  }
   5820 
   5821  IgnoredErrorResult ignoredError;
   5822  AutoEditSubActionNotifier startToHandleEditSubAction(
   5823      *this, EditSubAction::eMoveNode, nsIEditor::eNext, ignoredError);
   5824  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   5825    return Err(ignoredError.StealNSResult());
   5826  }
   5827  NS_WARNING_ASSERTION(
   5828      !ignoredError.Failed(),
   5829      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   5830 
   5831  TopLevelEditSubActionDataRef().WillDeleteContent(*this, aContentToMove);
   5832 
   5833  nsresult rv = DoTransactionInternal(moveNodeTransaction);
   5834  if (NS_SUCCEEDED(rv)) {
   5835    if (mTextServicesDocument) {
   5836      const OwningNonNull<TextServicesDocument> textServicesDocument =
   5837          *mTextServicesDocument;
   5838      textServicesDocument->DidDeleteContent(aContentToMove);
   5839    }
   5840  }
   5841 
   5842  if (!mActionListeners.IsEmpty()) {
   5843    for (auto& listener : mActionListeners.Clone()) {
   5844      DebugOnly<nsresult> rvIgnored =
   5845          listener->DidDeleteNode(&aContentToMove, rv);
   5846      NS_WARNING_ASSERTION(
   5847          NS_SUCCEEDED(rvIgnored),
   5848          "nsIEditActionListener::DidDeleteNode() failed, but ignored");
   5849    }
   5850  }
   5851 
   5852  if (MOZ_UNLIKELY(Destroyed())) {
   5853    NS_WARNING(
   5854        "MoveNodeTransaction::DoTransaction() caused destroying the editor");
   5855    return Err(NS_ERROR_EDITOR_DESTROYED);
   5856  }
   5857 
   5858  if (NS_FAILED(rv)) {
   5859    NS_WARNING("MoveNodeTransaction::DoTransaction() failed");
   5860    return Err(rv);
   5861  }
   5862 
   5863  TopLevelEditSubActionDataRef().DidInsertContent(*this, aContentToMove);
   5864 
   5865  return MoveNodeResult::HandledResult(
   5866      moveNodeTransaction->SuggestNextInsertionPoint().To<EditorDOMPoint>(),
   5867      moveNodeTransaction->SuggestPointToPutCaret().To<EditorDOMPoint>());
   5868 }
   5869 
   5870 Result<MoveNodeResult, nsresult> HTMLEditor::MoveSiblingsWithTransaction(
   5871    nsIContent& aFirstContentToMove, nsIContent& aLastContentToMove,
   5872    const EditorDOMPoint& aPointToInsert) {
   5873  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
   5874  MOZ_ASSERT(aFirstContentToMove.GetParentNode() ==
   5875             aLastContentToMove.GetParentNode());
   5876  if (&aFirstContentToMove == &aLastContentToMove) {
   5877    Result<MoveNodeResult, nsresult> moveNodeResultOrError =
   5878        MoveNodeWithTransaction(aFirstContentToMove, aPointToInsert);
   5879    NS_WARNING_ASSERTION(moveNodeResultOrError.isOk(),
   5880                         "HTMLEditor::MoveNodeWithTransaction() failed");
   5881    return moveNodeResultOrError;
   5882  }
   5883 
   5884  MOZ_ASSERT(*aFirstContentToMove.ComputeIndexInParentNode() <
   5885             *aLastContentToMove.ComputeIndexInParentNode());
   5886 
   5887  // Don't do anything if it's already in right place.
   5888  {
   5889    const EditorDOMPoint atFirstContent(&aFirstContentToMove);
   5890    if (NS_WARN_IF(!atFirstContent.IsSet())) {
   5891      return Err(NS_ERROR_FAILURE);
   5892    }
   5893    const EditorDOMPoint atLastContent(&aLastContentToMove);
   5894    if (NS_WARN_IF(!atLastContent.IsSet())) {
   5895      return Err(NS_ERROR_FAILURE);
   5896    }
   5897    if (aPointToInsert.GetContainer() == atFirstContent.GetContainer() &&
   5898        atFirstContent.EqualsOrIsBefore(aPointToInsert) &&
   5899        aPointToInsert.EqualsOrIsBefore(
   5900            atLastContent.NextPoint<EditorRawDOMPoint>())) {
   5901      return MoveNodeResult::IgnoredResult(atLastContent.NextPoint());
   5902    }
   5903  }
   5904 
   5905  const RefPtr<MoveSiblingsTransaction> moveSiblingsTransaction =
   5906      MoveSiblingsTransaction::MaybeCreate(*this, aFirstContentToMove,
   5907                                           aLastContentToMove, aPointToInsert);
   5908  if (MOZ_UNLIKELY(!moveSiblingsTransaction)) {
   5909    NS_WARNING("MoveNodeTransaction::MaybeCreate() failed");
   5910    return Err(NS_ERROR_FAILURE);
   5911  }
   5912 
   5913  IgnoredErrorResult ignoredError;
   5914  AutoEditSubActionNotifier startToHandleEditSubAction(
   5915      *this, EditSubAction::eMoveNode, nsIEditor::eNext, ignoredError);
   5916  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   5917    return Err(ignoredError.StealNSResult());
   5918  }
   5919  NS_WARNING_ASSERTION(
   5920      !ignoredError.Failed(),
   5921      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
   5922 
   5923  // WillDeleteContent will extend the changed range to contain all points where
   5924  // the moved nodes were.  Therefore, we need to call it only for the first and
   5925  // and the last one.
   5926  TopLevelEditSubActionDataRef().WillDeleteContent(*this, aFirstContentToMove);
   5927  TopLevelEditSubActionDataRef().WillDeleteContent(*this, aLastContentToMove);
   5928 
   5929  nsresult rv = DoTransactionInternal(moveSiblingsTransaction);
   5930  Maybe<CopyableAutoTArray<OwningNonNull<nsIContent>, 64>> movedSiblings;
   5931  if (NS_SUCCEEDED(rv)) {
   5932    if (mTextServicesDocument) {
   5933      movedSiblings.emplace(moveSiblingsTransaction->TargetSiblings());
   5934      const OwningNonNull<TextServicesDocument> textServicesDocument =
   5935          *mTextServicesDocument;
   5936      for (const OwningNonNull<nsIContent>& movedContent :
   5937           movedSiblings.ref()) {
   5938        textServicesDocument->DidDeleteContent(MOZ_KnownLive(*movedContent));
   5939      }
   5940    }
   5941  }
   5942 
   5943  if (!mActionListeners.IsEmpty()) {
   5944    if (!movedSiblings) {
   5945      movedSiblings.emplace(moveSiblingsTransaction->TargetSiblings());
   5946    }
   5947    for (auto& listener : mActionListeners.Clone()) {
   5948      for (const OwningNonNull<nsIContent>& movedContent :
   5949           movedSiblings.ref()) {
   5950        DebugOnly<nsresult> rvIgnored =
   5951            listener->DidDeleteNode(MOZ_KnownLive(movedContent), rv);
   5952        NS_WARNING_ASSERTION(
   5953            NS_SUCCEEDED(rvIgnored),
   5954            "nsIEditActionListener::DidDeleteNode() failed, but ignored");
   5955      }
   5956    }
   5957  }
   5958 
   5959  if (MOZ_UNLIKELY(Destroyed())) {
   5960    NS_WARNING(
   5961        "MoveNodeTransaction::DoTransaction() caused destroying the editor");
   5962    return Err(NS_ERROR_EDITOR_DESTROYED);
   5963  }
   5964 
   5965  if (NS_FAILED(rv)) {
   5966    NS_WARNING("MoveNodeTransaction::DoTransaction() failed");
   5967    return Err(rv);
   5968  }
   5969 
   5970  nsIContent* const firstMovedContentInExpectedContainer =
   5971      moveSiblingsTransaction->GetFirstMovedContent();
   5972  nsIContent* const lastMovedContentInExpectedContainer =
   5973      moveSiblingsTransaction->GetLastMovedContent();
   5974  if (!firstMovedContentInExpectedContainer) {
   5975    return MoveNodeResult::IgnoredResult(aPointToInsert);
   5976  }
   5977  MOZ_ASSERT(lastMovedContentInExpectedContainer);
   5978 
   5979  // DidInsertContent will extend the changed range to contain all moved
   5980  // contents.  Therefore, we need to call it only for the first and and the
   5981  // last one.
   5982  TopLevelEditSubActionDataRef().DidInsertContent(
   5983      *this, *firstMovedContentInExpectedContainer);
   5984  if (firstMovedContentInExpectedContainer ==
   5985      lastMovedContentInExpectedContainer) {
   5986    // Only one node was moved.
   5987    return MoveNodeResult::HandledResult(
   5988        moveSiblingsTransaction->SuggestNextInsertionPoint()
   5989            .To<EditorDOMPoint>(),
   5990        moveSiblingsTransaction->SuggestPointToPutCaret().To<EditorDOMPoint>());
   5991  }
   5992  TopLevelEditSubActionDataRef().DidInsertContent(
   5993      *this, *lastMovedContentInExpectedContainer);
   5994  return MoveNodeResult::HandledResult(
   5995      *firstMovedContentInExpectedContainer,
   5996      moveSiblingsTransaction->SuggestNextInsertionPoint().To<EditorDOMPoint>(),
   5997      moveSiblingsTransaction->SuggestPointToPutCaret().To<EditorDOMPoint>());
   5998 }
   5999 
   6000 Result<RefPtr<Element>, nsresult> HTMLEditor::DeleteSelectionAndCreateElement(
   6001    nsAtom& aTag, const InitializeInsertingElement& aInitializer) {
   6002  MOZ_ASSERT(IsEditActionDataAvailable());
   6003 
   6004  nsresult rv = DeleteSelectionAndPrepareToCreateNode();
   6005  if (NS_FAILED(rv)) {
   6006    NS_WARNING("HTMLEditor::DeleteSelectionAndPrepareToCreateNode() failed");
   6007    return Err(rv);
   6008  }
   6009 
   6010  EditorDOMPoint pointToInsert(SelectionRef().AnchorRef());
   6011  if (!pointToInsert.IsSet()) {
   6012    return Err(NS_ERROR_FAILURE);
   6013  }
   6014  Result<CreateElementResult, nsresult> createNewElementResult =
   6015      CreateAndInsertElement(WithTransaction::Yes, aTag, pointToInsert,
   6016                             aInitializer);
   6017  if (MOZ_UNLIKELY(createNewElementResult.isErr())) {
   6018    NS_WARNING(
   6019        "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
   6020    return createNewElementResult.propagateErr();
   6021  }
   6022  MOZ_ASSERT(createNewElementResult.inspect().GetNewNode());
   6023 
   6024  // We want the selection to be just after the new node
   6025  createNewElementResult.inspect().IgnoreCaretPointSuggestion();
   6026  rv = CollapseSelectionTo(
   6027      EditorRawDOMPoint::After(*createNewElementResult.inspect().GetNewNode()));
   6028  if (NS_FAILED(rv)) {
   6029    NS_WARNING("EditorBase::CollapseSelectionTo() failed");
   6030    return Err(rv);
   6031  }
   6032  return createNewElementResult.unwrap().UnwrapNewNode();
   6033 }
   6034 
   6035 nsresult HTMLEditor::DeleteSelectionAndPrepareToCreateNode() {
   6036  MOZ_ASSERT(IsEditActionDataAvailable());
   6037 
   6038  if (NS_WARN_IF(!SelectionRef().GetAnchorFocusRange())) {
   6039    return NS_OK;
   6040  }
   6041 
   6042  if (!SelectionRef().GetAnchorFocusRange()->Collapsed()) {
   6043    nsresult rv =
   6044        DeleteSelectionAsSubAction(nsIEditor::eNone, nsIEditor::eStrip);
   6045    if (NS_FAILED(rv)) {
   6046      NS_WARNING("EditorBase::DeleteSelectionAsSubAction() failed");
   6047      return rv;
   6048    }
   6049    MOZ_ASSERT(SelectionRef().GetAnchorFocusRange() &&
   6050                   SelectionRef().GetAnchorFocusRange()->Collapsed(),
   6051               "Selection not collapsed after delete");
   6052  }
   6053 
   6054  // If the selection is a chardata node, split it if necessary and compute
   6055  // where to put the new node
   6056  EditorDOMPoint atAnchor(SelectionRef().AnchorRef());
   6057  if (NS_WARN_IF(!atAnchor.IsSet()) || !atAnchor.IsInDataNode()) {
   6058    return NS_OK;
   6059  }
   6060 
   6061  if (NS_WARN_IF(!atAnchor.GetContainerParent())) {
   6062    return NS_ERROR_FAILURE;
   6063  }
   6064 
   6065  if (atAnchor.IsStartOfContainer()) {
   6066    const EditorRawDOMPoint atAnchorContainer(atAnchor.GetContainer());
   6067    if (NS_WARN_IF(!atAnchorContainer.IsSetAndValid())) {
   6068      return NS_ERROR_FAILURE;
   6069    }
   6070    nsresult rv = CollapseSelectionTo(atAnchorContainer);
   6071    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6072                         "EditorBase::CollapseSelectionTo() failed");
   6073    return rv;
   6074  }
   6075 
   6076  if (atAnchor.IsEndOfContainer()) {
   6077    EditorRawDOMPoint afterAnchorContainer(atAnchor.GetContainer());
   6078    if (NS_WARN_IF(!afterAnchorContainer.AdvanceOffset())) {
   6079      return NS_ERROR_FAILURE;
   6080    }
   6081    nsresult rv = CollapseSelectionTo(afterAnchorContainer);
   6082    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6083                         "EditorBase::CollapseSelectionTo() failed");
   6084    return rv;
   6085  }
   6086 
   6087  Result<SplitNodeResult, nsresult> splitAtAnchorResult =
   6088      SplitNodeWithTransaction(atAnchor);
   6089  if (MOZ_UNLIKELY(splitAtAnchorResult.isErr())) {
   6090    NS_WARNING("HTMLEditor::SplitNodeWithTransaction() failed");
   6091    return splitAtAnchorResult.unwrapErr();
   6092  }
   6093 
   6094  splitAtAnchorResult.inspect().IgnoreCaretPointSuggestion();
   6095  const auto atRightContent =
   6096      splitAtAnchorResult.inspect().AtNextContent<EditorRawDOMPoint>();
   6097  if (NS_WARN_IF(!atRightContent.IsSet())) {
   6098    return NS_ERROR_FAILURE;
   6099  }
   6100  MOZ_ASSERT(atRightContent.IsSetAndValid());
   6101  nsresult rv = CollapseSelectionTo(atRightContent);
   6102  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6103                       "EditorBase::CollapseSelectionTo() failed");
   6104  return rv;
   6105 }
   6106 
   6107 bool HTMLEditor::IsEmpty() const {
   6108  if (mPaddingBRElementForEmptyEditor) {
   6109    return true;
   6110  }
   6111 
   6112  const Element* activeElement =
   6113      GetDocument() ? GetDocument()->GetActiveElement() : nullptr;
   6114  const Element* editingHostOrBodyOrRootElement =
   6115      activeElement && activeElement->IsEditable()
   6116          ? ComputeEditingHost(*activeElement, LimitInBodyElement::No)
   6117          : ComputeEditingHost(LimitInBodyElement::No);
   6118  if (MOZ_UNLIKELY(!editingHostOrBodyOrRootElement)) {
   6119    // If there is no active element nor no selection range in the document,
   6120    // let's check entire the document as what we do traditionally.
   6121    editingHostOrBodyOrRootElement = GetRoot();
   6122    if (!editingHostOrBodyOrRootElement) {
   6123      return true;
   6124    }
   6125  }
   6126 
   6127  for (nsIContent* childContent =
   6128           editingHostOrBodyOrRootElement->GetFirstChild();
   6129       childContent; childContent = childContent->GetNextSibling()) {
   6130    if (!childContent->IsText() || childContent->Length()) {
   6131      return false;
   6132    }
   6133  }
   6134  return true;
   6135 }
   6136 
   6137 // add to aElement the CSS inline styles corresponding to the HTML attribute
   6138 // aAttribute with its value aValue
   6139 nsresult HTMLEditor::SetAttributeOrEquivalent(Element* aElement,
   6140                                              nsAtom* aAttribute,
   6141                                              const nsAString& aValue,
   6142                                              bool aSuppressTransaction) {
   6143  MOZ_ASSERT(aElement);
   6144  MOZ_ASSERT(aAttribute);
   6145 
   6146  nsAutoScriptBlocker scriptBlocker;
   6147  nsStyledElement* styledElement = nsStyledElement::FromNodeOrNull(aElement);
   6148  if (!IsCSSEnabled()) {
   6149    // we are not in an HTML+CSS editor; let's set the attribute the HTML way
   6150    if (EditorElementStyle::IsHTMLStyle(aAttribute)) {
   6151      const EditorElementStyle elementStyle =
   6152          EditorElementStyle::Create(*aAttribute);
   6153      if (styledElement && elementStyle.IsCSSRemovable(*styledElement)) {
   6154        // MOZ_KnownLive(*styledElement): It's aElement and its lifetime must
   6155        // be guaranteed by the caller because of MOZ_CAN_RUN_SCRIPT method.
   6156        nsresult rv = CSSEditUtils::RemoveCSSEquivalentToStyle(
   6157            aSuppressTransaction ? WithTransaction::No : WithTransaction::Yes,
   6158            *this, MOZ_KnownLive(*styledElement), elementStyle, nullptr);
   6159        if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
   6160          return NS_ERROR_EDITOR_DESTROYED;
   6161        }
   6162        NS_WARNING_ASSERTION(
   6163            NS_SUCCEEDED(rv),
   6164            "CSSEditUtils::RemoveCSSEquivalentToStyle() failed, but ignored");
   6165      }
   6166    }
   6167    if (aSuppressTransaction) {
   6168      AutoElementAttrAPIWrapper elementWrapper(*this, *aElement);
   6169      nsresult rv = elementWrapper.SetAttr(aAttribute, aValue, true);
   6170      if (NS_FAILED(rv)) {
   6171        NS_WARNING("AutoElementAttrAPIWrapper::SetAttr() failed");
   6172        return rv;
   6173      }
   6174      NS_WARNING_ASSERTION(
   6175          elementWrapper.IsExpectedResult(aValue),
   6176          "Setting attribute caused other mutations, but ignored");
   6177      return NS_OK;
   6178    }
   6179    nsresult rv = SetAttributeWithTransaction(*aElement, *aAttribute, aValue);
   6180    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6181                         "EditorBase::SetAttributeWithTransaction() failed");
   6182    return rv;
   6183  }
   6184 
   6185  if (EditorElementStyle::IsHTMLStyle(aAttribute)) {
   6186    const EditorElementStyle elementStyle =
   6187        EditorElementStyle::Create(*aAttribute);
   6188    if (styledElement && elementStyle.IsCSSSettable(*styledElement)) {
   6189      // MOZ_KnownLive(*styledElement): It's aElement and its lifetime must
   6190      // be guaranteed by the caller because of MOZ_CAN_RUN_SCRIPT method.
   6191      Result<size_t, nsresult> count = CSSEditUtils::SetCSSEquivalentToStyle(
   6192          aSuppressTransaction ? WithTransaction::No : WithTransaction::Yes,
   6193          *this, MOZ_KnownLive(*styledElement), elementStyle, &aValue);
   6194      if (MOZ_UNLIKELY(count.isErr())) {
   6195        if (NS_WARN_IF(count.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6196          return NS_ERROR_EDITOR_DESTROYED;
   6197        }
   6198        NS_WARNING(
   6199            "CSSEditUtils::SetCSSEquivalentToStyle() failed, but ignored");
   6200      }
   6201      if (count.inspect()) {
   6202        // we found an equivalence ; let's remove the HTML attribute itself if
   6203        // it is set
   6204        nsAutoString existingValue;
   6205        if (!aElement->GetAttr(aAttribute, existingValue)) {
   6206          return NS_OK;
   6207        }
   6208 
   6209        if (aSuppressTransaction) {
   6210          AutoElementAttrAPIWrapper elementWrapper(*this, *aElement);
   6211          nsresult rv = elementWrapper.UnsetAttr(aAttribute, true);
   6212          if (NS_FAILED(rv)) {
   6213            NS_WARNING("AutoElementAttrAPIWrapper::UnsetAttr() failed");
   6214            return rv;
   6215          }
   6216          NS_WARNING_ASSERTION(
   6217              elementWrapper.IsExpectedResult(EmptyString()),
   6218              "Removing attribute caused other mutations, but ignored");
   6219          return NS_OK;
   6220        }
   6221        nsresult rv = RemoveAttributeWithTransaction(*aElement, *aAttribute);
   6222        NS_WARNING_ASSERTION(
   6223            NS_SUCCEEDED(rv),
   6224            "EditorBase::RemoveAttributeWithTransaction() failed");
   6225        return rv;
   6226      }
   6227    }
   6228  }
   6229 
   6230  // count is an integer that represents the number of CSS declarations
   6231  // applied to the element. If it is zero, we found no equivalence in this
   6232  // implementation for the attribute
   6233  if (aAttribute == nsGkAtoms::style) {
   6234    // if it is the style attribute, just add the new value to the existing
   6235    // style attribute's value
   6236    nsString existingValue;  // Use nsString to avoid copying the string
   6237                             // buffer at setting the attribute below.
   6238    aElement->GetAttr(nsGkAtoms::style, existingValue);
   6239    if (!existingValue.IsEmpty()) {
   6240      existingValue.Append(HTMLEditUtils::kSpace);
   6241    }
   6242    existingValue.Append(aValue);
   6243    if (aSuppressTransaction) {
   6244      AutoElementAttrAPIWrapper elementWrapper(*this, *aElement);
   6245      nsresult rv =
   6246          elementWrapper.SetAttr(nsGkAtoms::style, existingValue, true);
   6247      if (NS_FAILED(rv)) {
   6248        NS_WARNING("AutoElementAttrAPIWrapper::SetAttr() failed");
   6249        return rv;
   6250      }
   6251      NS_WARNING_ASSERTION(
   6252          elementWrapper.IsExpectedResult(existingValue),
   6253          "Setting style attribute caused other mutations, but ignored");
   6254      return NS_OK;
   6255    }
   6256    nsresult rv = SetAttributeWithTransaction(*aElement, *nsGkAtoms::style,
   6257                                              existingValue);
   6258    NS_WARNING_ASSERTION(
   6259        NS_SUCCEEDED(rv),
   6260        "EditorBase::SetAttributeWithTransaction(nsGkAtoms::style) failed");
   6261    return rv;
   6262  }
   6263 
   6264  // we have no CSS equivalence for this attribute and it is not the style
   6265  // attribute; let's set it the good'n'old HTML way
   6266  if (aSuppressTransaction) {
   6267    AutoElementAttrAPIWrapper elementWrapper(*this, *aElement);
   6268    nsresult rv = elementWrapper.SetAttr(aAttribute, aValue, true);
   6269    if (NS_FAILED(rv)) {
   6270      NS_WARNING("AutoElementAttrAPIWrapper::SetAttr() failed");
   6271      return rv;
   6272    }
   6273    NS_WARNING_ASSERTION(
   6274        elementWrapper.IsExpectedResult(aValue),
   6275        "Setting attribute caused other mutations, but ignored");
   6276    return NS_OK;
   6277  }
   6278  nsresult rv = SetAttributeWithTransaction(*aElement, *aAttribute, aValue);
   6279  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6280                       "EditorBase::SetAttributeWithTransaction() failed");
   6281  return rv;
   6282 }
   6283 
   6284 nsresult HTMLEditor::RemoveAttributeOrEquivalent(Element* aElement,
   6285                                                 nsAtom* aAttribute,
   6286                                                 bool aSuppressTransaction) {
   6287  MOZ_ASSERT(aElement);
   6288  MOZ_ASSERT(aAttribute);
   6289 
   6290  if (IsCSSEnabled() && EditorElementStyle::IsHTMLStyle(aAttribute)) {
   6291    const EditorElementStyle elementStyle =
   6292        EditorElementStyle::Create(*aAttribute);
   6293    if (elementStyle.IsCSSRemovable(*aElement)) {
   6294      // XXX It might be keep handling attribute even if aElement is not
   6295      //     an nsStyledElement instance.
   6296      nsStyledElement* styledElement =
   6297          nsStyledElement::FromNodeOrNull(aElement);
   6298      if (NS_WARN_IF(!styledElement)) {
   6299        return NS_ERROR_INVALID_ARG;
   6300      }
   6301      // MOZ_KnownLive(*styledElement): It's aElement and its lifetime must
   6302      // be guaranteed by the caller because of MOZ_CAN_RUN_SCRIPT method.
   6303      nsresult rv = CSSEditUtils::RemoveCSSEquivalentToStyle(
   6304          aSuppressTransaction ? WithTransaction::No : WithTransaction::Yes,
   6305          *this, MOZ_KnownLive(*styledElement), elementStyle, nullptr);
   6306      if (NS_FAILED(rv)) {
   6307        NS_WARNING("CSSEditUtils::RemoveCSSEquivalentToStyle() failed");
   6308        return rv;
   6309      }
   6310    }
   6311  }
   6312 
   6313  if (!aElement->HasAttr(aAttribute)) {
   6314    return NS_OK;
   6315  }
   6316 
   6317  if (aSuppressTransaction) {
   6318    AutoElementAttrAPIWrapper elementWrapper(*this, *aElement);
   6319    nsresult rv = elementWrapper.UnsetAttr(aAttribute, true);
   6320    if (NS_FAILED(rv)) {
   6321      NS_WARNING("AutoElementAttrAPIWrapper::UnsetAttr() failed");
   6322      return rv;
   6323    }
   6324    NS_WARNING_ASSERTION(
   6325        elementWrapper.IsExpectedResult(EmptyString()),
   6326        "Removing attribute caused other mutations, but ignored");
   6327    return NS_OK;
   6328  }
   6329  nsresult rv = RemoveAttributeWithTransaction(*aElement, *aAttribute);
   6330  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6331                       "EditorBase::RemoveAttributeWithTransaction() failed");
   6332  return rv;
   6333 }
   6334 
   6335 NS_IMETHODIMP HTMLEditor::SetIsCSSEnabled(bool aIsCSSPrefChecked) {
   6336  AutoEditActionDataSetter editActionData(*this,
   6337                                          EditAction::eEnableOrDisableCSS);
   6338  if (NS_WARN_IF(!editActionData.CanHandle())) {
   6339    return NS_ERROR_NOT_INITIALIZED;
   6340  }
   6341 
   6342  mIsCSSPrefChecked = aIsCSSPrefChecked;
   6343  return NS_OK;
   6344 }
   6345 
   6346 // Set the block background color
   6347 nsresult HTMLEditor::SetBlockBackgroundColorWithCSSAsSubAction(
   6348    const nsAString& aColor) {
   6349  MOZ_ASSERT(IsEditActionDataAvailable());
   6350 
   6351  // background-color change and committing composition should be undone
   6352  // together
   6353  AutoPlaceholderBatch treatAsOneTransaction(
   6354      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   6355 
   6356  CommitComposition();
   6357 
   6358  // XXX Shouldn't we do this before calling `CommitComposition()`?
   6359  if (IsPlaintextMailComposer()) {
   6360    return NS_OK;
   6361  }
   6362 
   6363  {
   6364    Result<EditActionResult, nsresult> result = CanHandleHTMLEditSubAction();
   6365    if (MOZ_UNLIKELY(result.isErr())) {
   6366      NS_WARNING("HTMLEditor::CanHandleHTMLEditSubAction() failed");
   6367      return result.unwrapErr();
   6368    }
   6369    if (result.inspect().Canceled()) {
   6370      return NS_OK;
   6371    }
   6372  }
   6373 
   6374  IgnoredErrorResult ignoredError;
   6375  AutoEditSubActionNotifier startToHandleEditSubAction(
   6376      *this, EditSubAction::eInsertElement, nsIEditor::eNext, ignoredError);
   6377  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
   6378    return ignoredError.StealNSResult();
   6379  }
   6380  NS_WARNING_ASSERTION(!ignoredError.Failed(),
   6381                       "HTMLEditor::OnStartToHandleTopLevelEditSubAction() "
   6382                       "failed, but ignored");
   6383 
   6384  // TODO: We don't need AutoTransactionsConserveSelection here in the normal
   6385  //       cases, but removing this may cause the behavior with the legacy
   6386  //       mutation event listeners.  We should try to delete this in a bug.
   6387  AutoTransactionsConserveSelection dontChangeMySelection(*this);
   6388 
   6389  AutoClonedSelectionRangeArray selectionRanges(SelectionRef());
   6390  MOZ_ALWAYS_TRUE(selectionRanges.SaveAndTrackRanges(*this));
   6391  for (const OwningNonNull<nsRange>& domRange : selectionRanges.Ranges()) {
   6392    EditorDOMRange range(domRange);
   6393    if (NS_WARN_IF(!range.IsPositioned())) {
   6394      continue;
   6395    }
   6396 
   6397    if (range.InSameContainer()) {
   6398      // If the range is in a text node, set background color of its parent
   6399      // block.
   6400      if (range.StartRef().IsInTextNode()) {
   6401        const RefPtr<nsStyledElement> editableBlockStyledElement =
   6402            nsStyledElement::FromNodeOrNull(HTMLEditUtils::GetAncestorElement(
   6403                *range.StartRef().ContainerAs<Text>(),
   6404                HTMLEditUtils::ClosestEditableBlockElement,
   6405                BlockInlineCheck::UseComputedDisplayOutsideStyle));
   6406        if (!editableBlockStyledElement ||
   6407            !EditorElementStyle::BGColor().IsCSSSettable(
   6408                *editableBlockStyledElement)) {
   6409          continue;
   6410        }
   6411        Result<size_t, nsresult> result = CSSEditUtils::SetCSSEquivalentToStyle(
   6412            WithTransaction::Yes, *this, *editableBlockStyledElement,
   6413            EditorElementStyle::BGColor(), &aColor);
   6414        if (MOZ_UNLIKELY(result.isErr())) {
   6415          if (NS_WARN_IF(result.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6416            return NS_ERROR_EDITOR_DESTROYED;
   6417          }
   6418          NS_WARNING(
   6419              "CSSEditUtils::SetCSSEquivalentToStyle(EditorElementStyle::"
   6420              "BGColor()) failed, but ignored");
   6421        }
   6422        continue;
   6423      }
   6424 
   6425      // If `Selection` is collapsed in a `<body>` element, set background
   6426      // color of the `<body>` element.
   6427      if (range.Collapsed() &&
   6428          range.StartRef().IsContainerHTMLElement(nsGkAtoms::body)) {
   6429        const RefPtr<nsStyledElement> styledElement =
   6430            range.StartRef().GetContainerAs<nsStyledElement>();
   6431        if (!styledElement ||
   6432            !EditorElementStyle::BGColor().IsCSSSettable(*styledElement)) {
   6433          continue;
   6434        }
   6435        Result<size_t, nsresult> result = CSSEditUtils::SetCSSEquivalentToStyle(
   6436            WithTransaction::Yes, *this, *styledElement,
   6437            EditorElementStyle::BGColor(), &aColor);
   6438        if (MOZ_UNLIKELY(result.isErr())) {
   6439          if (NS_WARN_IF(result.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6440            return NS_ERROR_EDITOR_DESTROYED;
   6441          }
   6442          NS_WARNING(
   6443              "CSSEditUtils::SetCSSEquivalentToStyle(EditorElementStyle::"
   6444              "BGColor()) failed, but ignored");
   6445        }
   6446        continue;
   6447      }
   6448 
   6449      // If one node is selected, set background color of it if it's a
   6450      // block, or of its parent block otherwise.
   6451      if ((range.StartRef().IsStartOfContainer() &&
   6452           range.EndRef().IsStartOfContainer()) ||
   6453          range.StartRef().Offset() + 1 == range.EndRef().Offset()) {
   6454        if (NS_WARN_IF(range.StartRef().IsInDataNode())) {
   6455          continue;
   6456        }
   6457        const RefPtr<nsStyledElement> editableBlockStyledElement =
   6458            nsStyledElement::FromNodeOrNull(
   6459                HTMLEditUtils::GetInclusiveAncestorElement(
   6460                    *range.StartRef().GetChild(),
   6461                    HTMLEditUtils::ClosestEditableBlockElement,
   6462                    BlockInlineCheck::UseComputedDisplayOutsideStyle));
   6463        if (!editableBlockStyledElement ||
   6464            !EditorElementStyle::BGColor().IsCSSSettable(
   6465                *editableBlockStyledElement)) {
   6466          continue;
   6467        }
   6468        Result<size_t, nsresult> result = CSSEditUtils::SetCSSEquivalentToStyle(
   6469            WithTransaction::Yes, *this, *editableBlockStyledElement,
   6470            EditorElementStyle::BGColor(), &aColor);
   6471        if (MOZ_UNLIKELY(result.isErr())) {
   6472          if (NS_WARN_IF(result.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6473            return NS_ERROR_EDITOR_DESTROYED;
   6474          }
   6475          NS_WARNING(
   6476              "CSSEditUtils::SetCSSEquivalentToStyle(EditorElementStyle::"
   6477              "BGColor()) failed, but ignored");
   6478        }
   6479        continue;
   6480      }
   6481    }  // if (range.InSameContainer())
   6482 
   6483    // Collect editable nodes which are entirely contained in the range.
   6484    AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
   6485    {
   6486      ContentSubtreeIterator subtreeIter;
   6487      // If there is no node which is entirely in the range,
   6488      // `ContentSubtreeIterator::Init()` fails, but this is possible case,
   6489      // don't warn it.
   6490      nsresult rv = subtreeIter.Init(range.StartRef().ToRawRangeBoundary(),
   6491                                     range.EndRef().ToRawRangeBoundary());
   6492      NS_WARNING_ASSERTION(
   6493          NS_SUCCEEDED(rv),
   6494          "ContentSubtreeIterator::Init() failed, but ignored");
   6495      if (NS_SUCCEEDED(rv)) {
   6496        for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
   6497          nsINode* node = subtreeIter.GetCurrentNode();
   6498          if (NS_WARN_IF(!node)) {
   6499            return NS_ERROR_FAILURE;
   6500          }
   6501          if (node->IsContent() && EditorUtils::IsEditableContent(
   6502                                       *node->AsContent(), EditorType::HTML)) {
   6503            arrayOfContents.AppendElement(*node->AsContent());
   6504          }
   6505        }
   6506      }
   6507    }
   6508 
   6509    // This caches block parent if we set its background color.
   6510    RefPtr<Element> handledBlockParent;
   6511 
   6512    // If start node is a text node, set background color of its parent
   6513    // block.
   6514    if (range.StartRef().IsInTextNode() &&
   6515        EditorUtils::IsEditableContent(*range.StartRef().ContainerAs<Text>(),
   6516                                       EditorType::HTML)) {
   6517      Element* const editableBlockElement = HTMLEditUtils::GetAncestorElement(
   6518          *range.StartRef().ContainerAs<Text>(),
   6519          HTMLEditUtils::ClosestEditableBlockElement,
   6520          BlockInlineCheck::UseComputedDisplayOutsideStyle);
   6521      if (editableBlockElement && handledBlockParent != editableBlockElement) {
   6522        handledBlockParent = editableBlockElement;
   6523        nsStyledElement* const blockStyledElement =
   6524            nsStyledElement::FromNode(handledBlockParent);
   6525        if (blockStyledElement &&
   6526            EditorElementStyle::BGColor().IsCSSSettable(*blockStyledElement)) {
   6527          // MOZ_KnownLive(*blockStyledElement): It's handledBlockParent
   6528          // whose type is RefPtr.
   6529          Result<size_t, nsresult> result =
   6530              CSSEditUtils::SetCSSEquivalentToStyle(
   6531                  WithTransaction::Yes, *this,
   6532                  MOZ_KnownLive(*blockStyledElement),
   6533                  EditorElementStyle::BGColor(), &aColor);
   6534          if (MOZ_UNLIKELY(result.isErr())) {
   6535            if (NS_WARN_IF(result.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6536              return NS_ERROR_EDITOR_DESTROYED;
   6537            }
   6538            NS_WARNING(
   6539                "CSSEditUtils::SetCSSEquivalentToStyle(EditorElementStyle::"
   6540                "BGColor()) failed, but ignored");
   6541          }
   6542        }
   6543      }
   6544    }
   6545 
   6546    // Then, set background color of each block or block parent of all nodes
   6547    // in the range entirely.
   6548    for (OwningNonNull<nsIContent>& content : arrayOfContents) {
   6549      Element* const editableBlockElement =
   6550          HTMLEditUtils::GetInclusiveAncestorElement(
   6551              content, HTMLEditUtils::ClosestEditableBlockElement,
   6552              BlockInlineCheck::UseComputedDisplayOutsideStyle);
   6553      if (editableBlockElement && handledBlockParent != editableBlockElement) {
   6554        handledBlockParent = editableBlockElement;
   6555        nsStyledElement* const blockStyledElement =
   6556            nsStyledElement::FromNode(handledBlockParent);
   6557        if (blockStyledElement &&
   6558            EditorElementStyle::BGColor().IsCSSSettable(*blockStyledElement)) {
   6559          // MOZ_KnownLive(*blockStyledElement): It's handledBlockParent whose
   6560          // type is RefPtr.
   6561          Result<size_t, nsresult> result =
   6562              CSSEditUtils::SetCSSEquivalentToStyle(
   6563                  WithTransaction::Yes, *this,
   6564                  MOZ_KnownLive(*blockStyledElement),
   6565                  EditorElementStyle::BGColor(), &aColor);
   6566          if (MOZ_UNLIKELY(result.isErr())) {
   6567            if (NS_WARN_IF(result.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6568              return NS_ERROR_EDITOR_DESTROYED;
   6569            }
   6570            NS_WARNING(
   6571                "CSSEditUtils::SetCSSEquivalentToStyle(EditorElementStyle::"
   6572                "BGColor()) failed, but ignored");
   6573          }
   6574        }
   6575      }
   6576    }
   6577 
   6578    // Finally, if end node is a text node, set background color of its
   6579    // parent block.
   6580    if (range.EndRef().IsInTextNode() &&
   6581        EditorUtils::IsEditableContent(*range.EndRef().ContainerAs<Text>(),
   6582                                       EditorType::HTML)) {
   6583      Element* const editableBlockElement = HTMLEditUtils::GetAncestorElement(
   6584          *range.EndRef().ContainerAs<Text>(),
   6585          HTMLEditUtils::ClosestEditableBlockElement,
   6586          BlockInlineCheck::UseComputedDisplayOutsideStyle);
   6587      if (editableBlockElement && handledBlockParent != editableBlockElement) {
   6588        const RefPtr<nsStyledElement> blockStyledElement =
   6589            nsStyledElement::FromNode(editableBlockElement);
   6590        if (blockStyledElement &&
   6591            EditorElementStyle::BGColor().IsCSSSettable(*blockStyledElement)) {
   6592          Result<size_t, nsresult> result =
   6593              CSSEditUtils::SetCSSEquivalentToStyle(
   6594                  WithTransaction::Yes, *this, *blockStyledElement,
   6595                  EditorElementStyle::BGColor(), &aColor);
   6596          if (MOZ_UNLIKELY(result.isErr())) {
   6597            if (NS_WARN_IF(result.inspectErr() == NS_ERROR_EDITOR_DESTROYED)) {
   6598              return NS_ERROR_EDITOR_DESTROYED;
   6599            }
   6600            NS_WARNING(
   6601                "CSSEditUtils::SetCSSEquivalentToStyle(EditorElementStyle::"
   6602                "BGColor()) failed, but ignored");
   6603          }
   6604        }
   6605      }
   6606    }
   6607  }  // for-loop of selectionRanges
   6608 
   6609  MOZ_ASSERT(selectionRanges.HasSavedRanges());
   6610  selectionRanges.RestoreFromSavedRanges();
   6611  nsresult rv = selectionRanges.ApplyTo(SelectionRef());
   6612  if (NS_WARN_IF(Destroyed())) {
   6613    return NS_ERROR_EDITOR_DESTROYED;
   6614  }
   6615  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6616                       "AutoClonedSelectionRangeArray::ApplyTo() failed");
   6617  return rv;
   6618 }
   6619 
   6620 NS_IMETHODIMP HTMLEditor::SetBackgroundColor(const nsAString& aColor) {
   6621  nsresult rv = SetBackgroundColorAsAction(aColor);
   6622  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6623                       "HTMLEditor::SetBackgroundColorAsAction() failed");
   6624  return rv;
   6625 }
   6626 
   6627 nsresult HTMLEditor::SetBackgroundColorAsAction(const nsAString& aColor,
   6628                                                nsIPrincipal* aPrincipal) {
   6629  AutoEditActionDataSetter editActionData(
   6630      *this, EditAction::eSetBackgroundColor, aPrincipal);
   6631  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
   6632  if (NS_FAILED(rv)) {
   6633    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
   6634                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
   6635    return EditorBase::ToGenericNSResult(rv);
   6636  }
   6637 
   6638  if (IsCSSEnabled()) {
   6639    // if we are in CSS mode, we have to apply the background color to the
   6640    // containing block (or the body if we have no block-level element in
   6641    // the document)
   6642    nsresult rv = SetBlockBackgroundColorWithCSSAsSubAction(aColor);
   6643    NS_WARNING_ASSERTION(
   6644        NS_SUCCEEDED(rv),
   6645        "HTMLEditor::SetBlockBackgroundColorWithCSSAsSubAction() failed");
   6646    return EditorBase::ToGenericNSResult(rv);
   6647  }
   6648 
   6649  // but in HTML mode, we can only set the document's background color
   6650  rv = SetHTMLBackgroundColorWithTransaction(aColor);
   6651  NS_WARNING_ASSERTION(
   6652      NS_SUCCEEDED(rv),
   6653      "HTMLEditor::SetHTMLBackgroundColorWithTransaction() failed");
   6654  return EditorBase::ToGenericNSResult(rv);
   6655 }
   6656 
   6657 Result<EditorDOMPoint, nsresult>
   6658 HTMLEditor::CopyLastEditableChildStylesWithTransaction(
   6659    Element& aPreviousBlock, Element& aNewBlock, const Element& aEditingHost) {
   6660  MOZ_ASSERT(IsEditActionDataAvailable());
   6661 
   6662  // First, clear out aNewBlock.  Contract is that we want only the styles
   6663  // from aPreviousBlock.
   6664  AutoTArray<OwningNonNull<nsIContent>, 32> newBlockChildren;
   6665  HTMLEditUtils::CollectAllChildren(aNewBlock, newBlockChildren);
   6666  for (const OwningNonNull<nsIContent>& child : newBlockChildren) {
   6667    // MOZ_KNownLive(child) because of bug 1622253
   6668    nsresult rv = DeleteNodeWithTransaction(MOZ_KnownLive(child));
   6669    if (NS_FAILED(rv)) {
   6670      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   6671      return Err(rv);
   6672    }
   6673  }
   6674  if (MOZ_UNLIKELY(aNewBlock.GetFirstChild())) {
   6675    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   6676  }
   6677 
   6678  // XXX aNewBlock may be moved or removed.  Even in such case, we should
   6679  //     keep cloning the styles?
   6680 
   6681  // Look for the deepest last editable leaf node in aPreviousBlock.
   6682  // Then, if found one is a <br> element, look for non-<br> element.
   6683  nsIContent* deepestEditableContent = nullptr;
   6684  for (nsCOMPtr<nsIContent> child = &aPreviousBlock; child;
   6685       child = HTMLEditUtils::GetLastChild(
   6686           *child, {WalkTreeOption::IgnoreNonEditableNode})) {
   6687    deepestEditableContent = child;
   6688  }
   6689  while (deepestEditableContent &&
   6690         deepestEditableContent->IsHTMLElement(nsGkAtoms::br)) {
   6691    deepestEditableContent = HTMLEditUtils::GetPreviousContent(
   6692        *deepestEditableContent, {WalkTreeOption::IgnoreNonEditableNode},
   6693        BlockInlineCheck::UseComputedDisplayOutsideStyle, &aEditingHost);
   6694  }
   6695  if (!deepestEditableContent) {
   6696    return EditorDOMPoint(&aNewBlock, 0u);
   6697  }
   6698 
   6699  Element* deepestVisibleEditableElement =
   6700      deepestEditableContent->GetAsElementOrParentElement();
   6701  if (!deepestVisibleEditableElement) {
   6702    return EditorDOMPoint(&aNewBlock, 0u);
   6703  }
   6704 
   6705  // Clone inline elements to keep current style in the new block.
   6706  // XXX Looks like that this is really slow if lastEditableDescendant is
   6707  //     far from aPreviousBlock.  Probably, we should clone inline containers
   6708  //     from ancestor to descendants without transactions, then, insert it
   6709  //     after that with transaction.
   6710  RefPtr<Element> lastClonedElement, firstClonedElement;
   6711  for (RefPtr<Element> elementInPreviousBlock = deepestVisibleEditableElement;
   6712       elementInPreviousBlock && elementInPreviousBlock != &aPreviousBlock;
   6713       elementInPreviousBlock = elementInPreviousBlock->GetParentElement()) {
   6714    if (!HTMLEditUtils::IsInlineStyleElement(*elementInPreviousBlock) &&
   6715        !elementInPreviousBlock->IsHTMLElement(nsGkAtoms::span)) {
   6716      continue;
   6717    }
   6718    OwningNonNull<nsAtom> tagName =
   6719        *elementInPreviousBlock->NodeInfo()->NameAtom();
   6720    // At first time, just create the most descendant inline container
   6721    // element.
   6722    if (!firstClonedElement) {
   6723      Result<CreateElementResult, nsresult> createNewElementResult =
   6724          CreateAndInsertElement(
   6725              WithTransaction::Yes, tagName, EditorDOMPoint(&aNewBlock, 0u),
   6726              // MOZ_CAN_RUN_SCRIPT_BOUNDARY due to bug 1758868
   6727              [&elementInPreviousBlock](
   6728                  HTMLEditor& aHTMLEditor, Element& aNewElement,
   6729                  const EditorDOMPoint&) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
   6730                // Clone all attributes.  Note that despite the method name,
   6731                // CloneAttributesWithTransaction does not create
   6732                // transactions in this case because aNewElement has not
   6733                // been connected yet.
   6734                // XXX Looks like that this clones id attribute too.
   6735                aHTMLEditor.CloneAttributesWithTransaction(
   6736                    aNewElement, *elementInPreviousBlock);
   6737                return NS_OK;
   6738              });
   6739      if (MOZ_UNLIKELY(createNewElementResult.isErr())) {
   6740        NS_WARNING(
   6741            "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
   6742        return createNewElementResult.propagateErr();
   6743      }
   6744      CreateElementResult unwrappedCreateNewElementResult =
   6745          createNewElementResult.unwrap();
   6746      // We'll return with a point suggesting new caret position and the
   6747      // following path does not require an update of selection here.
   6748      // Therefore, we don't need to update selection here.
   6749      unwrappedCreateNewElementResult.IgnoreCaretPointSuggestion();
   6750      firstClonedElement = lastClonedElement =
   6751          unwrappedCreateNewElementResult.UnwrapNewNode();
   6752      continue;
   6753    }
   6754    // Otherwise, inserts new parent inline container to the previous inserted
   6755    // inline container.
   6756    Result<CreateElementResult, nsresult> wrapClonedElementResult =
   6757        InsertContainerWithTransaction(*lastClonedElement, tagName);
   6758    if (MOZ_UNLIKELY(wrapClonedElementResult.isErr())) {
   6759      NS_WARNING("HTMLEditor::InsertContainerWithTransaction() failed");
   6760      return wrapClonedElementResult.propagateErr();
   6761    }
   6762    CreateElementResult unwrappedWrapClonedElementResult =
   6763        wrapClonedElementResult.unwrap();
   6764    // We'll return with a point suggesting new caret so that we don't need to
   6765    // update selection here.
   6766    unwrappedWrapClonedElementResult.IgnoreCaretPointSuggestion();
   6767    MOZ_ASSERT(unwrappedWrapClonedElementResult.GetNewNode());
   6768    lastClonedElement = unwrappedWrapClonedElementResult.UnwrapNewNode();
   6769    CloneAttributesWithTransaction(*lastClonedElement, *elementInPreviousBlock);
   6770    if (NS_WARN_IF(Destroyed())) {
   6771      return Err(NS_ERROR_EDITOR_DESTROYED);
   6772    }
   6773  }
   6774 
   6775  if (!firstClonedElement) {
   6776    // XXX Even if no inline elements are cloned, shouldn't we create new
   6777    //     <br> element for aNewBlock?
   6778    return EditorDOMPoint(&aNewBlock, 0u);
   6779  }
   6780 
   6781  Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
   6782      InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement,
   6783                      EditorDOMPoint(firstClonedElement, 0u));
   6784  if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
   6785    NS_WARNING(
   6786        "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   6787        "LineBreakType::BRElement) failed");
   6788    return insertBRElementResultOrError.propagateErr();
   6789  }
   6790  CreateLineBreakResult insertBRElementResult =
   6791      insertBRElementResultOrError.unwrap();
   6792  MOZ_ASSERT(insertBRElementResult.Handled());
   6793  insertBRElementResult.IgnoreCaretPointSuggestion();
   6794  return insertBRElementResult.AtLineBreak<EditorDOMPoint>();
   6795 }
   6796 
   6797 nsresult HTMLEditor::GetElementOrigin(Element& aElement, int32_t& aX,
   6798                                      int32_t& aY) {
   6799  aX = 0;
   6800  aY = 0;
   6801 
   6802  if (NS_WARN_IF(!IsInitialized())) {
   6803    return NS_ERROR_NOT_INITIALIZED;
   6804  }
   6805  PresShell* presShell = GetPresShell();
   6806  if (NS_WARN_IF(!presShell)) {
   6807    return NS_ERROR_NOT_INITIALIZED;
   6808  }
   6809 
   6810  nsIFrame* frame = aElement.GetPrimaryFrame();
   6811  if (NS_WARN_IF(!frame)) {
   6812    return NS_OK;
   6813  }
   6814 
   6815  nsIFrame* absoluteContainerBlockFrame =
   6816      presShell->GetAbsoluteContainingBlock(frame);
   6817  if (NS_WARN_IF(!absoluteContainerBlockFrame)) {
   6818    return NS_OK;
   6819  }
   6820  nsPoint off = frame->GetOffsetTo(absoluteContainerBlockFrame);
   6821  aX = nsPresContext::AppUnitsToIntCSSPixels(off.x);
   6822  aY = nsPresContext::AppUnitsToIntCSSPixels(off.y);
   6823 
   6824  return NS_OK;
   6825 }
   6826 
   6827 Element* HTMLEditor::GetSelectionContainerElement() const {
   6828  MOZ_ASSERT(IsEditActionDataAvailable());
   6829 
   6830  nsINode* focusNode = nullptr;
   6831  if (SelectionRef().IsCollapsed()) {
   6832    focusNode = SelectionRef().GetFocusNode();
   6833    if (NS_WARN_IF(!focusNode)) {
   6834      return nullptr;
   6835    }
   6836  } else {
   6837    const uint32_t rangeCount = SelectionRef().RangeCount();
   6838    MOZ_ASSERT(rangeCount, "If 0, Selection::IsCollapsed() should return true");
   6839 
   6840    if (rangeCount == 1) {
   6841      const nsRange* range = SelectionRef().GetRangeAt(0);
   6842 
   6843      const RangeBoundary& startRef = range->StartRef();
   6844      const RangeBoundary& endRef = range->EndRef();
   6845 
   6846      // This method called GetSelectedElement() to retrieve proper container
   6847      // when only one node is selected.  However, it simply returns start
   6848      // node of Selection with additional cost.  So, we do not need to call
   6849      // it anymore.
   6850      if (startRef.GetContainer()->IsElement() &&
   6851          startRef.GetContainer() == endRef.GetContainer() &&
   6852          startRef.GetChildAtOffset() &&
   6853          startRef.GetChildAtOffset()->GetNextSibling() ==
   6854              endRef.GetChildAtOffset()) {
   6855        focusNode = startRef.GetChildAtOffset();
   6856        MOZ_ASSERT(focusNode, "Start container must not be nullptr");
   6857      } else {
   6858        focusNode = range->GetClosestCommonInclusiveAncestor();
   6859        if (!focusNode) {
   6860          NS_WARNING(
   6861              "AbstractRange::GetClosestCommonInclusiveAncestor() returned "
   6862              "nullptr");
   6863          return nullptr;
   6864        }
   6865      }
   6866    } else {
   6867      for (const uint32_t i : IntegerRange(rangeCount)) {
   6868        MOZ_ASSERT(SelectionRef().RangeCount() == rangeCount);
   6869        const nsRange* range = SelectionRef().GetRangeAt(i);
   6870        MOZ_ASSERT(range);
   6871        nsINode* startContainer = range->GetStartContainer();
   6872        if (!focusNode) {
   6873          focusNode = startContainer;
   6874        } else if (focusNode != startContainer) {
   6875          // XXX Looks odd to use parent of startContainer because previous
   6876          //     range may not be in the parent node of current
   6877          //     startContainer.
   6878          focusNode = startContainer->GetParentNode();
   6879          // XXX Looks odd to break the for-loop here because we refer only
   6880          //     first range and another range which starts from different
   6881          //     container, and the latter range is preferred. Why?
   6882          break;
   6883        }
   6884      }
   6885      if (!focusNode) {
   6886        NS_WARNING("Focused node of selection was not found");
   6887        return nullptr;
   6888      }
   6889    }
   6890  }
   6891 
   6892  if (focusNode->IsText()) {
   6893    focusNode = focusNode->GetParentNode();
   6894    if (NS_WARN_IF(!focusNode)) {
   6895      return nullptr;
   6896    }
   6897  }
   6898 
   6899  if (NS_WARN_IF(!focusNode->IsElement())) {
   6900    return nullptr;
   6901  }
   6902  return focusNode->AsElement();
   6903 }
   6904 
   6905 NS_IMETHODIMP HTMLEditor::IsAnonymousElement(Element* aElement, bool* aReturn) {
   6906  if (NS_WARN_IF(!aElement)) {
   6907    return NS_ERROR_INVALID_ARG;
   6908  }
   6909  *aReturn = aElement->IsRootOfNativeAnonymousSubtree();
   6910  return NS_OK;
   6911 }
   6912 
   6913 nsresult HTMLEditor::SetReturnInParagraphCreatesNewParagraph(
   6914    bool aCreatesNewParagraph) {
   6915  mCRInParagraphCreatesParagraph = aCreatesNewParagraph;
   6916  return NS_OK;
   6917 }
   6918 
   6919 bool HTMLEditor::GetReturnInParagraphCreatesNewParagraph() const {
   6920  return mCRInParagraphCreatesParagraph;
   6921 }
   6922 
   6923 nsresult HTMLEditor::GetReturnInParagraphCreatesNewParagraph(
   6924    bool* aCreatesNewParagraph) {
   6925  *aCreatesNewParagraph = mCRInParagraphCreatesParagraph;
   6926  return NS_OK;
   6927 }
   6928 
   6929 NS_IMETHODIMP HTMLEditor::GetWrapWidth(int32_t* aWrapColumn) {
   6930  if (NS_WARN_IF(!aWrapColumn)) {
   6931    return NS_ERROR_INVALID_ARG;
   6932  }
   6933  *aWrapColumn = WrapWidth();
   6934  return NS_OK;
   6935 }
   6936 
   6937 //
   6938 // See if the style value includes this attribute, and if it does,
   6939 // cut out everything from the attribute to the next semicolon.
   6940 //
   6941 static void CutStyle(const char* stylename, nsString& styleValue) {
   6942  // Find the current wrapping type:
   6943  int32_t styleStart = styleValue.LowerCaseFindASCII(stylename);
   6944  if (styleStart >= 0) {
   6945    int32_t styleEnd = styleValue.Find(u";", styleStart);
   6946    if (styleEnd > styleStart) {
   6947      styleValue.Cut(styleStart, styleEnd - styleStart + 1);
   6948    } else {
   6949      styleValue.Cut(styleStart, styleValue.Length() - styleStart);
   6950    }
   6951  }
   6952 }
   6953 
   6954 NS_IMETHODIMP HTMLEditor::SetWrapWidth(int32_t aWrapColumn) {
   6955  AutoEditActionDataSetter editActionData(*this, EditAction::eSetWrapWidth);
   6956  if (NS_WARN_IF(!editActionData.CanHandle())) {
   6957    return NS_ERROR_NOT_INITIALIZED;
   6958  }
   6959 
   6960  mWrapColumn = aWrapColumn;
   6961 
   6962  // Make sure we're a plaintext editor, otherwise we shouldn't
   6963  // do the rest of this.
   6964  if (!IsPlaintextMailComposer()) {
   6965    return NS_OK;
   6966  }
   6967 
   6968  // Ought to set a style sheet here...
   6969  const RefPtr<Element> rootElement = GetRoot();
   6970  if (NS_WARN_IF(!rootElement)) {
   6971    return NS_ERROR_NOT_INITIALIZED;
   6972  }
   6973 
   6974  // Get the current style for this root element:
   6975  nsAutoString styleValue;
   6976  rootElement->GetAttr(nsGkAtoms::style, styleValue);
   6977 
   6978  // We'll replace styles for these values:
   6979  CutStyle("white-space", styleValue);
   6980  CutStyle("width", styleValue);
   6981  CutStyle("font-family", styleValue);
   6982 
   6983  // If we have other style left, trim off any existing semicolons
   6984  // or white-space, then add a known semicolon-space:
   6985  if (!styleValue.IsEmpty()) {
   6986    styleValue.Trim("; \t", false, true);
   6987    styleValue.AppendLiteral("; ");
   6988  }
   6989 
   6990  // Make sure we have fixed-width font.  This should be done for us,
   6991  // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;".
   6992  // Only do this if we're wrapping.
   6993  if (IsWrapHackEnabled() && aWrapColumn >= 0) {
   6994    styleValue.AppendLiteral("font-family: -moz-fixed; ");
   6995  }
   6996 
   6997  // and now we're ready to set the new white-space/wrapping style.
   6998  if (aWrapColumn > 0) {
   6999    // Wrap to a fixed column.
   7000    styleValue.AppendLiteral("white-space: pre-wrap; width: ");
   7001    styleValue.AppendInt(aWrapColumn);
   7002    styleValue.AppendLiteral("ch;");
   7003  } else if (!aWrapColumn) {
   7004    styleValue.AppendLiteral("white-space: pre-wrap;");
   7005  } else {
   7006    styleValue.AppendLiteral("white-space: pre;");
   7007  }
   7008 
   7009  AutoElementAttrAPIWrapper elementWrapper(*this, *rootElement);
   7010  nsresult rv = elementWrapper.SetAttr(nsGkAtoms::style, styleValue, true);
   7011  if (NS_FAILED(rv)) {
   7012    NS_WARNING("AutoElementAttrAPIWrapper::SetAttr() failed");
   7013    return rv;
   7014  }
   7015  NS_WARNING_ASSERTION(
   7016      elementWrapper.IsExpectedResult(styleValue),
   7017      "Setting style attribute caused other mutations, but ignored");
   7018  return NS_OK;
   7019 }
   7020 
   7021 Element* HTMLEditor::GetFocusedElement() const {
   7022  Element* const focusedElement = nsFocusManager::GetFocusedElementStatic();
   7023 
   7024  Document* document = GetDocument();
   7025  if (NS_WARN_IF(!document)) {
   7026    return nullptr;
   7027  }
   7028  const bool inDesignMode = focusedElement ? focusedElement->IsInDesignMode()
   7029                                           : document->IsInDesignMode();
   7030  if (!focusedElement) {
   7031    // in designMode, nobody gets focus in most cases.
   7032    if (inDesignMode && OurWindowHasFocus()) {
   7033      return document->GetRootElement();
   7034    }
   7035    return nullptr;
   7036  }
   7037 
   7038  if (inDesignMode) {
   7039    return OurWindowHasFocus() &&
   7040                   focusedElement->IsInclusiveDescendantOf(document)
   7041               ? focusedElement
   7042               : nullptr;
   7043  }
   7044 
   7045  // We're HTML editor for contenteditable
   7046 
   7047  // If the focused content isn't editable, or it has independent selection,
   7048  // we don't have focus.
   7049  if (!focusedElement->HasFlag(NODE_IS_EDITABLE) ||
   7050      focusedElement->HasIndependentSelection()) {
   7051    return nullptr;
   7052  }
   7053  // If our window is focused, we're focused.
   7054  return OurWindowHasFocus() ? focusedElement : nullptr;
   7055 }
   7056 
   7057 bool HTMLEditor::IsActiveInDOMWindow() const {
   7058  nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
   7059  if (NS_WARN_IF(!focusManager)) {
   7060    return false;
   7061  }
   7062 
   7063  Document* document = GetDocument();
   7064  if (NS_WARN_IF(!document)) {
   7065    return false;
   7066  }
   7067 
   7068  // If we're in designMode, we're always active in the DOM window.
   7069  if (IsInDesignMode()) {
   7070    return true;
   7071  }
   7072 
   7073  nsPIDOMWindowOuter* ourWindow = document->GetWindow();
   7074  nsCOMPtr<nsPIDOMWindowOuter> win;
   7075  nsIContent* content = nsFocusManager::GetFocusedDescendant(
   7076      ourWindow, nsFocusManager::eOnlyCurrentWindow, getter_AddRefs(win));
   7077  if (!content) {
   7078    return false;
   7079  }
   7080 
   7081  if (content->IsInDesignMode()) {
   7082    return true;
   7083  }
   7084 
   7085  // We're HTML editor for contenteditable
   7086 
   7087  // If the active content isn't editable, or it has independent selection,
   7088  // we're not active).
   7089  if (!content->HasFlag(NODE_IS_EDITABLE) ||
   7090      content->HasIndependentSelection()) {
   7091    return false;
   7092  }
   7093  return true;
   7094 }
   7095 
   7096 Element* HTMLEditor::ComputeEditingHostInternal(
   7097    const nsIContent* aContent, LimitInBodyElement aLimitInBodyElement) const {
   7098  Document* document = GetDocument();
   7099  if (NS_WARN_IF(!document)) {
   7100    return nullptr;
   7101  }
   7102 
   7103  auto MaybeLimitInBodyElement =
   7104      [&](const Element* aCandidateEditingHost) -> Element* {
   7105    if (!aCandidateEditingHost) {
   7106      return nullptr;
   7107    }
   7108    if (aLimitInBodyElement != LimitInBodyElement::Yes) {
   7109      return const_cast<Element*>(aCandidateEditingHost);
   7110    }
   7111    // By default, we should limit editing host to the <body> element for
   7112    // avoiding deleting or creating unexpected elements outside the <body>.
   7113    // However, this is incompatible with Chrome so that we should stop
   7114    // doing this with adding safety checks more.
   7115    if (document->GetBodyElement() &&
   7116        nsContentUtils::ContentIsFlattenedTreeDescendantOf(
   7117            aCandidateEditingHost, document->GetBodyElement())) {
   7118      return const_cast<Element*>(aCandidateEditingHost);
   7119    }
   7120    // XXX If aContent is an editing host and has no parent node, we reach here,
   7121    //     but returning the <body> which is not connected to aContent is odd.
   7122    return document->GetBodyElement();
   7123  };
   7124 
   7125  // We're HTML editor for contenteditable
   7126  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   7127  if (NS_WARN_IF(!editActionData.CanHandle())) {
   7128    return nullptr;
   7129  }
   7130 
   7131  const nsIContent* const content = [&]() -> const nsIContent* {
   7132    if (aContent) {
   7133      return aContent;
   7134    }
   7135    // If there are selection ranges, let's look for their common ancestor's
   7136    // editing host because selection ranges may be visible for users.
   7137    nsIContent* selectionCommonAncestor = nullptr;
   7138    for (uint32_t i : IntegerRange(SelectionRef().RangeCount())) {
   7139      nsRange* range = SelectionRef().GetRangeAt(i);
   7140      MOZ_ASSERT(range);
   7141      nsIContent* commonAncestor =
   7142          nsIContent::FromNodeOrNull(range->GetCommonAncestorContainer(
   7143              IgnoreErrors(), AllowRangeCrossShadowBoundary::Yes));
   7144      if (MOZ_UNLIKELY(!commonAncestor)) {
   7145        continue;
   7146      }
   7147      if (!selectionCommonAncestor) {
   7148        selectionCommonAncestor = commonAncestor;
   7149      } else {
   7150        selectionCommonAncestor = nsIContent::FromNodeOrNull(
   7151            nsContentUtils::GetCommonFlattenedTreeAncestorForSelection(
   7152                commonAncestor, selectionCommonAncestor));
   7153      }
   7154    }
   7155    if (selectionCommonAncestor) {
   7156      return selectionCommonAncestor;
   7157    }
   7158    // Otherwise, let's use the focused element in the window.
   7159    nsPIDOMWindowInner* const innerWindow = document->GetInnerWindow();
   7160    if (MOZ_UNLIKELY(!innerWindow)) {
   7161      return nullptr;
   7162    }
   7163    if (Element* focusedElementInWindow = innerWindow->GetFocusedElement()) {
   7164      if (focusedElementInWindow->ChromeOnlyAccess()) {
   7165        focusedElementInWindow = Element::FromNodeOrNull(
   7166            // XXX Should we use
   7167            // nsIContent::FindFirstNonChromeOnlyAccessContent() instead of
   7168            // nsINode::GetClosestNativeAnonymousSubtreeRootParentOrHost()?
   7169            focusedElementInWindow
   7170                ->GetClosestNativeAnonymousSubtreeRootParentOrHost());
   7171      }
   7172      if (focusedElementInWindow) {
   7173        return focusedElementInWindow->IsEditable() ? focusedElementInWindow
   7174                                                    : nullptr;
   7175      }
   7176    }
   7177    // If there is no focused element and the document is in the design mode,
   7178    // let's return the <body>.
   7179    if (document->IsInDesignMode()) {
   7180      return document->GetBodyElement();
   7181    }
   7182    // Otherwise, we cannot find the editing host...
   7183    return nullptr;
   7184  }();
   7185  if ((content && content->IsInDesignMode()) ||
   7186      (!content && document->IsInDesignMode())) {
   7187    // FIXME: There may be no <body>.  In such case and aLimitInBodyElement is
   7188    // "No", we should use root element instead.
   7189    return document->GetBodyElement();
   7190  }
   7191 
   7192  if (NS_WARN_IF(!content)) {
   7193    return nullptr;
   7194  }
   7195 
   7196  // If the active content isn't editable, we're not active.
   7197  if (!content->HasFlag(NODE_IS_EDITABLE)) {
   7198    return nullptr;
   7199  }
   7200 
   7201  // Although the content shouldn't be in a native anonymous subtree, but
   7202  // perhaps due to a bug of Selection or Range API, it may occur.  HTMLEditor
   7203  // shouldn't touch native anonymous subtree so that return nullptr in such
   7204  // case.
   7205  if (MOZ_UNLIKELY(content->IsInNativeAnonymousSubtree())) {
   7206    return nullptr;
   7207  }
   7208 
   7209  // Note that `Selection` can be in <input> or <textarea>.  In the case, we
   7210  // need to look for an ancestor which does not have editable parent.
   7211  return MaybeLimitInBodyElement(
   7212      const_cast<nsIContent*>(content)->GetEditingHost());
   7213 }
   7214 
   7215 void HTMLEditor::NotifyEditingHostMaybeChanged() {
   7216  // Note that even if the document is in design mode, a contenteditable element
   7217  // in a shadow tree is focusable.   Therefore, we may need to update editing
   7218  // host even when the document is in design mode.
   7219  if (MOZ_UNLIKELY(NS_WARN_IF(!GetDocument()))) {
   7220    return;
   7221  }
   7222 
   7223  // We're HTML editor for contenteditable
   7224  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   7225  if (NS_WARN_IF(!editActionData.CanHandle())) {
   7226    return;
   7227  }
   7228 
   7229  // Get selection ancestor limit which may be old editing host.
   7230  nsIContent* ancestorLimiter = SelectionRef().GetAncestorLimiter();
   7231  if (!ancestorLimiter) {
   7232    // If we've not initialized selection ancestor limit, we should wait focus
   7233    // event to set proper limiter.
   7234    return;
   7235  }
   7236 
   7237  // Compute current editing host.
   7238  Element* const editingHost = ComputeEditingHost();
   7239  if (NS_WARN_IF(!editingHost)) {
   7240    return;
   7241  }
   7242 
   7243  // Update selection ancestor limit if current editing host includes the
   7244  // previous editing host.
   7245  // Additionally, the editing host may be an element in shadow DOM and the
   7246  // shadow host is in designMode.  In this case, we need to set the editing
   7247  // host as the new selection limiter.
   7248  if (ancestorLimiter->IsInclusiveDescendantOf(editingHost) ||
   7249      (ancestorLimiter->IsInDesignMode() != editingHost->IsInDesignMode())) {
   7250    // Note that don't call HTMLEditor::InitializeSelectionAncestorLimit()
   7251    // here because it may collapse selection to the first editable node.
   7252    EditorBase::InitializeSelectionAncestorLimit(*editingHost);
   7253  }
   7254 }
   7255 
   7256 EventTarget* HTMLEditor::GetDOMEventTarget() const {
   7257  // Don't use getDocument here, because we have no way of knowing
   7258  // whether Init() was ever called.  So we need to get the document
   7259  // ourselves, if it exists.
   7260  Document* doc = GetDocument();
   7261  MOZ_ASSERT(doc, "The HTMLEditor has not been initialized yet");
   7262  if (!doc) {
   7263    return nullptr;
   7264  }
   7265 
   7266  // Register the EditorEventListener to the parent of window.
   7267  //
   7268  // The advantage of this approach is HTMLEditor can still
   7269  // receive events when shadow dom is involved.
   7270  if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
   7271    return win->GetParentTarget();
   7272  }
   7273  return nullptr;
   7274 }
   7275 
   7276 Element* HTMLEditor::GetBodyElement() const {
   7277  Document* document = GetDocument();
   7278  MOZ_ASSERT(document, "The HTMLEditor hasn't been initialized yet");
   7279  if (NS_WARN_IF(!document)) {
   7280    return nullptr;
   7281  }
   7282  return document->GetBody();
   7283 }
   7284 
   7285 nsINode* HTMLEditor::GetFocusedNode() const {
   7286  Element* focusedElement = GetFocusedElement();
   7287  if (!focusedElement) {
   7288    return nullptr;
   7289  }
   7290 
   7291  // focusedElement might be non-null even
   7292  // nsFocusManager::GetFocusedElementStatic() returns null.  That's the
   7293  // designMode case, and in that case our GetFocusedElement() returns the root
   7294  // element, but we want to return the document.
   7295 
   7296  if ((focusedElement = nsFocusManager::GetFocusedElementStatic())) {
   7297    return focusedElement;
   7298  }
   7299 
   7300  return GetDocument();
   7301 }
   7302 
   7303 bool HTMLEditor::OurWindowHasFocus() const {
   7304  nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
   7305  if (NS_WARN_IF(!focusManager)) {
   7306    return false;
   7307  }
   7308  nsPIDOMWindowOuter* focusedWindow = focusManager->GetFocusedWindow();
   7309  if (!focusedWindow) {
   7310    return false;
   7311  }
   7312  Document* document = GetDocument();
   7313  if (NS_WARN_IF(!document)) {
   7314    return false;
   7315  }
   7316  nsPIDOMWindowOuter* ourWindow = document->GetWindow();
   7317  return ourWindow == focusedWindow;
   7318 }
   7319 
   7320 bool HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const {
   7321  if (!EditorBase::IsAcceptableInputEvent(aGUIEvent)) {
   7322    return false;
   7323  }
   7324 
   7325  // While there is composition, all composition events in its top level
   7326  // window are always fired on the composing editor.  Therefore, if this
   7327  // editor has composition, the composition events should be handled in this
   7328  // editor.
   7329  if (mComposition && aGUIEvent->AsCompositionEvent()) {
   7330    return true;
   7331  }
   7332 
   7333  nsCOMPtr<nsINode> eventTargetNode =
   7334      nsINode::FromEventTargetOrNull(aGUIEvent->GetOriginalDOMEventTarget());
   7335  if (NS_WARN_IF(!eventTargetNode)) {
   7336    return false;
   7337  }
   7338 
   7339  if (eventTargetNode->IsContent()) {
   7340    eventTargetNode =
   7341        eventTargetNode->AsContent()->FindFirstNonChromeOnlyAccessContent();
   7342    if (NS_WARN_IF(!eventTargetNode)) {
   7343      return false;
   7344    }
   7345  }
   7346 
   7347  RefPtr<Document> document = GetDocument();
   7348  if (NS_WARN_IF(!document)) {
   7349    return false;
   7350  }
   7351 
   7352  if (eventTargetNode->IsInDesignMode()) {
   7353    // If this editor is in designMode and the event target is the document,
   7354    // the event is for this editor.
   7355    if (eventTargetNode->IsDocument()) {
   7356      return eventTargetNode == document;
   7357    }
   7358    // Otherwise, check whether the event target is in this document or not.
   7359    if (NS_WARN_IF(!eventTargetNode->IsContent())) {
   7360      return false;
   7361    }
   7362    if (document == eventTargetNode->GetUncomposedDoc()) {
   7363      return true;
   7364    }
   7365  }
   7366 
   7367  // Space event for <button> and <summary> with contenteditable
   7368  // should be handle by the themselves.
   7369  if (aGUIEvent->mMessage == eKeyPress &&
   7370      aGUIEvent->AsKeyboardEvent()->ShouldWorkAsSpaceKey()) {
   7371    nsGenericHTMLElement* element =
   7372        HTMLButtonElement::FromNode(eventTargetNode);
   7373    if (!element) {
   7374      element = HTMLSummaryElement::FromNode(eventTargetNode);
   7375    }
   7376 
   7377    if (element && element->IsContentEditable()) {
   7378      return false;
   7379    }
   7380  }
   7381  // This HTML editor is for contenteditable.  We need to check the validity
   7382  // of the target.
   7383  if (NS_WARN_IF(!eventTargetNode->IsContent())) {
   7384    return false;
   7385  }
   7386 
   7387  // If the event is a mouse event, we need to check if the target content is
   7388  // the focused editing host or its descendant.
   7389  if (aGUIEvent->AsMouseEventBase()) {
   7390    nsIContent* editingHost = ComputeEditingHost();
   7391    // If there is no active editing host, we cannot handle the mouse event
   7392    // correctly.
   7393    if (!editingHost) {
   7394      return false;
   7395    }
   7396    // If clicked on non-editable root element but the body element is the
   7397    // active editing host, we should assume that the click event is
   7398    // targetted.
   7399    if (eventTargetNode == document->GetRootElement() &&
   7400        !eventTargetNode->HasFlag(NODE_IS_EDITABLE) &&
   7401        editingHost == document->GetBodyElement()) {
   7402      eventTargetNode = editingHost;
   7403    }
   7404    // If the target element is neither the active editing host nor a
   7405    // descendant of it, we may not be able to handle the event.
   7406    if (!eventTargetNode->IsInclusiveDescendantOf(editingHost)) {
   7407      return false;
   7408    }
   7409    // If the clicked element has an independent selection, we shouldn't
   7410    // handle this click event.
   7411    if (eventTargetNode->AsContent()->HasIndependentSelection()) {
   7412      return false;
   7413    }
   7414    // If the target content is editable, we should handle this event.
   7415    return eventTargetNode->HasFlag(NODE_IS_EDITABLE);
   7416  }
   7417 
   7418  // If the target of the other events which target focused element isn't
   7419  // editable or has an independent selection, this editor shouldn't handle
   7420  // the event.
   7421  if (!eventTargetNode->HasFlag(NODE_IS_EDITABLE) ||
   7422      eventTargetNode->AsContent()->HasIndependentSelection()) {
   7423    return false;
   7424  }
   7425 
   7426  // Finally, check whether we're actually focused or not.  When we're not
   7427  // focused, we should ignore the dispatched event by script (or something)
   7428  // because content editable element needs selection in itself for editing.
   7429  // However, when we're not focused, it's not guaranteed.
   7430  return IsActiveInDOMWindow();
   7431 }
   7432 
   7433 Result<widget::IMEState, nsresult> HTMLEditor::GetPreferredIMEState() const {
   7434  // HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
   7435  return IMEState{IsReadonly() ? IMEEnabled::Disabled : IMEEnabled::Enabled,
   7436                  IMEState::DONT_CHANGE_OPEN_STATE};
   7437 }
   7438 
   7439 already_AddRefed<Element> HTMLEditor::GetInputEventTargetElement() const {
   7440  // If there is no selection ranges, we'll do nothing.  Therefore,
   7441  // `beforeinput` event should not be fired.
   7442  // FIXME: If there is no selection but we've already modified the DOM,
   7443  // we should fire `input` event on the editing host.  However, we cannot
   7444  // know which one was the editing host when we touched the DOM.
   7445  if (MOZ_UNLIKELY(!SelectionRef().RangeCount())) {
   7446    return nullptr;
   7447  }
   7448 
   7449  RefPtr<Element> target = ComputeEditingHost(LimitInBodyElement::No);
   7450  if (target) {
   7451    return target.forget();
   7452  }
   7453 
   7454  // When there is no active editing host due to focus node is a
   7455  // non-editable node, we should look for its editable parent to
   7456  // dispatch `beforeinput` event.
   7457  nsIContent* focusContent =
   7458      nsIContent::FromNodeOrNull(SelectionRef().GetFocusNode());
   7459  if (!focusContent || focusContent->IsEditable()) {
   7460    return nullptr;
   7461  }
   7462  for (Element* element : focusContent->AncestorsOfType<Element>()) {
   7463    if (element->IsEditable()) {
   7464      target = element->GetEditingHost();
   7465      return target.forget();
   7466    }
   7467  }
   7468  return nullptr;
   7469 }
   7470 
   7471 }  // namespace mozilla