Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/editor/libeditor/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 301 kB image not shown  

Quelle  HTMLEditor.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "HTMLEditor.h"
#include "HTMLEditHelpers.h"
#include "HTMLEditorInlines.h"

#include "AutoClonedRangeArray.h"
#include "AutoSelectionRestorer.h"
#include "CSSEditUtils.h"
#include "EditAction.h"
#include "EditorBase.h"
#include "EditorDOMPoint.h"
#include "EditorLineBreak.h"
#include "EditorUtils.h"
#include "ErrorList.h"
#include "HTMLEditorEventListener.h"
#include "HTMLEditUtils.h"
#include "InsertNodeTransaction.h"
#include "JoinNodesTransaction.h"
#include "MoveNodeTransaction.h"
#include "PendingStyles.h"
#include "ReplaceTextTransaction.h"
#include "SplitNodeTransaction.h"
#include "WhiteSpaceVisibilityKeeper.h"
#include "WSRunScanner.h"

#include "mozilla/ComposerCommandsUpdater.h"
#include "mozilla/ContentIterator.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EditorForwards.h"
#include "mozilla/Encoding.h"  // for Encoding
#include "mozilla/FlushType.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/IntegerRange.h"  // for IntegerRange
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/mozInlineSpellChecker.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_editor.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TextControlElement.h"
#include "mozilla/TextEditor.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TextServicesDocument.h"
#include "mozilla/ToString.h"
#include "mozilla/css/Loader.h"
#include "mozilla/dom/AncestorIterator.h"
#include "mozilla/dom/Attr.h"
#include "mozilla/dom/BorrowedAttrInfo.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/HTMLAnchorElement.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLBRElement.h"
#include "mozilla/dom/HTMLButtonElement.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/dom/NameSpaceConstants.h"
#include "mozilla/dom/Selection.h"

#include "nsContentList.h"
#include "nsContentUtils.h"
#include "nsCRT.h"
#include "nsDebug.h"
#include "nsElementTable.h"
#include "nsFocusManager.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsHTMLDocument.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"  // for nsINode::IsInDesignMode()
#include "nsIEditActionListener.h"
#include "nsIFrame.h"
#include "nsIPrincipal.h"
#include "nsISelectionController.h"
#include "nsIURI.h"
#include "nsIWidget.h"
#include "nsNetUtil.h"
#include "nsPresContext.h"
#include "nsPrintfCString.h"
#include "nsPIDOMWindow.h"
#include "nsStyledElement.h"
#include "nsTextFragment.h"
#include "nsUnicharUtils.h"

namespace mozilla {

using namespace dom;
using namespace widget;

LazyLogModule gHTMLEditorFocusLog("HTMLEditorFocus");

using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
using LeafNodeType = HTMLEditUtils::LeafNodeType;
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;

// Some utilities to handle overloading of "A" tag for link and named anchor.
static bool IsLinkTag(const nsAtom& aTagName) {
  return &aTagName == nsGkAtoms::href;
}

static bool IsNamedAnchorTag(const nsAtom& aTagName) {
  return &aTagName == nsGkAtoms::anchor;
}

// Helper struct for DoJoinNodes() and DoSplitNode().
struct MOZ_STACK_CLASS SavedRange final {
  RefPtr<Selection> mSelection;
  nsCOMPtr<nsINode> mStartContainer;
  nsCOMPtr<nsINode> mEndContainer;
  uint32_t mStartOffset = 0;
  uint32_t mEndOffset = 0;
};

/******************************************************************************
 * HTMLEditor
 *****************************************************************************/


template Result<CreateContentResult, nsresult>
HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
    nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert,
    SplitAtEdges aSplitAtEdges);
template Result<CreateElementResult, nsresult>
HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
    Element& aContentToInsert, const EditorDOMPoint& aPointToInsert,
    SplitAtEdges aSplitAtEdges);
template Result<CreateTextResult, nsresult>
HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
    Text& aContentToInsert, const EditorDOMPoint& aPointToInsert,
    SplitAtEdges aSplitAtEdges);

MOZ_RUNINIT HTMLEditor::InitializeInsertingElement
    HTMLEditor::DoNothingForNewElement =
        [](HTMLEditor&, Element&, const EditorDOMPoint&) { return NS_OK; };

MOZ_RUNINIT HTMLEditor::InitializeInsertingElement
    HTMLEditor::InsertNewBRElement =
        [](HTMLEditor& aHTMLEditor, Element& aNewElement,
           const EditorDOMPoint&) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
          MOZ_ASSERT(!aNewElement.IsInComposedDoc());
          Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
              aHTMLEditor.InsertLineBreak(WithTransaction::No,
                                          LineBreakType::BRElement,
                                          EditorDOMPoint(&aNewElement, 0u));
          if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
            NS_WARNING(
                "HTMLEditor::InsertLineBreak(WithTransaction::No, "
                "LineBreakType::BRElement) failed");
            return insertBRElementResultOrError.unwrapErr();
          }
          insertBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
          return NS_OK;
        };

// static
Result<CreateElementResult, nsresult>
HTMLEditor::AppendNewElementToInsertingElement(
    HTMLEditor& aHTMLEditor, const nsStaticAtom& aTagName, Element& aNewElement,
    const InitializeInsertingElement& aInitializer) {
  MOZ_ASSERT(!aNewElement.IsInComposedDoc());
  Result<CreateElementResult, nsresult> createNewElementResult =
      aHTMLEditor.CreateAndInsertElement(
          WithTransaction::No, const_cast<nsStaticAtom&>(aTagName),
          EditorDOMPoint(&aNewElement, 0u), aInitializer);
  NS_WARNING_ASSERTION(
      createNewElementResult.isOk(),
      "HTMLEditor::CreateAndInsertElement(WithTransaction::No) failed");
  return createNewElementResult;
}

// static
Result<CreateElementResult, nsresult>
HTMLEditor::AppendNewElementWithBRToInsertingElement(
    HTMLEditor& aHTMLEditor, const nsStaticAtom& aTagName,
    Element& aNewElement) {
  MOZ_ASSERT(!aNewElement.IsInComposedDoc());
  Result<CreateElementResult, nsresult> createNewElementWithBRResult =
      HTMLEditor::AppendNewElementToInsertingElement(
          aHTMLEditor, aTagName, aNewElement, HTMLEditor::InsertNewBRElement);
  NS_WARNING_ASSERTION(
      createNewElementWithBRResult.isOk(),
      "HTMLEditor::AppendNewElementToInsertingElement() failed");
  return createNewElementWithBRResult;
}

MOZ_RUNINIT HTMLEditor::AttributeFilter HTMLEditor::CopyAllAttributes =
    [](HTMLEditor&, const Element&, const Element&, int32_t, const nsAtom&,
       nsString&) { return true; };
MOZ_RUNINIT HTMLEditor::AttributeFilter HTMLEditor::CopyAllAttributesExceptId =
    [](HTMLEditor&, const Element&, const Element&, int32_t aNamespaceID,
       const nsAtom& aAttrName, nsString&) {
      return aNamespaceID != kNameSpaceID_None || &aAttrName != nsGkAtoms::id;
    };
MOZ_RUNINIT HTMLEditor::AttributeFilter HTMLEditor::CopyAllAttributesExceptDir =
    [](HTMLEditor&, const Element&, const Element&, int32_t aNamespaceID,
       const nsAtom& aAttrName, nsString&) {
      return aNamespaceID != kNameSpaceID_None || &aAttrName != nsGkAtoms::dir;
    };
MOZ_RUNINIT HTMLEditor::AttributeFilter
    HTMLEditor::CopyAllAttributesExceptIdAndDir =
        [](HTMLEditor&, const Element&, const Element&, int32_t aNamespaceID,
           const nsAtom& aAttrName, nsString&) {
          return !(
              aNamespaceID == kNameSpaceID_None &&
              (&aAttrName == nsGkAtoms::id || &aAttrName == nsGkAtoms::dir));
        };

HTMLEditor::HTMLEditor(const Document& aDocument)
    : EditorBase(EditorBase::EditorType::HTML),
      mCRInParagraphCreatesParagraph(false),
      mIsObjectResizingEnabled(
          StaticPrefs::editor_resizing_enabled_by_default()),
      mIsResizing(false),
      mPreserveRatio(false),
      mResizedObjectIsAnImage(false),
      mIsAbsolutelyPositioningEnabled(
          StaticPrefs::editor_positioning_enabled_by_default()),
      mResizedObjectIsAbsolutelyPositioned(false),
      mGrabberClicked(false),
      mIsMoving(false),
      mSnapToGridEnabled(false),
      mIsInlineTableEditingEnabled(
          StaticPrefs::editor_inline_table_editing_enabled_by_default()),
      mIsCSSPrefChecked(StaticPrefs::editor_use_css()),
      mOriginalX(0),
      mOriginalY(0),
      mResizedObjectX(0),
      mResizedObjectY(0),
      mResizedObjectWidth(0),
      mResizedObjectHeight(0),
      mResizedObjectMarginLeft(0),
      mResizedObjectMarginTop(0),
      mResizedObjectBorderLeft(0),
      mResizedObjectBorderTop(0),
      mXIncrementFactor(0),
      mYIncrementFactor(0),
      mWidthIncrementFactor(0),
      mHeightIncrementFactor(0),
      mInfoXIncrement(20),
      mInfoYIncrement(20),
      mPositionedObjectX(0),
      mPositionedObjectY(0),
      mPositionedObjectWidth(0),
      mPositionedObjectHeight(0),
      mPositionedObjectMarginLeft(0),
      mPositionedObjectMarginTop(0),
      mPositionedObjectBorderLeft(0),
      mPositionedObjectBorderTop(0),
      mGridSize(0),
      mDefaultParagraphSeparator(ParagraphSeparator::div) {}

HTMLEditor::~HTMLEditor() {
  Telemetry::Accumulate(Telemetry::HTMLEDITORS_WITH_BEFOREINPUT_LISTENERS,
                        MayHaveBeforeInputEventListenersForTelemetry() ? 1 : 0);
  Telemetry::Accumulate(
      Telemetry::HTMLEDITORS_OVERRIDDEN_BY_BEFOREINPUT_LISTENERS,
      mHasBeforeInputBeenCanceled ? 1 : 0);
  Telemetry::Accumulate(
      Telemetry::
          HTMLEDITORS_WITH_MUTATION_LISTENERS_WITHOUT_BEFOREINPUT_LISTENERS,
      !MayHaveBeforeInputEventListenersForTelemetry() &&
              MayHaveMutationEventListeners()
          ? 1
          : 0);
  Telemetry::Accumulate(
      Telemetry::
          HTMLEDITORS_WITH_MUTATION_OBSERVERS_WITHOUT_BEFOREINPUT_LISTENERS,
      !MayHaveBeforeInputEventListenersForTelemetry() &&
              MutationObserverHasObservedNodeForTelemetry()
          ? 1
          : 0);

  mPendingStylesToApplyToNewContent = nullptr;

  if (mDisabledLinkHandling) {
    if (Document* doc = GetDocument()) {
      doc->SetLinkHandlingEnabled(mOldLinkHandlingEnabled);
    }
  }

  RemoveEventListeners();

  HideAnonymousEditingUIs();
}

NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLEditor)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLEditor, EditorBase)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStylesToApplyToNewContent)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mComposerCommandsUpdater)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChangedRangeForTopLevelEditSubAction)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaddingBRElementForEmptyEditor)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastCollapsibleWhiteSpaceAppendedTextNode)
  tmp->HideAnonymousEditingUIs();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLEditor, EditorBase)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStylesToApplyToNewContent)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mComposerCommandsUpdater)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChangedRangeForTopLevelEditSubAction)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaddingBRElementForEmptyEditor)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastCollapsibleWhiteSpaceAppendedTextNode)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopRightHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLeftHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRightHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomLeftHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomRightHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActivatedHandle)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingShadow)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingInfo)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizedObject)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbsolutelyPositionedObject)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGrabber)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPositioningShadow)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineEditedCell)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnBeforeButton)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveColumnButton)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnAfterButton)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowBeforeButton)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveRowButton)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowAfterButton)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_ADDREF_INHERITED(HTMLEditor, EditorBase)
NS_IMPL_RELEASE_INHERITED(HTMLEditor, EditorBase)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLEditor)
  NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor)
  NS_INTERFACE_MAP_ENTRY(nsIHTMLObjectResizer)
  NS_INTERFACE_MAP_ENTRY(nsIHTMLAbsPosEditor)
  NS_INTERFACE_MAP_ENTRY(nsIHTMLInlineTableEditor)
  NS_INTERFACE_MAP_ENTRY(nsITableEditor)
  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
  NS_INTERFACE_MAP_ENTRY(nsIEditorMailSupport)
NS_INTERFACE_MAP_END_INHERITING(EditorBase)

nsresult HTMLEditor::Init(Document& aDocument,
                          ComposerCommandsUpdater& aComposerCommandsUpdater,
                          uint32_t aFlags) {
  MOZ_ASSERT(!mInitSucceeded,
             "HTMLEditor::Init() called again without calling PreDestroy()?");

  MOZ_DIAGNOSTIC_ASSERT(!mComposerCommandsUpdater ||
                        mComposerCommandsUpdater == &aComposerCommandsUpdater);
  mComposerCommandsUpdater = &aComposerCommandsUpdater;

  RefPtr<PresShell> presShell = aDocument.GetPresShell();
  if (NS_WARN_IF(!presShell)) {
    return NS_ERROR_FAILURE;
  }
  nsresult rv = InitInternal(aDocument, nullptr, *presShell, aFlags);
  if (NS_FAILED(rv)) {
    NS_WARNING("EditorBase::InitInternal() failed");
    return rv;
  }

  // Init mutation observer
  aDocument.AddMutationObserverUnlessExists(this);

  if (!mRootElement) {
    UpdateRootElement();
  }

  // disable Composer-only features
  if (IsMailEditor()) {
    DebugOnly<nsresult> rvIgnored = SetAbsolutePositioningEnabled(false);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "HTMLEditor::SetAbsolutePositioningEnabled(false) failed, but ignored");
    rvIgnored = SetSnapToGridEnabled(false);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "HTMLEditor::SetSnapToGridEnabled(false) failed, but ignored");
  }

  // disable links
  Document* document = GetDocument();
  if (NS_WARN_IF(!document)) {
    return NS_ERROR_FAILURE;
  }
  if (!IsPlaintextMailComposer() && !IsInteractionAllowed()) {
    mDisabledLinkHandling = true;
    mOldLinkHandlingEnabled = document->LinkHandlingEnabled();
    document->SetLinkHandlingEnabled(false);
  }

  // init the type-in state
  mPendingStylesToApplyToNewContent = new PendingStyles();

  AutoEditActionDataSetter editActionData(*this, EditAction::eInitializing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_FAILURE;
  }

  rv = InitEditorContentAndSelection();
  if (NS_FAILED(rv)) {
    NS_WARNING("HTMLEditor::InitEditorContentAndSelection() failed");
    // XXX Sholdn't we expose `NS_ERROR_EDITOR_DESTROYED` even though this
    //     is a public method?
    return EditorBase::ToGenericNSResult(rv);
  }

  // Throw away the old transaction manager if this is not the first time that
  // we're initializing the editor.
  ClearUndoRedo();
  EnableUndoRedo();  // FYI: Creating mTransactionManager in this call

  if (mTransactionManager) {
    mTransactionManager->Attach(*this);
  }

  MOZ_ASSERT(!mInitSucceeded, "HTMLEditor::Init() shouldn't be nested");
  mInitSucceeded = true;
  return NS_OK;
}

nsresult HTMLEditor::PostCreate() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  nsresult rv = PostCreateInternal();
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::PostCreatInternal() failed");
  return rv;
}

void HTMLEditor::PreDestroy() {
  if (mDidPreDestroy) {
    return;
  }

  mInitSucceeded = false;

  // FYI: Cannot create AutoEditActionDataSetter here.  However, it does not
  //      necessary for the methods called by the following code.

  RefPtr<Document> document = GetDocument();
  if (document) {
    document->RemoveMutationObserver(this);
  }

  // Clean up after our anonymous content -- we don't want these nodes to
  // stay around (which they would, since the frames have an owning reference).
  PresShell* presShell = GetPresShell();
  if (presShell && presShell->IsDestroying()) {
    // Just destroying PresShell now.
    // We have to keep UI elements of anonymous content until PresShell
    // is destroyed.
    RefPtr<HTMLEditor> self = this;
    nsContentUtils::AddScriptRunner(
        NS_NewRunnableFunction("HTMLEditor::PreDestroy",
                               [self]() { self->HideAnonymousEditingUIs(); }));
  } else {
    // PresShell is alive or already gone.
    HideAnonymousEditingUIs();
  }

  mPaddingBRElementForEmptyEditor = nullptr;

  PreDestroyInternal();
}

NS_IMETHODIMP HTMLEditor::GetDocumentCharacterSet(nsACString& aCharacterSet) {
  nsresult rv = GetDocumentCharsetInternal(aCharacterSet);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "HTMLEditor::GetDocumentCharsetInternal() failed");
  return rv;
}

NS_IMETHODIMP HTMLEditor::SetDocumentCharacterSet(
    const nsACString& aCharacterSet) {
  AutoEditActionDataSetter editActionData(*this, EditAction::eSetCharacterSet);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
  if (NS_FAILED(rv)) {
    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  RefPtr<Document> document = GetDocument();
  if (NS_WARN_IF(!document)) {
    return EditorBase::ToGenericNSResult(NS_ERROR_NOT_INITIALIZED);
  }
  // This method is scriptable, so add-ons could pass in something other
  // than a canonical name.
  const Encoding* encoding = Encoding::ForLabelNoReplacement(aCharacterSet);
  if (!encoding) {
    NS_WARNING("Encoding::ForLabelNoReplacement() failed");
    return EditorBase::ToGenericNSResult(NS_ERROR_INVALID_ARG);
  }
  document->SetDocumentCharacterSet(WrapNotNull(encoding));

  // Update META charset element.
  if (UpdateMetaCharsetWithTransaction(*document, aCharacterSet)) {
    return NS_OK;
  }

  // Set attributes to the created element
  if (aCharacterSet.IsEmpty()) {
    return NS_OK;
  }

  RefPtr<nsContentList> headElementList =
      document->GetElementsByTagName(u"head"_ns);
  if (NS_WARN_IF(!headElementList)) {
    return NS_OK;
  }

  nsCOMPtr<nsIContent> primaryHeadElement = headElementList->Item(0);
  if (NS_WARN_IF(!primaryHeadElement)) {
    return NS_OK;
  }

  // Create a new meta charset tag
  Result<CreateElementResult, nsresult> createNewMetaElementResult =
      CreateAndInsertElement(
          WithTransaction::Yes, *nsGkAtoms::meta,
          EditorDOMPoint(primaryHeadElement, 0),
          [&aCharacterSet](HTMLEditor&, Element& aMetaElement,
                           const EditorDOMPoint&) {
            MOZ_ASSERT(!aMetaElement.IsInComposedDoc());
            DebugOnly<nsresult> rvIgnored =
                aMetaElement.SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv,
                                     u"Content-Type"_ns, false);
            NS_WARNING_ASSERTION(
                NS_SUCCEEDED(rvIgnored),
                "Element::SetAttr(nsGkAtoms::httpEquiv, \"Content-Type\", "
                "false) failed, but ignored");
            rvIgnored =
                aMetaElement.SetAttr(kNameSpaceID_None, nsGkAtoms::content,
                                     u"text/html;charset="_ns +
                                         NS_ConvertASCIItoUTF16(aCharacterSet),
                                     false);
            NS_WARNING_ASSERTION(
                NS_SUCCEEDED(rvIgnored),
                nsPrintfCString(
                    "Element::SetAttr(nsGkAtoms::content, "
                    "\"text/html;charset=%s\", false) failed, but ignored",
                    nsPromiseFlatCString(aCharacterSet).get())
                    .get());
            return NS_OK;
          });
  NS_WARNING_ASSERTION(createNewMetaElementResult.isOk(),
                       "HTMLEditor::CreateAndInsertElement(WithTransaction::"
                       "Yes, nsGkAtoms::meta) failed, but ignored");
  // Probably, we don't need to update selection in this case since we should
  // not put selection into <head> element.
  createNewMetaElementResult.inspect().IgnoreCaretPointSuggestion();
  return NS_OK;
}

bool HTMLEditor::UpdateMetaCharsetWithTransaction(
    Document& aDocument, const nsACString& aCharacterSet) {
  // get a list of META tags
  RefPtr<nsContentList> metaElementList =
      aDocument.GetElementsByTagName(u"meta"_ns);
  if (NS_WARN_IF(!metaElementList)) {
    return false;
  }

  for (uint32_t i = 0; i < metaElementList->Length(true); ++i) {
    RefPtr<Element> metaElement = metaElementList->Item(i)->AsElement();
    MOZ_ASSERT(metaElement);

    nsAutoString currentValue;
    metaElement->GetAttr(nsGkAtoms::httpEquiv, currentValue);

    if (!FindInReadable(u"content-type"_ns, currentValue,
                        nsCaseInsensitiveStringComparator)) {
      continue;
    }

    metaElement->GetAttr(nsGkAtoms::content, currentValue);

    constexpr auto charsetEquals = u"charset="_ns;
    nsAString::const_iterator originalStart, start, end;
    originalStart = currentValue.BeginReading(start);
    currentValue.EndReading(end);
    if (!FindInReadable(charsetEquals, start, end,
                        nsCaseInsensitiveStringComparator)) {
      continue;
    }

    // set attribute to <original prefix> charset=text/html
    nsresult rv = SetAttributeWithTransaction(
        *metaElement, *nsGkAtoms::content,
        Substring(originalStart, start) + charsetEquals +
            NS_ConvertASCIItoUTF16(aCharacterSet));
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rv),
        "EditorBase::SetAttributeWithTransaction(nsGkAtoms::content) failed");
    return NS_SUCCEEDED(rv);
  }
  return false;
}

NS_IMETHODIMP HTMLEditor::NotifySelectionChanged(Document* aDocument,
                                                 Selection* aSelection,
                                                 int16_t aReason,
                                                 int32_t aAmount) {
  if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
    return NS_ERROR_INVALID_ARG;
  }

  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  if (mPendingStylesToApplyToNewContent) {
    RefPtr<PendingStyles> pendingStyles = mPendingStylesToApplyToNewContent;
    pendingStyles->OnSelectionChange(*this, aReason);

    // We used a class which derived from nsISelectionListener to call
    // HTMLEditor::RefreshEditingUI().  The lifetime of the class was
    // exactly same as mPendingStylesToApplyToNewContent.  So, call it only when
    // mPendingStylesToApplyToNewContent is not nullptr.
    if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
                    nsISelectionListener::KEYPRESS_REASON |
                    nsISelectionListener::SELECTALL_REASON)) &&
        aSelection) {
      // the selection changed and we need to check if we have to
      // hide and/or redisplay resizing handles
      // FYI: This is an XPCOM method.  So, the caller, Selection, guarantees
      //      the lifetime of this instance.  So, don't need to grab this with
      //      local variable.
      DebugOnly<nsresult> rv = RefreshEditingUI();
      NS_WARNING_ASSERTION(
          NS_SUCCEEDED(rv),
          "HTMLEditor::RefreshEditingUI() failed, but ignored");
    }
  }

  if (mComposerCommandsUpdater) {
    RefPtr<ComposerCommandsUpdater> updater = mComposerCommandsUpdater;
    updater->OnSelectionChange();
  }

  nsresult rv = EditorBase::NotifySelectionChanged(aDocument, aSelection,
                                                   aReason, aAmount);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::NotifySelectionChanged() failed");
  return rv;
}

void HTMLEditor::UpdateRootElement() {
  // Use the HTML documents body element as the editor root if we didn't
  // get a root element during initialization.

  mRootElement = GetBodyElement();
  if (!mRootElement) {
    RefPtr<Document> doc = GetDocument();
    if (doc) {
      // If there is no HTML body element,
      // we should use the document root element instead.
      mRootElement = doc->GetDocumentElement();
    }
    // else leave it null, for lack of anything better.
  }
}

nsresult HTMLEditor::FocusedElementOrDocumentBecomesEditable(
    Document& aDocument, Element* aElement) {
  MOZ_LOG(gHTMLEditorFocusLog, LogLevel::Info,
          ("%s(aDocument=%p, aElement=%s): mHasFocus=%s, mIsInDesignMode=%s, "
           "aDocument.IsInDesignMode()=%s, aElement->IsInDesignMode()=%s",
           __FUNCTION__, &aDocument, ToString(RefPtr{aElement}).c_str(),
           mHasFocus ? "true" : "false", mIsInDesignMode ? "true" : "false",
           aDocument.IsInDesignMode() ? "true" : "false",
           aElement ? (aElement->IsInDesignMode() ? "true" : "false") : "N/A"));

  const bool enteringInDesignMode =
      (aDocument.IsInDesignMode() && (!aElement || aElement->IsInDesignMode()));

  // If we should've already handled focus event, selection limiter should not
  // be set.  However, IMEStateManager is not notified the pseudo focus change
  // in this case. Therefore, we need to notify IMEStateManager of this.
  if (mHasFocus) {
    if (enteringInDesignMode) {
      mIsInDesignMode = true;
      return NS_OK;
    }
    // Although editor is already initialized due to re-used, ISM may not
    // create IME content observer yet. So we have to create it.
    IMEState newState;
    nsresult rv = GetPreferredIMEState(&newState);
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::GetPreferredIMEState() failed");
      mIsInDesignMode = false;
      return NS_OK;
    }
    const RefPtr<Element> focusedElement = GetFocusedElement();
    if (focusedElement) {
      MOZ_ASSERT(focusedElement == aElement);
      TextControlElement* const textControlElement =
          TextControlElement::FromNode(focusedElement);
      if (textControlElement &&
          textControlElement->IsSingleLineTextControlOrTextArea()) {
        // Let's emulate blur first.
        DebugOnly<nsresult> rv = FinalizeSelection();
        NS_WARNING_ASSERTION(
            NS_SUCCEEDED(rv),
            "HTMLEditor::FinalizeSelection() failed, but ignored");
        mHasFocus = false;
        mIsInDesignMode = false;
      }
      IMEStateManager::UpdateIMEState(newState, focusedElement, *this);
      // XXX Do we need to notify focused TextEditor of focus?  In theory,
      // the TextEditor should get focus event in this case.
    }
    mIsInDesignMode = false;
    return NS_OK;
  }

  // If we should be in the design mode, we want to handle focus event fired
  // on the document node.  Therefore, we should emulate it here.
  if (enteringInDesignMode) {
    MOZ_ASSERT(&aDocument == GetDocument());
    nsresult rv = OnFocus(aDocument);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::OnFocus() failed");
    return rv;
  }

  if (NS_WARN_IF(!aElement)) {
    return NS_ERROR_INVALID_ARG;
  }

  // Otherwise, we should've already handled focus event on the element,
  // therefore, we need to emulate it here.
  MOZ_ASSERT(nsFocusManager::GetFocusedElementStatic() == aElement);
  nsresult rv = OnFocus(*aElement);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::OnFocus() failed");

  // Note that we don't need to call
  // IMEStateManager::MaybeOnEditableStateDisabled here because
  // EditorBase::OnFocus must have already been called IMEStateManager::OnFocus
  // if succeeded. And perhaps, it's okay that IME is not enabled when
  // HTMLEditor fails to start handling since nobody can handle composition
  // events anyway...

  return rv;
}

nsresult HTMLEditor::OnFocus(const nsINode& aOriginalEventTargetNode) {
  MOZ_LOG(gHTMLEditorFocusLog, LogLevel::Info,
          ("%s(aOriginalEventTarget=%s): mIsInDesignMode=%s, "
           "aOriginalEventTargetNode.IsInDesignMode()=%s",
           __FUNCTION__, ToString(RefPtr{&aOriginalEventTargetNode}).c_str(),
           mIsInDesignMode ? "true" : "false",
           aOriginalEventTargetNode.IsInDesignMode() ? "true" : "false"));

  // Before doing anything, we should check whether the original target is still
  // valid focus event target because it may have already lost focus.
  if (!CanKeepHandlingFocusEvent(aOriginalEventTargetNode)) {
    return NS_OK;
  }

  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_FAILURE;
  }

  nsresult rv = EditorBase::OnFocus(aOriginalEventTargetNode);
  if (NS_FAILED(rv)) {
    NS_WARNING("EditorBase::OnFocus() failed");
    return rv;
  }
  mHasFocus = true;
  mIsInDesignMode = aOriginalEventTargetNode.IsInDesignMode();
  return NS_OK;
}

nsresult HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
    HTMLEditor* aHTMLEditor, Document& aDocument, Element* aElement) {
  MOZ_LOG(
      gHTMLEditorFocusLog, LogLevel::Info,
      ("%s(aHTMLEditor=%p, aDocument=%p, aElement=%s): "
       "aHTMLEditor->HasFocus()=%s, aHTMLEditor->IsInDesignMode()=%s, "
       "aDocument.IsInDesignMode()=%s, aElement->IsInDesignMode()=%s, "
       "nsFocusManager::GetFocusedElementStatic()=%s",
       __FUNCTION__, aHTMLEditor, &aDocument,
       ToString(RefPtr{aElement}).c_str(),
       aHTMLEditor ? (aHTMLEditor->HasFocus() ? "true" : "false") : "N/A",
       aHTMLEditor ? (aHTMLEditor->IsInDesignMode() ? "true" : "false") : "N/A",
       aDocument.IsInDesignMode() ? "true" : "false",
       aElement ? (aElement->IsInDesignMode() ? "true" : "false") : "N/A",
       ToString(RefPtr{nsFocusManager::GetFocusedElementStatic()}).c_str()));

  nsresult rv = [&]() MOZ_CAN_RUN_SCRIPT {
    // If HTMLEditor has not been created yet, we just need to adjust
    // IMEStateManager.  So, don't return error.
    if (!aHTMLEditor || !aHTMLEditor->HasFocus()) {
      return NS_OK;
    }

    // Let's emulate blur first.
    nsresult rv = aHTMLEditor->FinalizeSelection();
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                         "HTMLEditor::FinalizeSelection() failed");
    aHTMLEditor->mHasFocus = false;
    aHTMLEditor->mIsInDesignMode = false;

    RefPtr<Element> focusedElement = nsFocusManager::GetFocusedElementStatic();
    if (focusedElement && !focusedElement->IsInComposedDoc()) {
      // nsFocusManager may keep storing the focused element even after
      // disconnected from the tree, but HTMLEditor cannot work with editable
      // nodes not in a composed document.  Therefore, we should treat no
      // focused element in the case.
      focusedElement = nullptr;
    }
    TextControlElement* const focusedTextControlElement =
        TextControlElement::FromNodeOrNull(focusedElement);
    if ((focusedElement && focusedElement->IsEditable() &&
         focusedElement->OwnerDoc() == aHTMLEditor->GetDocument() &&
         (!focusedTextControlElement ||
          !focusedTextControlElement->IsSingleLineTextControlOrTextArea())) ||
        (!focusedElement && aDocument.IsInDesignMode())) {
      // Then, the focused element is still editable, let's emulate focus to
      // make the editor be ready to handle input.
      DebugOnly<nsresult> rvIgnored = aHTMLEditor->OnFocus(
          focusedElement ? static_cast<nsINode&>(*focusedElement)
                         : static_cast<nsINode&>(aDocument));
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                           "HTMLEditor::OnFocus() failed, but ignored");
    } else if (focusedTextControlElement &&
               focusedTextControlElement->IsSingleLineTextControlOrTextArea()) {
      if (const RefPtr<TextEditor> textEditor =
              focusedTextControlElement->GetExtantTextEditor()) {
        textEditor->OnFocus(*focusedElement);
      }
    }
    return rv;
  }();

  // If the element becomes not editable without focus change, IMEStateManager
  // does not have a chance to disable IME.  Therefore, (even if we fail to
  // handle the emulated blur/focus above,) we should notify IMEStateManager of
  // the editing state change.  Note that if the window of the HTMLEditor has
  // already lost focus, we don't need to do that and we should not touch the
  // other windows.
  if (const RefPtr<nsPresContext> presContext = aDocument.GetPresContext()) {
    const RefPtr<Element> focusedElementInDocument =
        Element::FromNodeOrNull(aDocument.GetUnretargetedFocusedContent());
    MOZ_ASSERT_IF(focusedElementInDocument,
                  focusedElementInDocument->GetPresContext(
                      Element::PresContextFor::eForComposedDoc));
    IMEStateManager::MaybeOnEditableStateDisabled(*presContext,
                                                  focusedElementInDocument);
  }

  return rv;
}

nsresult HTMLEditor::OnBlur(const EventTarget* aEventTarget) {
  MOZ_LOG(gHTMLEditorFocusLog, LogLevel::Info,
          ("%s(aEventTarget=%s): mHasFocus=%s, mIsInDesignMode=%s, "
           "aEventTarget->IsInDesignMode()=%s",
           __FUNCTION__, ToString(RefPtr{aEventTarget}).c_str(),
           mHasFocus ? "true" : "false", mIsInDesignMode ? "true" : "false",
           nsINode::FromEventTargetOrNull(aEventTarget)
               ? (nsINode::FromEventTarget(aEventTarget)->IsInDesignMode()
                      ? "true"
                      : "false")
               : "N/A"));
  const Element* eventTargetAsElement =
      Element::FromEventTargetOrNull(aEventTarget);

  // If another element already has focus, we should not maintain the selection
  // because we may not have the rights doing it.
  const Element* focusedElement = nsFocusManager::GetFocusedElementStatic();
  if (focusedElement && focusedElement != eventTargetAsElement) {
    // XXX If we had focus and new focused element is a text control, we may
    // need to notify focus of its TextEditor...
    mIsInDesignMode = false;
    mHasFocus = false;
    return NS_OK;
  }

  // If we're in the designMode and blur occurs, the target must be the document
  // node.  If a blur event is fired and the target is an element, it must be
  // delayed blur event at initializing the `HTMLEditor`.
  if (mIsInDesignMode && eventTargetAsElement &&
      eventTargetAsElement->IsInComposedDoc()) {
    return NS_OK;
  }

  nsresult rv = FinalizeSelection();
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::FinalizeSelection() failed");
  mIsInDesignMode = false;
  mHasFocus = false;
  return rv;
}

Element* HTMLEditor::FindSelectionRoot(const nsINode& aNode) const {
  MOZ_ASSERT(aNode.IsDocument() || aNode.IsContent(),
             "aNode must be content or document node");

  if (NS_WARN_IF(!aNode.IsInComposedDoc())) {
    return nullptr;
  }

  if (aNode.IsInDesignMode()) {
    return GetDocument()->GetRootElement();
  }

  nsIContent* content = const_cast<nsIContent*>(aNode.AsContent());
  if (!content->HasFlag(NODE_IS_EDITABLE)) {
    // If the content is in read-write state but is not editable itself,
    // return it as the selection root.
    if (content->IsElement() &&
        content->AsElement()->State().HasState(ElementState::READWRITE)) {
      return content->AsElement();
    }
    return nullptr;
  }

  // For non-readonly editors we want to find the root of the editable subtree
  // containing aContent.
  return content->GetEditingHost();
}

bool HTMLEditor::EntireDocumentIsEditable() const {
  Document* document = GetDocument();
  return document && document->GetDocumentElement() &&
         (document->GetDocumentElement()->IsEditable() ||
          (document->GetBody() && document->GetBody()->IsEditable()));
}

void HTMLEditor::CreateEventListeners() {
  // Don't create the handler twice
  if (!mEventListener) {
    mEventListener = new HTMLEditorEventListener();
  }
}

nsresult HTMLEditor::InstallEventListeners() {
  // FIXME InstallEventListeners() should not be called if we failed to set
  // document or create an event listener.  So, these checks should be
  // MOZ_DIAGNOSTIC_ASSERT instead.
  MOZ_ASSERT(GetDocument());
  if (MOZ_UNLIKELY(!GetDocument()) || NS_WARN_IF(!mEventListener)) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  // NOTE: HTMLEditor doesn't need to initialize mEventTarget here because
  // the target must be document node and it must be referenced as weak pointer.

  HTMLEditorEventListener* listener =
      reinterpret_cast<HTMLEditorEventListener*>(mEventListener.get());
  nsresult rv = listener->Connect(this);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "HTMLEditorEventListener::Connect() failed");
  return rv;
}

void HTMLEditor::Detach(
    const ComposerCommandsUpdater& aComposerCommandsUpdater) {
  MOZ_DIAGNOSTIC_ASSERT_IF(
      mComposerCommandsUpdater,
      &aComposerCommandsUpdater == mComposerCommandsUpdater);
  if (mComposerCommandsUpdater == &aComposerCommandsUpdater) {
    mComposerCommandsUpdater = nullptr;
    if (mTransactionManager) {
      mTransactionManager->Detach(*this);
    }
  }
}

NS_IMETHODIMP HTMLEditor::BeginningOfDocument() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  nsresult rv = MaybeCollapseSelectionAtFirstEditableNode(false);
  NS_WARNING_ASSERTION(
      NS_SUCCEEDED(rv),
      "HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(false) failed");
  return rv;
}

NS_IMETHODIMP HTMLEditor::EndOfDocument() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }
  nsresult rv = CollapseSelectionToEndOfLastLeafNodeOfDocument();
  NS_WARNING_ASSERTION(
      NS_SUCCEEDED(rv),
      "HTMLEditor::CollapseSelectionToEndOfLastLeafNodeOfDocument() failed");
  // This is low level API for embedders and chrome script so that we can return
  // raw error code here.
  return rv;
}

nsresult HTMLEditor::CollapseSelectionToEndOfLastLeafNodeOfDocument() const {
  MOZ_ASSERT(IsEditActionDataAvailable());

  // We should do nothing with the result of GetRoot() if only a part of the
  // document is editable.
  if (!EntireDocumentIsEditable()) {
    return NS_OK;
  }

  RefPtr<Element> bodyOrDocumentElement = GetRoot();
  if (NS_WARN_IF(!bodyOrDocumentElement)) {
    return NS_ERROR_NULL_POINTER;
  }

  auto pointToPutCaret = [&]() -> EditorRawDOMPoint {
    nsCOMPtr<nsIContent> lastLeafContent = HTMLEditUtils::GetLastLeafContent(
        *bodyOrDocumentElement, {LeafNodeType::OnlyLeafNode});
    if (!lastLeafContent) {
      return EditorRawDOMPoint::AtEndOf(*bodyOrDocumentElement);
    }
    // TODO: We should put caret into text node if it's visible.
    return lastLeafContent->IsText() ||
                   HTMLEditUtils::IsContainerNode(*lastLeafContent)
               ? EditorRawDOMPoint::AtEndOf(*lastLeafContent)
               : EditorRawDOMPoint(lastLeafContent);
  }();
  nsresult rv = CollapseSelectionTo(pointToPutCaret);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::CollapseSelectionTo() failed");
  return rv;
}

void HTMLEditor::InitializeSelectionAncestorLimit(
    Element& aAncestorLimit) const {
  MOZ_ASSERT(IsEditActionDataAvailable());

  // Hack for initializing selection.
  // HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode() will try to
  // collapse selection at first editable text node or inline element which
  // cannot have text nodes as its children.  However, selection has already
  // set into the new editing host by user, we should not change it.  For
  // solving this issue, we should do nothing if selection range is in active
  // editing host except it's not collapsed at start of the editing host since
  // aSelection.SetAncestorLimiter(aAncestorLimit) will collapse selection
  // at start of the new limiter if focus node of aSelection is outside of the
  // editing host.  However, we need to check here if selection is already
  // collapsed at start of the editing host because it's possible JS to do it.
  // In such case, we should not modify selection with calling
  // MaybeCollapseSelectionAtFirstEditableNode().

  // Basically, we should try to collapse selection at first editable node
  // in HTMLEditor.
  bool tryToCollapseSelectionAtFirstEditableNode = true;
  if (SelectionRef().RangeCount() == 1 && SelectionRef().IsCollapsed()) {
    Element* editingHost = ComputeEditingHost();
    const nsRange* range = SelectionRef().GetRangeAt(0);
    if (range->GetStartContainer() == editingHost && !range->StartOffset()) {
      // JS or user operation has already collapsed selection at start of
      // the editing host.  So, we don't need to try to change selection
      // in this case.
      tryToCollapseSelectionAtFirstEditableNode = false;
    }
  }

  EditorBase::InitializeSelectionAncestorLimit(aAncestorLimit);

  // XXX Do we need to check if we still need to change selection?  E.g.,
  //     we could have already lost focus while we're changing the ancestor
  //     limiter because it may causes "selectionchange" event.
  if (tryToCollapseSelectionAtFirstEditableNode) {
    DebugOnly<nsresult> rvIgnored =
        MaybeCollapseSelectionAtFirstEditableNode(true);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(true) failed, "
        "but ignored");
  }

  // If the target is a text control element, we won't handle user input
  // for the `TextEditor` in it.  However, we need to be open for `execCommand`.
  // Therefore, we shouldn't set ancestor limit in this case.
  // Note that we should do this once setting ancestor limiter for backward
  // compatiblity of select events, etc.  (Selection should be collapsed into
  // the text control element.)
  if (aAncestorLimit.HasIndependentSelection()) {
    SelectionRef().SetAncestorLimiter(nullptr);
  }
}

nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
    bool aIgnoreIfSelectionInEditingHost) const {
  MOZ_ASSERT(IsEditActionDataAvailable());

  RefPtr<Element> editingHost = ComputeEditingHost(LimitInBodyElement::No);
  if (NS_WARN_IF(!editingHost)) {
    return NS_OK;
  }

  // If selection range is already in the editing host and the range is not
  // start of the editing host, we shouldn't reset selection.  E.g., window
  // is activated when the editor had focus before inactivated.
  if (aIgnoreIfSelectionInEditingHost && SelectionRef().RangeCount() == 1) {
    const nsRange* range = SelectionRef().GetRangeAt(0);
    if (!range->Collapsed() ||
        range->GetStartContainer() != editingHost.get() ||
        range->StartOffset()) {
      return NS_OK;
    }
  }

  for (nsIContent* leafContent = HTMLEditUtils::GetFirstLeafContent(
           *editingHost,
           {LeafNodeType::LeafNodeOrNonEditableNode,
            LeafNodeType::LeafNodeOrChildBlock},
           BlockInlineCheck::UseComputedDisplayStyle, editingHost);
       leafContent;) {
    // If we meet a non-editable node first, we should move caret to start
    // of the container block or editing host.
    if (!EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) {
      MOZ_ASSERT(leafContent->GetParent());
      MOZ_ASSERT(EditorUtils::IsEditableContent(*leafContent->GetParent(),
                                                EditorType::HTML));
      if (const Element* editableBlockElementOrInlineEditingHost =
              HTMLEditUtils::GetAncestorElement(
                  *leafContent,
                  HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost,
                  BlockInlineCheck::UseComputedDisplayStyle)) {
        nsresult rv = CollapseSelectionTo(
            EditorDOMPoint(editableBlockElementOrInlineEditingHost, 0));
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                             "EditorBase::CollapseSelectionTo() failed");
        return rv;
      }
      NS_WARNING("Found leaf content did not have editable parent, why?");
      return NS_ERROR_FAILURE;
    }

    // When we meet an empty inline element, we should look for a next sibling.
    // For example, if current editor is:
    // <div contenteditable><span></span><b><br></b></div>
    // then, we should put caret at the <br> element.  So, let's check if found
    // node is an empty inline container element.
    if (Element* leafElement = Element::FromNode(leafContent)) {
      if (HTMLEditUtils::IsInlineContent(
              *leafElement, BlockInlineCheck::UseComputedDisplayStyle) &&
          !HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafElement) &&
          HTMLEditUtils::CanNodeContain(*leafElement,
                                        *nsGkAtoms::textTagName)) {
        // Chromium collapses selection to start of the editing host when this
        // is the last leaf content.  So, we don't need special handling here.
        leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
            *leafElement,
            {LeafNodeType::LeafNodeOrNonEditableNode,
             LeafNodeType::LeafNodeOrChildBlock},
            BlockInlineCheck::UseComputedDisplayStyle, editingHost);
        continue;
      }
    }

    if (Text* text = leafContent->GetAsText()) {
      // If there is editable and visible text node, move caret at first of
      // the visible character.
      const WSScanResult scanResultInTextNode =
          WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
              WSRunScanner::Scan::EditableNodes, EditorRawDOMPoint(text, 0),
              BlockInlineCheck::UseComputedDisplayStyle);
      if ((scanResultInTextNode.InVisibleOrCollapsibleCharacters() ||
           scanResultInTextNode.ReachedPreformattedLineBreak()) &&
          scanResultInTextNode.TextPtr() == text) {
        nsresult rv = CollapseSelectionTo(
            scanResultInTextNode.PointAtReachedContent<EditorRawDOMPoint>());
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                             "EditorBase::CollapseSelectionTo() failed");
        return rv;
      }
      // If it's an invisible text node, keep scanning next leaf.
      leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
          *leafContent,
          {LeafNodeType::LeafNodeOrNonEditableNode,
           LeafNodeType::LeafNodeOrChildBlock},
          BlockInlineCheck::UseComputedDisplayStyle, editingHost);
      continue;
    }

    // If there is editable <br> or something void element like <img>, <input>,
    // <hr> etc, move caret before it.
    if (!HTMLEditUtils::CanNodeContain(*leafContent, *nsGkAtoms::textTagName) ||
        HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafContent)) {
      MOZ_ASSERT(leafContent->GetParent());
      if (EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) {
        nsresult rv = CollapseSelectionTo(EditorDOMPoint(leafContent));
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                             "EditorBase::CollapseSelectionTo() failed");
        return rv;
      }
      MOZ_ASSERT_UNREACHABLE(
          "How do we reach editable leaf in non-editable element?");
      // But if it's not editable, let's put caret at start of editing host
      // for now.
      nsresult rv = CollapseSelectionTo(EditorDOMPoint(editingHost, 0));
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                           "EditorBase::CollapseSelectionTo() failed");
      return rv;
    }

    // If we meet non-empty block element, we need to scan its child too.
    if (HTMLEditUtils::IsBlockElement(
            *leafContent, BlockInlineCheck::UseComputedDisplayStyle) &&
        !HTMLEditUtils::IsEmptyNode(
            *leafContent,
            {EmptyCheckOption::TreatSingleBRElementAsVisible,
             EmptyCheckOption::TreatNonEditableContentAsInvisible}) &&
        !HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafContent)) {
      leafContent = HTMLEditUtils::GetFirstLeafContent(
          *leafContent,
          {LeafNodeType::LeafNodeOrNonEditableNode,
           LeafNodeType::LeafNodeOrChildBlock},
          BlockInlineCheck::UseComputedDisplayStyle, editingHost);
      continue;
    }

    // Otherwise, we must meet an empty block element or a data node like
    // comment node.  Let's ignore it.
    leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
        *leafContent,
        {LeafNodeType::LeafNodeOrNonEditableNode,
         LeafNodeType::LeafNodeOrChildBlock},
        BlockInlineCheck::UseComputedDisplayStyle, editingHost);
  }

  // If there is no visible/editable node except another block element in
  // current editing host, we should move caret to very first of the editing
  // host.
  // XXX This may not make sense, but Chromium behaves so.  Therefore, the
  //     reason why we do this is just compatibility with Chromium.
  nsresult rv = CollapseSelectionTo(EditorDOMPoint(editingHost, 0));
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::CollapseSelectionTo() failed");
  return rv;
}

void HTMLEditor::PreHandleMouseDown(const MouseEvent& aMouseDownEvent) {
  if (mPendingStylesToApplyToNewContent) {
    // mPendingStylesToApplyToNewContent will be notified of selection change
    // even if aMouseDownEvent is not an acceptable event for this editor.
    // Therefore, we need to notify it of this event too.
    mPendingStylesToApplyToNewContent->PreHandleMouseEvent(aMouseDownEvent);
  }
}

void HTMLEditor::PreHandleMouseUp(const MouseEvent& aMouseUpEvent) {
  if (mPendingStylesToApplyToNewContent) {
    // mPendingStylesToApplyToNewContent will be notified of selection change
    // even if aMouseUpEvent is not an acceptable event for this editor.
    // Therefore, we need to notify it of this event too.
    mPendingStylesToApplyToNewContent->PreHandleMouseEvent(aMouseUpEvent);
  }
}

void HTMLEditor::PreHandleSelectionChangeCommand(Command aCommand) {
  if (mPendingStylesToApplyToNewContent) {
    mPendingStylesToApplyToNewContent->PreHandleSelectionChangeCommand(
        aCommand);
  }
}

void HTMLEditor::PostHandleSelectionChangeCommand(Command aCommand) {
  if (!mPendingStylesToApplyToNewContent) {
    return;
  }

  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (!editActionData.CanHandle()) {
    return;
  }
  mPendingStylesToApplyToNewContent->PostHandleSelectionChangeCommand(*this,
                                                                      aCommand);
}

nsresult HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
  // NOTE: When you change this method, you should also change:
  //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
  if (NS_WARN_IF(!aKeyboardEvent)) {
    return NS_ERROR_UNEXPECTED;
  }

  if (IsReadonly()) {
    HandleKeyPressEventInReadOnlyMode(*aKeyboardEvent);
    return NS_OK;
  }

  MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
             "HandleKeyPressEvent gets non-keypress event");

  switch (aKeyboardEvent->mKeyCode) {
    case NS_VK_META:
    case NS_VK_WIN:
    case NS_VK_SHIFT:
    case NS_VK_CONTROL:
    case NS_VK_ALT:
      // FYI: This shouldn't occur since modifier key shouldn't cause eKeyPress
      //      event.
      aKeyboardEvent->PreventDefault();
      return NS_OK;

    case NS_VK_BACK:
    case NS_VK_DELETE: {
      nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                           "EditorBase::HandleKeyPressEvent() failed");
      return rv;
    }
    case NS_VK_TAB: {
      // Basically, "Tab" key be used only for focus navigation.
      // FYI: In web apps, this is always true.
      if (IsTabbable()) {
        return NS_OK;
      }

      // If we're in the plaintext mode, and not tabbable editor, let's
      // insert a horizontal tabulation.
      if (IsPlaintextMailComposer()) {
        if (aKeyboardEvent->IsShift() || aKeyboardEvent->IsControl() ||
            aKeyboardEvent->IsAlt() || aKeyboardEvent->IsMeta()) {
          return NS_OK;
        }

        // else we insert the tab straight through
        aKeyboardEvent->PreventDefault();
        nsresult rv = OnInputText(u"\t"_ns);
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                             "EditorBase::OnInputText(\\t) failed");
        return rv;
      }

      // Otherwise, e.g., we're an embedding editor in chrome, we can handle
      // "Tab" key as an input.
      if (aKeyboardEvent->IsControl() || aKeyboardEvent->IsAlt() ||
          aKeyboardEvent->IsMeta()) {
        return NS_OK;
      }

      RefPtr<Selection> selection = GetSelection();
      if (NS_WARN_IF(!selection) || NS_WARN_IF(!selection->RangeCount())) {
        return NS_ERROR_FAILURE;
      }

      nsINode* startContainer = selection->GetRangeAt(0)->GetStartContainer();
      MOZ_ASSERT(startContainer);
      if (!startContainer->IsContent()) {
        break;
      }

      const Element* editableBlockElement =
          HTMLEditUtils::GetInclusiveAncestorElement(
              *startContainer->AsContent(),
              HTMLEditUtils::ClosestEditableBlockElement,
              BlockInlineCheck::UseComputedDisplayOutsideStyle);
      if (!editableBlockElement) {
        break;
      }

      // If selection is in a table element, we need special handling.
      if (HTMLEditUtils::IsAnyTableElement(editableBlockElement)) {
        Result<EditActionResult, nsresult> result =
            HandleTabKeyPressInTable(aKeyboardEvent);
        if (MOZ_UNLIKELY(result.isErr())) {
          NS_WARNING("HTMLEditor::HandleTabKeyPressInTable() failed");
          return EditorBase::ToGenericNSResult(result.unwrapErr());
        }
        if (!result.inspect().Handled()) {
          return NS_OK;
        }
        nsresult rv = ScrollSelectionFocusIntoView();
        NS_WARNING_ASSERTION(
            NS_SUCCEEDED(rv),
            "EditorBase::ScrollSelectionFocusIntoView() failed");
        return EditorBase::ToGenericNSResult(rv);
      }

      // If selection is in an list item element, treat it as indent or outdent.
      if (HTMLEditUtils::IsListItem(editableBlockElement)) {
        aKeyboardEvent->PreventDefault();
        if (!aKeyboardEvent->IsShift()) {
          nsresult rv = IndentAsAction();
          NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                               "HTMLEditor::IndentAsAction() failed");
          return EditorBase::ToGenericNSResult(rv);
        }
        nsresult rv = OutdentAsAction();
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                             "HTMLEditor::OutdentAsAction() failed");
        return EditorBase::ToGenericNSResult(rv);
      }

      // If only "Tab" key is pressed in normal context, just treat it as
      // horizontal tab character input.
      if (aKeyboardEvent->IsShift()) {
        return NS_OK;
      }
      aKeyboardEvent->PreventDefault();
      nsresult rv = OnInputText(u"\t"_ns);
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                           "EditorBase::OnInputText(\\t) failed");
      return EditorBase::ToGenericNSResult(rv);
    }
    case NS_VK_RETURN: {
      if (!aKeyboardEvent->IsInputtingLineBreak()) {
        return NS_OK;
      }
      // Anyway consume the event even if we cannot handle it actually because
      // we've already checked whether the an editing host has focus.
      aKeyboardEvent->PreventDefault();
      const RefPtr<Element> editingHost =
          ComputeEditingHost(LimitInBodyElement::No);
      if (NS_WARN_IF(!editingHost)) {
        return NS_ERROR_UNEXPECTED;
      }
      // Shift + Enter should insert a <br> or a LF instead of splitting current
      // paragraph.  Additionally, if we're in plaintext-only mode, we should
      // do so because Chrome does so, but execCommand("insertParagraph") keeps
      // working as contenteditable=true.  So, we cannot redirect in
      // InsertParagraphSeparatorAsAction().
      if (aKeyboardEvent->IsShift() ||
          editingHost->IsContentEditablePlainTextOnly()) {
        // Only inserts a <br> element.
        nsresult rv = InsertLineBreakAsAction();
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                             "HTMLEditor::InsertLineBreakAsAction() failed");
        return EditorBase::ToGenericNSResult(rv);
      }
      // uses rules to figure out what to insert
      nsresult rv = InsertParagraphSeparatorAsAction();
      NS_WARNING_ASSERTION(
          NS_SUCCEEDED(rv),
          "HTMLEditor::InsertParagraphSeparatorAsAction() failed");
      return EditorBase::ToGenericNSResult(rv);
    }
  }

  if (!aKeyboardEvent->IsInputtingText()) {
    // we don't PreventDefault() here or keybindings like control-x won't work
    return NS_OK;
  }
  aKeyboardEvent->PreventDefault();
  // If we dispatch 2 keypress events for a surrogate pair and we set only
  // first `.key` value to the surrogate pair, the preceding one has it and the
  // other has empty string.  In this case, we should handle only the first one
  // with the key value.
  if (!StaticPrefs::dom_event_keypress_dispatch_once_per_surrogate_pair() &&
      !StaticPrefs::dom_event_keypress_key_allow_lone_surrogate() &&
      aKeyboardEvent->mKeyValue.IsEmpty() &&
      IS_SURROGATE(aKeyboardEvent->mCharCode)) {
    return NS_OK;
  }
  nsAutoString str(aKeyboardEvent->mKeyValue);
  if (str.IsEmpty()) {
    str.Assign(static_cast<char16_t>(aKeyboardEvent->mCharCode));
  }
  // FYI: DIfferent from TextEditor, we can treat \r (CR) as-is in HTMLEditor.
  nsresult rv = OnInputText(str);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::OnInputText() failed");
  return rv;
}

NS_IMETHODIMP HTMLEditor::NodeIsBlock(nsINode* aNode, bool* aIsBlock) {
  if (NS_WARN_IF(!aNode)) {
    return NS_ERROR_INVALID_ARG;
  }
  if (MOZ_UNLIKELY(!aNode->IsElement())) {
    *aIsBlock = false;
    return NS_OK;
  }
  // If the node is in composed doc, we'll refer its style.  If we don't flush
  // pending style here, another API call may change the style.  Therefore,
  // let's flush the pending style changes right now.
  if (aNode->IsInComposedDoc()) {
    if (RefPtr<PresShell> presShell = GetPresShell()) {
      presShell->FlushPendingNotifications(FlushType::Style);
    }
  }
  *aIsBlock = HTMLEditUtils::IsBlockElement(
      *aNode->AsElement(), BlockInlineCheck::UseComputedDisplayOutsideStyle);
  return NS_OK;
}

NS_IMETHODIMP HTMLEditor::UpdateBaseURL() {
  RefPtr<Document> document = GetDocument();
  if (NS_WARN_IF(!document)) {
    return NS_ERROR_FAILURE;
  }

  // Look for an HTML <base> tag
  RefPtr<nsContentList> baseElementList =
      document->GetElementsByTagName(u"base"_ns);

  // If no base tag, then set baseURL to the document's URL.  This is very
  // important, else relative URLs for links and images are wrong
  if (!baseElementList || !baseElementList->Item(0)) {
    document->SetBaseURI(document->GetDocumentURI());
  }
  return NS_OK;
}

NS_IMETHODIMP HTMLEditor::InsertLineBreak() {
  // XPCOM method's InsertLineBreak() should insert paragraph separator in
  // HTMLEditor.
  AutoEditActionDataSetter editActionData(
      *this, EditAction::eInsertParagraphSeparator);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
  if (NS_FAILED(rv)) {
    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  const RefPtr<Element> editingHost =
      ComputeEditingHost(LimitInBodyElement::No);
  if (!editingHost) {
    return NS_SUCCESS_DOM_NO_OPERATION;
  }

  Result<EditActionResult, nsresult> result =
      InsertParagraphSeparatorAsSubAction(*editingHost);
  if (MOZ_UNLIKELY(result.isErr())) {
    NS_WARNING("HTMLEditor::InsertParagraphSeparatorAsSubAction() failed");
    return EditorBase::ToGenericNSResult(result.unwrapErr());
  }
  return NS_OK;
}

nsresult HTMLEditor::InsertLineBreakAsAction(nsIPrincipal* aPrincipal) {
  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak,
                                          aPrincipal);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
  if (NS_FAILED(rv)) {
    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  if (IsSelectionRangeContainerNotContent()) {
    return NS_SUCCESS_DOM_NO_OPERATION;
  }

  rv = InsertLineBreakAsSubAction();
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "HTMLEditor::InsertLineBreakAsSubAction() failed");
  // Don't return NS_SUCCESS_DOM_NO_OPERATION for compatibility of `execCommand`
  // result of Chrome.
  return NS_FAILED(rv) ? rv : NS_OK;
}

nsresult HTMLEditor::InsertParagraphSeparatorAsAction(
    nsIPrincipal* aPrincipal) {
  AutoEditActionDataSetter editActionData(
      *this, EditAction::eInsertParagraphSeparator, aPrincipal);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
  if (NS_FAILED(rv)) {
    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  const RefPtr<Element> editingHost =
      ComputeEditingHost(LimitInBodyElement::No);
  if (!editingHost) {
    return NS_SUCCESS_DOM_NO_OPERATION;
  }

  Result<EditActionResult, nsresult> result =
      InsertParagraphSeparatorAsSubAction(*editingHost);
  if (MOZ_UNLIKELY(result.isErr())) {
    NS_WARNING("HTMLEditor::InsertParagraphSeparatorAsSubAction() failed");
    return EditorBase::ToGenericNSResult(result.unwrapErr());
  }
  return NS_OK;
}

Result<EditActionResult, nsresult> HTMLEditor::HandleTabKeyPressInTable(
    WidgetKeyboardEvent* aKeyboardEvent) {
  MOZ_ASSERT(aKeyboardEvent);

  AutoEditActionDataSetter dummyEditActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!dummyEditActionData.CanHandle())) {
    // Do nothing if we didn't find a table cell.
    return EditActionResult::IgnoredResult();
  }

  // Find enclosing table cell from selection (cell may be selected element)
  const RefPtr<Element> cellElement =
      GetInclusiveAncestorByTagNameAtSelection(*nsGkAtoms::td);
  if (!cellElement) {
    NS_WARNING(
        "HTMLEditor::GetInclusiveAncestorByTagNameAtSelection(*nsGkAtoms::td) "
        "returned nullptr");
    // Do nothing if we didn't find a table cell.
    return EditActionResult::IgnoredResult();
  }

  // find enclosing table
  RefPtr<Element> table =
      HTMLEditUtils::GetClosestAncestorTableElement(*cellElement);
  if (!table) {
    NS_WARNING("HTMLEditor::GetClosestAncestorTableElement() failed");
    return EditActionResult::IgnoredResult();
  }

  // advance to next cell
  // first create an iterator over the table
  PostContentIterator postOrderIter;
  nsresult rv = postOrderIter.Init(table);
  if (NS_FAILED(rv)) {
    NS_WARNING("PostContentIterator::Init() failed");
    return Err(rv);
  }
  // position postOrderIter at block
  rv = postOrderIter.PositionAt(cellElement);
  if (NS_FAILED(rv)) {
    NS_WARNING("PostContentIterator::PositionAt() failed");
    return Err(rv);
  }

  do {
    if (aKeyboardEvent->IsShift()) {
      postOrderIter.Prev();
    } else {
      postOrderIter.Next();
    }

    nsCOMPtr<nsINode> node = postOrderIter.GetCurrentNode();
    if (node && HTMLEditUtils::IsTableCell(node) &&
        HTMLEditUtils::GetClosestAncestorTableElement(*node->AsElement()) ==
            table) {
      aKeyboardEvent->PreventDefault();
      CollapseSelectionToDeepestNonTableFirstChild(node);
      if (NS_WARN_IF(Destroyed())) {
        return Err(NS_ERROR_EDITOR_DESTROYED);
      }
      return EditActionResult::HandledResult();
    }
  } while (!postOrderIter.IsDone());

  if (aKeyboardEvent->IsShift()) {
    return EditActionResult::IgnoredResult();
  }

  // If we haven't handled it yet, then we must have run off the end of the
  // table.  Insert a new row.
  // XXX We should investigate whether this behavior is supported by other
  //     browsers later.
  AutoEditActionDataSetter editActionData(*this,
--> --------------------

--> maximum size reached

--> --------------------

99%


¤ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.