Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  HTMLEditorInsertParagraphHandler.cpp

  Sprache: C
 

/* 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 "EditorBase.h"
#include "HTMLEditor.h"
#include "HTMLEditorInlines.h"
#include "HTMLEditorNestedClasses.h"

#include <utility>

#include "AutoClonedRangeArray.h"
#include "CSSEditUtils.h"
#include "EditAction.h"
#include "EditorDOMPoint.h"
#include "EditorLineBreak.h"
#include "EditorUtils.h"
#include "HTMLEditHelpers.h"
#include "HTMLEditUtils.h"
#include "PendingStyles.h"  // for SpecifiedStyle
#include "WhiteSpaceVisibilityKeeper.h"
#include "WSRunScanner.h"

#include "ErrorList.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/ContentIterator.h"
#include "mozilla/EditorForwards.h"
#include "mozilla/Maybe.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLBRElement.h"
#include "mozilla/dom/Selection.h"
#include "nsAtom.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsINode.h"
#include "nsTArray.h"
#include "nsTextNode.h"

namespace mozilla {

using namespace dom;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
using EmptyCheckOptions = HTMLEditUtils::EmptyCheckOptions;
using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
using LeafNodeOptions = HTMLEditUtils::LeafNodeOptions;

Result<EditActionResult, nsresult>
HTMLEditor::InsertParagraphSeparatorAsSubAction(const Element& aEditingHost) {
  if (NS_WARN_IF(!mInitSucceeded)) {
    return Err(NS_ERROR_NOT_INITIALIZED);
  }

  {
    Result<EditActionResult, nsresult> result =
        CanHandleHTMLEditSubAction(CheckSelectionInReplacedElement::No);
    if (MOZ_UNLIKELY(result.isErr())) {
      NS_WARNING("HTMLEditor::CanHandleHTMLEditSubAction() failed");
      return result;
    }
    if (result.inspect().Canceled()) {
      return result;
    }
  }

  if (GetEditContext()) {
    // Don't insert paragraph if there is an EditContext
    return EditActionResult::HandledResult();
  }

  // XXX This may be called by execCommand() with "insertParagraph".
  //     In such case, naming the transaction "TypingTxnName" is odd.
  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName,
                                             ScrollSelectionIntoView::Yes,
                                             __FUNCTION__);

  IgnoredErrorResult ignoredError;
  AutoEditSubActionNotifier startToHandleEditSubAction(
      *this, EditSubAction::eInsertParagraphSeparator, nsIEditor::eNext,
      ignoredError);
  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
    return Err(ignoredError.StealNSResult());
  }
  NS_WARNING_ASSERTION(
      !ignoredError.Failed(),
      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");

  UndefineCaretBidiLevel();

  // If the selection isn't collapsed, delete it.
  if (!SelectionRef().IsCollapsed()) {
    nsresult rv =
        DeleteSelectionAsSubAction(nsIEditor::eNone, nsIEditor::eStrip);
    if (NS_FAILED(rv)) {
      NS_WARNING(
          "EditorBase::DeleteSelectionAsSubAction(eNone, eStrip) failed");
      return Err(rv);
    }
  }

  AutoInsertParagraphHandler insertParagraphHandler(*this, aEditingHost);
  Result<EditActionResult, nsresult> insertParagraphResult =
      insertParagraphHandler.Run();
  NS_WARNING_ASSERTION(insertParagraphResult.isOk(),
                       "AutoInsertParagraphHandler::Run() failed");
  return insertParagraphResult;
}

Result<EditActionResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::Run() {
  MOZ_ASSERT(mHTMLEditor.IsEditActionDataAvailable());
  MOZ_ASSERT(mHTMLEditor.IsTopLevelEditSubActionDataAvailable());

  nsresult rv = mHTMLEditor.EnsureNoPaddingBRElementForEmptyEditor();
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
    return Err(NS_ERROR_EDITOR_DESTROYED);
  }
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::EnsureNoPaddingBRElementForEmptyEditor() "
                       "failed, but ignored");

  if (NS_SUCCEEDED(rv) && mHTMLEditor.SelectionRef().IsCollapsed()) {
    nsresult rv =
        mHTMLEditor.EnsureCaretNotAfterInvisibleBRElement(mEditingHost);
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
      return Err(NS_ERROR_EDITOR_DESTROYED);
    }
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                         "HTMLEditor::EnsureCaretNotAfterInvisibleBRElement() "
                         "failed, but ignored");
    if (NS_SUCCEEDED(rv)) {
      nsresult rv = mHTMLEditor.PrepareInlineStylesForCaret();
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
        return Err(NS_ERROR_EDITOR_DESTROYED);
      }
      NS_WARNING_ASSERTION(
          NS_SUCCEEDED(rv),
          "HTMLEditor::PrepareInlineStylesForCaret() failed, but ignored");
    }
  }

  AutoClonedSelectionRangeArray selectionRanges(mHTMLEditor.SelectionRef());
  selectionRanges.EnsureOnlyEditableRanges(mEditingHost);

  auto pointToInsert =
      selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
  if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
    return Err(NS_ERROR_FAILURE);
  }
  // If the element can have a <br> element (it means that the element or its
  // container must be able to have <div> or <p> too), we can handle
  // insertParagraph at the point.
  pointToInsert = HTMLEditUtils::GetPossiblePointToInsert(
      pointToInsert, *nsGkAtoms::br, mEditingHost);
  if (NS_WARN_IF(!pointToInsert.IsSet())) {
    return Err(NS_ERROR_FAILURE);
  }
  MOZ_ASSERT(pointToInsert.IsInContentNode());

  if (mHTMLEditor.IsMailEditor()) {
    if (const RefPtr<Element> mailCiteElement =
            mHTMLEditor.GetMostDistantAncestorMailCiteElement(
                *pointToInsert.ContainerAs<nsIContent>())) {
      // Split any mailcites in the way.  Should we abort this if we encounter
      // table cell boundaries?
      Result<CaretPoint, nsresult> caretPointOrError =
          HandleInMailCiteElement(*mailCiteElement, pointToInsert);
      if (MOZ_UNLIKELY(caretPointOrError.isErr())) {
        NS_WARNING(
            "AutoInsertParagraphHandler::HandleInMailCiteElement() failed");
        return caretPointOrError.propagateErr();
      }
      CaretPoint caretPoint = caretPointOrError.unwrap();
      MOZ_ASSERT(caretPoint.HasCaretPointSuggestion());
      MOZ_ASSERT(caretPoint.CaretPointRef().GetInterlinePosition() ==
                 InterlinePosition::StartOfNextLine);
      MOZ_ASSERT(caretPoint.CaretPointRef().GetChild());
      MOZ_ASSERT(
          caretPoint.CaretPointRef().GetChild()->IsHTMLElement(nsGkAtoms::br));
      nsresult rv = caretPoint.SuggestCaretPointTo(mHTMLEditor, {});
      if (NS_FAILED(rv)) {
        NS_WARNING("CaretPoint::SuggestCaretPointTo() failed");
        return Err(rv);
      }
      return EditActionResult::HandledResult();
    }
  }

  // If the active editing host is an inline element, or if the active editing
  // host is the block parent itself and we're configured to use <br> as a
  // paragraph separator, just append a <br>.
  // If the editing host parent element is editable, it means that the editing
  // host must be a <body> element and the selection may be outside the body
  // element.  If the selection is outside the editing host, we should not
  // insert new paragraph nor <br> element.
  // XXX Currently, we don't support editing outside <body> element, but Blink
  //     does it.
  if (mEditingHost.GetParentElement() &&
      HTMLEditUtils::IsSimplyEditableNode(*mEditingHost.GetParentElement()) &&
      !nsContentUtils::ContentIsFlattenedTreeDescendantOf(
          pointToInsert.ContainerAs<nsIContent>(), &mEditingHost)) {
    return Err(NS_ERROR_EDITOR_NO_EDITABLE_RANGE);
  }

  // Look for the nearest parent block.  However, don't return error even if
  // there is no block parent here because in such case, i.e., editing host
  // is an inline element, we should insert <br> simply.
  RefPtr<Element> editableBlockElement =
      HTMLEditUtils::GetInclusiveAncestorElement(
          *pointToInsert.ContainerAs<nsIContent>(),
          HTMLEditUtils::ClosestEditableBlockElementOrButtonElement,
          BlockInlineCheck::UseComputedDisplayOutsideStyle);

  // If we cannot insert a <p>/<div> element at the selection, we should insert
  // a <br> element or a linefeed instead.
  if (ShouldInsertLineBreakInstead(editableBlockElement, pointToInsert)) {
    const Maybe<LineBreakType> lineBreakType =
        mHTMLEditor.GetPreferredLineBreakType(
            *pointToInsert.ContainerAs<nsIContent>(), mEditingHost);
    if (MOZ_UNLIKELY(!lineBreakType)) {
      // Cannot insert a line break there.
      return EditActionResult::IgnoredResult();
    }
    if (lineBreakType.value() == LineBreakType::Linefeed) {
      Result<EditActionResult, nsresult> insertLinefeedResultOrError =
          HandleInsertLinefeed(pointToInsert);
      NS_WARNING_ASSERTION(
          insertLinefeedResultOrError.isOk(),
          "AutoInsertParagraphHandler::HandleInsertLinefeed() failed");
      return insertLinefeedResultOrError;
    }
    Result<EditActionResult, nsresult> insertBRElementResultOrError =
        HandleInsertBRElement(pointToInsert);
    NS_WARNING_ASSERTION(
        insertBRElementResultOrError.isOk(),
        "AutoInsertParagraphHandler::HandleInsertBRElement() failed");
    return insertBRElementResultOrError;
  }

  RefPtr<Element> blockElementToPutCaret;
  // If the default paragraph separator is not <br> and selection is not in
  // a splittable block element, we should wrap selected contents in a new
  // paragraph, then, split it.
  if (!HTMLEditUtils::IsSplittableNode(*editableBlockElement) &&
      mDefaultParagraphSeparator != ParagraphSeparator::br) {
    MOZ_ASSERT(mDefaultParagraphSeparator == ParagraphSeparator::div ||
               mDefaultParagraphSeparator == ParagraphSeparator::p);
    // FIXME: If there is no splittable block element, the other browsers wrap
    // the right nodes into new paragraph, but keep the left node as-is.
    // We should follow them to make here simpler and better compatibility.
    Result<RefPtr<Element>, nsresult> suggestBlockElementToPutCaretOrError =
        mHTMLEditor.FormatBlockContainerWithTransaction(
            selectionRanges,
            MOZ_KnownLive(HTMLEditor::ToParagraphSeparatorTagName(
                mDefaultParagraphSeparator)),
            // For keeping the traditional behavior at insertParagraph command,
            // let's use the XUL paragraph state command targets even if we're
            // handling HTML insertParagraph command.
            FormatBlockMode::XULParagraphStateCommand, mEditingHost);
    if (MOZ_UNLIKELY(suggestBlockElementToPutCaretOrError.isErr())) {
      NS_WARNING("HTMLEditor::FormatBlockContainerWithTransaction() failed");
      return suggestBlockElementToPutCaretOrError.propagateErr();
    }
    if (selectionRanges.HasSavedRanges()) {
      selectionRanges.RestoreFromSavedRanges();
    }
    pointToInsert = selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
    if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    }
    MOZ_ASSERT(pointToInsert.IsSetAndValidInComposedDoc());

    editableBlockElement = HTMLEditUtils::GetInclusiveAncestorElement(
        *pointToInsert.ContainerAs<nsIContent>(),
        HTMLEditUtils::ClosestEditableBlockElementOrButtonElement,
        BlockInlineCheck::UseComputedDisplayOutsideStyle);
    if (NS_WARN_IF(!editableBlockElement)) {
      return Err(NS_ERROR_UNEXPECTED);
    }
    if (!HTMLEditUtils::IsSplittableNode(*editableBlockElement)) {
      // Didn't create a new block for some reason, e.g., it's a inline-block,
      // fall back to <br>.
      Result<EditActionResult, nsresult> insertBRElementResultOrError =
          HandleInsertBRElement(pointToInsert, blockElementToPutCaret);
      NS_WARNING_ASSERTION(
          insertBRElementResultOrError.isOk(),
          "AutoInsertParagraphHandler::HandleInsertBRElement() failed");
      return insertBRElementResultOrError;
    }
    // We want to collapse selection in the editable block element.
    blockElementToPutCaret = editableBlockElement;
  }

  // If block is empty, populate with br.  (For example, imagine a div that
  // contains the word "text".  The user selects "text" and types return.
  // "Text" is deleted leaving an empty block.  We want to put in one br to
  // make block have a line.  Then code further below will put in a second br.)
  RefPtr<Element> insertedPaddingBRElement;
  {
    Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
        InsertBRElementIfEmptyBlockElement(
            *editableBlockElement, InsertBRElementIntoEmptyBlock::End,
            BlockInlineCheck::UseComputedDisplayOutsideStyle);
    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
      NS_WARNING(
          "AutoInsertParagraphHandler::InsertBRElementIfEmptyBlockElement("
          "InsertBRElementIntoEmptyBlock::End, "
          "BlockInlineCheck::UseComputedDisplayOutsideStyle) failed");
      return insertBRElementResultOrError.propagateErr();
    }

    CreateLineBreakResult insertBRElementResult =
        insertBRElementResultOrError.unwrap();
    insertBRElementResult.IgnoreCaretPointSuggestion();
    if (insertBRElementResult.Handled()) {
      insertedPaddingBRElement = &insertBRElementResult->BRElementRef();
    }

    pointToInsert = selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
    if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    }
  }

  RefPtr<Element> maybeNonEditableListItem =
      HTMLEditUtils::GetClosestInclusiveAncestorListItemElement(
          *editableBlockElement, &mEditingHost);
  if (maybeNonEditableListItem &&
      HTMLEditUtils::IsSplittableNode(*maybeNonEditableListItem)) {
    Result<InsertParagraphResult, nsresult> insertParagraphInListItemResult =
        HandleInListItemElement(*maybeNonEditableListItem, pointToInsert);
    if (MOZ_UNLIKELY(insertParagraphInListItemResult.isErr())) {
      if (NS_WARN_IF(insertParagraphInListItemResult.unwrapErr() ==
                     NS_ERROR_EDITOR_DESTROYED)) {
        return Err(NS_ERROR_EDITOR_DESTROYED);
      }
      NS_WARNING(
          "AutoInsertParagraphHandler::HandleInListItemElement() failed, but "
          "ignored");
      return EditActionResult::HandledResult();
    }
    InsertParagraphResult unwrappedInsertParagraphInListItemResult =
        insertParagraphInListItemResult.unwrap();
    MOZ_ASSERT(unwrappedInsertParagraphInListItemResult.Handled());
    MOZ_ASSERT(unwrappedInsertParagraphInListItemResult.GetNewNode());
    const RefPtr<Element> listItemOrParagraphElement =
        unwrappedInsertParagraphInListItemResult.UnwrapNewNode();
    const EditorDOMPoint pointToPutCaret =
        unwrappedInsertParagraphInListItemResult.UnwrapCaretPoint();
    nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
        pointToPutCaret, listItemOrParagraphElement,
        {SuggestCaret::AndIgnoreTrivialError});
    if (NS_FAILED(rv)) {
      NS_WARNING(
          "AutoInsertParagraphHandler::"
          "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() failed");
      return Err(rv);
    }
    NS_WARNING_ASSERTION(rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
                         "CollapseSelection() failed, but ignored");
    return EditActionResult::HandledResult();
  }

  if (HTMLEditUtils::IsHeadingElement(*editableBlockElement)) {
    Result<InsertParagraphResult, nsresult>
        insertParagraphInHeadingElementResult =
            HandleInHeadingElement(*editableBlockElement, pointToInsert);
    if (MOZ_UNLIKELY(insertParagraphInHeadingElementResult.isErr())) {
      NS_WARNING(
          "AutoInsertParagraphHandler::HandleInHeadingElement() failed, but "
          "ignored");
      return EditActionResult::HandledResult();
    }
    InsertParagraphResult unwrappedInsertParagraphInHeadingElementResult =
        insertParagraphInHeadingElementResult.unwrap();
    if (unwrappedInsertParagraphInHeadingElementResult.Handled()) {
      MOZ_ASSERT(unwrappedInsertParagraphInHeadingElementResult.GetNewNode());
      blockElementToPutCaret =
          unwrappedInsertParagraphInHeadingElementResult.UnwrapNewNode();
    }
    const EditorDOMPoint pointToPutCaret =
        unwrappedInsertParagraphInHeadingElementResult.UnwrapCaretPoint();
    nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
        pointToPutCaret, blockElementToPutCaret,
        {SuggestCaret::OnlyIfHasSuggestion,
         SuggestCaret::AndIgnoreTrivialError});
    if (NS_FAILED(rv)) {
      NS_WARNING(
          "AutoInsertParagraphHandler::"
          "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() failed");
      return Err(rv);
    }
    NS_WARNING_ASSERTION(rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
                         "CollapseSelection() failed, but ignored");
    return EditActionResult::HandledResult();
  }

  // XXX Ideally, we should take same behavior with both <p> container and
  //     <div> container.  However, we are still using <br> as default
  //     paragraph separator (non-standard) and we've split only <p> container
  //     long time.  Therefore, some web apps may depend on this behavior like
  //     Gmail.  So, let's use traditional odd behavior only when the default
  //     paragraph separator is <br>.  Otherwise, take consistent behavior
  //     between <p> container and <div> container.
  if ((mDefaultParagraphSeparator == ParagraphSeparator::br &&
       editableBlockElement->IsHTMLElement(nsGkAtoms::p)) ||
      (mDefaultParagraphSeparator != ParagraphSeparator::br &&
       editableBlockElement->IsAnyOfHTMLElements(nsGkAtoms::p,
                                                 nsGkAtoms::div))) {
    const EditorDOMPoint pointToSplit = GetBetterPointToSplitParagraph(
        *editableBlockElement,
        insertedPaddingBRElement ? EditorDOMPoint(insertedPaddingBRElement)
                                 : pointToInsert,
        mEditingHost);
    if (ShouldCreateNewParagraph(*editableBlockElement, pointToSplit)) {
      MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
      // Paragraphs: special rules to look for <br>s
      Result<SplitNodeResult, nsresult> splitNodeResult =
          SplitParagraphWithTransaction(*editableBlockElement, pointToSplit);
      if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
        NS_WARNING("HTMLEditor::SplitParagraphWithTransaction() failed");
        return splitNodeResult.propagateErr();
      }
      if (splitNodeResult.inspect().Handled()) {
        SplitNodeResult unwrappedSplitNodeResult = splitNodeResult.unwrap();
        const RefPtr<Element> rightParagraphElement =
            unwrappedSplitNodeResult.DidSplit()
                ? unwrappedSplitNodeResult.GetNextContentAs<Element>()
                : blockElementToPutCaret.get();
        const EditorDOMPoint pointToPutCaret =
            unwrappedSplitNodeResult.UnwrapCaretPoint();
        nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
            pointToPutCaret, rightParagraphElement,
            {SuggestCaret::AndIgnoreTrivialError});
        if (NS_FAILED(rv)) {
          NS_WARNING(
              "AutoInsertParagraphHandler::"
              "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() "
              "failed");
          return Err(rv);
        }
        NS_WARNING_ASSERTION(
            rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
            "AutoInsertParagraphHandler::"
            "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() "
            "failed, but ignored");
        return EditActionResult::HandledResult();
      }
      MOZ_ASSERT(!splitNodeResult.inspect().HasCaretPointSuggestion());
    }

    // Fall through, if we didn't handle it above.
  }

  // If nobody handles this edit action, let's insert new <br> at the selection.
  Result<EditActionResult, nsresult> insertBRElementResultOrError =
      HandleInsertBRElement(pointToInsert, blockElementToPutCaret);
  NS_WARNING_ASSERTION(
      insertBRElementResultOrError.isOk(),
      "AutoInsertParagraphHandler::HandleInsertBRElement() failed");
  return insertBRElementResultOrError;
}

Result<EditActionResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::HandleInsertBRElement(
    const EditorDOMPoint& aPointToInsert,
    const Element* aBlockElementWhichShouldHaveCaret /* = nullptr */) {
  Result<CreateElementResult, nsresult> insertBRElementResult =
      InsertBRElement(aPointToInsert);
  if (MOZ_UNLIKELY(insertBRElementResult.isErr())) {
    NS_WARNING("AutoInsertParagraphHandler::InsertBRElement() failed");
    return insertBRElementResult.propagateErr();
  }
  const EditorDOMPoint pointToPutCaret =
      insertBRElementResult.unwrap().UnwrapCaretPoint();
  if (MOZ_UNLIKELY(!pointToPutCaret.IsSet())) {
    NS_WARNING(
        "AutoInsertParagraphHandler::InsertBRElement() didn't suggest a "
        "point to put caret");
    return Err(NS_ERROR_FAILURE);
  }
  nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
      pointToPutCaret, aBlockElementWhichShouldHaveCaret, {});
  if (NS_FAILED(rv)) {
    NS_WARNING(
        "AutoInsertParagraphHandler::"
        "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() failed");
    return Err(rv);
  }
  return EditActionResult::HandledResult();
}

Result<EditActionResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::HandleInsertLinefeed(
    const EditorDOMPoint& aPointToInsert) {
  Result<EditorDOMPoint, nsresult> insertLineFeedResult =
      AutoInsertLineBreakHandler::InsertLinefeed(mHTMLEditor, aPointToInsert,
                                                 mEditingHost);
  if (MOZ_UNLIKELY(insertLineFeedResult.isErr())) {
    NS_WARNING("AutoInsertLineBreakHandler::InsertLinefeed() failed");
    return insertLineFeedResult.propagateErr();
  }
  nsresult rv = mHTMLEditor.CollapseSelectionTo(insertLineFeedResult.inspect());
  if (NS_FAILED(rv)) {
    NS_WARNING("EditorBase::CollapseSelectionTo() failed");
    return Err(rv);
  }
  return EditActionResult::HandledResult();
}

bool HTMLEditor::AutoInsertParagraphHandler::ShouldInsertLineBreakInstead(
    const Element* aEditableBlockElement,
    const EditorDOMPoint& aCandidatePointToSplit) {
  // If there is no block parent in the editing host, i.e., the editing
  // host itself is also a non-block element, we should insert a line
  // break.
  if (!aEditableBlockElement) {
    // XXX Chromium checks if the CSS box of the editing host is a block.
    return true;
  }

  // If the editable block element is not splittable, e.g., it's an
  // editing host, and the default paragraph separator is <br> or the
  // element cannot contain a <p> element, we should insert a <br>
  // element.
  if (!HTMLEditUtils::IsSplittableNode(*aEditableBlockElement)) {
    return mDefaultParagraphSeparator == ParagraphSeparator::br ||
           !HTMLEditUtils::CanElementContainParagraph(*aEditableBlockElement) ||
           (aCandidatePointToSplit.IsInContentNode() &&
            mHTMLEditor
                    .GetPreferredLineBreakType(
                        *aCandidatePointToSplit.ContainerAs<nsIContent>(),
                        mEditingHost)
                    .valueOr(LineBreakType::BRElement) ==
                LineBreakType::Linefeed &&
            HTMLEditUtils::IsDisplayOutsideInline(mEditingHost));
  }

  // If the nearest block parent is a single-line container declared in
  // the execCommand spec and not the editing host, we should separate the
  // block even if the default paragraph separator is <br> element.
  if (HTMLEditUtils::IsSingleLineContainer(*aEditableBlockElement)) {
    return false;
  }

  // Otherwise, unless there is no block ancestor which can contain <p>
  // element, we shouldn't insert a line break here.
  for (const Element* editableBlockAncestor = aEditableBlockElement;
       editableBlockAncestor;
       editableBlockAncestor = HTMLEditUtils::GetAncestorElement(
           *editableBlockAncestor,
           HTMLEditUtils::ClosestEditableBlockElementOrButtonElement,
           BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
    if (HTMLEditUtils::CanElementContainParagraph(*editableBlockAncestor)) {
      return false;
    }
  }
  return true;
}

// static
nsresult HTMLEditor::AutoInsertParagraphHandler::
    CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
        const EditorDOMPoint& aCandidatePointToPutCaret,
        const Element* aBlockElementShouldHaveCaret,
        const SuggestCaretOptions& aOptions) {
  if (!aCandidatePointToPutCaret.IsSet()) {
    if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion)) {
      return NS_OK;
    }
    return aOptions.contains(SuggestCaret::AndIgnoreTrivialError)
               ? NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR
               : NS_ERROR_FAILURE;
  }
  EditorDOMPoint pointToPutCaret(aCandidatePointToPutCaret);
  if (aBlockElementShouldHaveCaret) {
    Result<EditorDOMPoint, nsresult> pointToPutCaretOrError =
        HTMLEditUtils::ComputePointToPutCaretInElementIfOutside<EditorDOMPoint>(
            *aBlockElementShouldHaveCaret, aCandidatePointToPutCaret);
    if (MOZ_UNLIKELY(pointToPutCaretOrError.isErr())) {
      NS_WARNING(
          "HTMLEditUtils::ComputePointToPutCaretInElementIfOutside() "
          "failed, but ignored");
    } else if (pointToPutCaretOrError.inspect().IsSet()) {
      pointToPutCaret = pointToPutCaretOrError.unwrap();
    }
  }
  nsresult rv = mHTMLEditor.CollapseSelectionTo(pointToPutCaret);
  if (NS_FAILED(rv) && MOZ_LIKELY(rv != NS_ERROR_EDITOR_DESTROYED) &&
      aOptions.contains(SuggestCaret::AndIgnoreTrivialError)) {
    rv = NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR;
  }
  return rv;
}

Result<CreateElementResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::InsertBRElement(
    const EditorDOMPoint& aPointToBreak) {
  MOZ_ASSERT(aPointToBreak.IsInContentNode());

  const bool editingHostIsEmpty = HTMLEditUtils::IsEmptyNode(
      mEditingHost, {EmptyCheckOption::TreatNonEditableContentAsInvisible});
  const WSRunScanner wsRunScanner({WSRunScanner::Option::OnlyEditableNodes},
                                  aPointToBreak);
  const WSScanResult backwardScanResult =
      wsRunScanner.ScanPreviousVisibleNodeOrBlockBoundaryFrom(aPointToBreak);
  if (MOZ_UNLIKELY(backwardScanResult.Failed())) {
    NS_WARNING(
        "WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom() failed");
    return Err(NS_ERROR_FAILURE);
  }
  const bool brElementIsAfterBlock =
      backwardScanResult.ReachedBlockBoundary() ||
      // FIXME: This is wrong considering because the inline editing host may
      // be surrounded by visible inline content.  However, WSRunScanner is
      // not aware of block boundary around it and stopping this change causes
      // starting to fail some WPT.  Therefore, we need to keep doing this for
      // now.
      backwardScanResult.ReachedInlineEditingHostBoundary();
  const WSScanResult forwardScanResult =
      wsRunScanner.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
          aPointToBreak);
  if (MOZ_UNLIKELY(forwardScanResult.Failed())) {
    NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom() failed");
    return Err(NS_ERROR_FAILURE);
  }
  const bool brElementIsBeforeBlock =
      forwardScanResult.ReachedBlockBoundary() ||
      // FIXME: See above comment
      forwardScanResult.ReachedInlineEditingHostBoundary();

  // First, insert a <br> element.
  RefPtr<Element> brElement;
  if (mHTMLEditor.IsPlaintextMailComposer()) {
    Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
        mHTMLEditor.InsertLineBreak(WithTransaction::Yes,
                                    LineBreakType::BRElement, aPointToBreak);
    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
      NS_WARNING(
          "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
          "LineBreakType::BRElement) failed");
      return insertBRElementResultOrError.propagateErr();
    }
    CreateLineBreakResult insertBRElementResult =
        insertBRElementResultOrError.unwrap();
    // We'll return with suggesting new caret position and nobody refers
    // selection after here.  So we don't need to update selection here.
    insertBRElementResult.IgnoreCaretPointSuggestion();
    brElement = &insertBRElementResult->BRElementRef();
  } else {
    EditorDOMPoint pointToBreak(aPointToBreak);
    // If the container of the break is a link, we need to split it and
    // insert new <br> between the split links.
    RefPtr<Element> linkNode =
        HTMLEditor::GetLinkElement(pointToBreak.GetContainer());
    if (linkNode) {
      // FIXME: Normalize surrounding white-spaces before splitting the
      // insertion point here.
      Result<SplitNodeResult, nsresult> splitLinkNodeResult =
          mHTMLEditor.SplitNodeDeepWithTransaction(
              *linkNode, pointToBreak,
              SplitAtEdges::eDoNotCreateEmptyContainer);
      if (MOZ_UNLIKELY(splitLinkNodeResult.isErr())) {
        NS_WARNING(
            "HTMLEditor::SplitNodeDeepWithTransaction(SplitAtEdges::"
            "eDoNotCreateEmptyContainer) failed");
        return splitLinkNodeResult.propagateErr();
      }
      // TODO: Some methods called by
      //       WhiteSpaceVisibilityKeeper::InsertLineBreak() use
      //       ComputeEditingHost() which depends on selection.  Therefore,
      //       we cannot skip updating selection here.
      nsresult rv = splitLinkNodeResult.inspect().SuggestCaretPointTo(
          mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
                        SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
      if (NS_FAILED(rv)) {
        NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
        return Err(rv);
      }
      pointToBreak =
          splitLinkNodeResult.inspect().AtSplitPoint<EditorDOMPoint>();
    }
    Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
        WhiteSpaceVisibilityKeeper::InsertLineBreak(LineBreakType::BRElement,
                                                    mHTMLEditor, pointToBreak);
    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
      NS_WARNING(
          "WhiteSpaceVisibilityKeeper::InsertLineBreak(LineBreakType::"
          "BRElement) failed");
      return insertBRElementResultOrError.propagateErr();
    }
    CreateLineBreakResult insertBRElementResult =
        insertBRElementResultOrError.unwrap();
    // We'll return with suggesting new caret position and nobody refers
    // selection after here.  So we don't need to update selection here.
    insertBRElementResult.IgnoreCaretPointSuggestion();
    brElement = &insertBRElementResult->BRElementRef();
  }

  if (MOZ_UNLIKELY(!brElement->GetParentNode())) {
    NS_WARNING("Inserted <br> element was removed by the web app");
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  auto afterBRElement = EditorDOMPoint::After(brElement);

  const auto InsertAdditionalInvisibleLineBreak =
      [this, &afterBRElement]()
          MOZ_CAN_RUN_SCRIPT -> Result<CreateLineBreakResult, nsresult> {
    // Empty last line is invisible if it's immediately before either parent or
    // another block's boundary so that we need to put invisible <br> element
    // here for making it visible.
    Result<CreateLineBreakResult, nsresult>
        insertPaddingBRElementResultOrError =
            WhiteSpaceVisibilityKeeper::InsertLineBreak(
                LineBreakType::BRElement, mHTMLEditor, afterBRElement);
    NS_WARNING_ASSERTION(insertPaddingBRElementResultOrError.isOk(),
                         "WhiteSpaceVisibilityKeeper::InsertLineBreak("
                         "LineBreakType::BRElement) failed");
    // afterBRElement points after the first <br> with referring an old child.
    // Therefore, we need to update it with new child which is the new invisible
    // <br>.
    afterBRElement = insertPaddingBRElementResultOrError.inspect()
                         .AtLineBreak<EditorDOMPoint>();
    return insertPaddingBRElementResultOrError;
  };

  if (brElementIsAfterBlock && brElementIsBeforeBlock) {
    // We just placed a <br> between block boundaries.  This is the one case
    // where we want the selection to be before the br we just placed, as the
    // br will be on a new line, rather than at end of prior line.
    // XXX brElementIsAfterBlock and brElementIsBeforeBlock were set before
    //     modifying the DOM tree.  So, now, the <br> element may not be
    //     between blocks.
    EditorDOMPoint pointToPutCaret;
    if (editingHostIsEmpty) {
      Result<CreateLineBreakResult, nsresult>
          insertPaddingBRElementResultOrError =
              InsertAdditionalInvisibleLineBreak();
      if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
        return insertPaddingBRElementResultOrError.propagateErr();
      }
      insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
      pointToPutCaret = std::move(afterBRElement);
    } else {
      pointToPutCaret =
          EditorDOMPoint(brElement, InterlinePosition::StartOfNextLine);
    }
    return CreateElementResult(std::move(brElement),
                               std::move(pointToPutCaret));
  }

  const WSScanResult forwardScanFromAfterBRElementResult =
      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
          {WSRunScanner::Option::OnlyEditableNodes}, afterBRElement);
  if (MOZ_UNLIKELY(forwardScanFromAfterBRElementResult.Failed())) {
    NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundary() failed");
    return Err(NS_ERROR_FAILURE);
  }
  if (forwardScanFromAfterBRElementResult.ReachedBRElement()) {
    // The next thing after the break we inserted is another break.  Move the
    // second break to be the first break's sibling.  This will prevent them
    // from being in different inline nodes, which would break
    // SetInterlinePosition().  It will also assure that if the user clicks
    // away and then clicks back on their new blank line, they will still get
    // the style from the line above.
    if (brElement->GetNextSibling() !=
        forwardScanFromAfterBRElementResult.BRElementPtr()) {
      MOZ_ASSERT(forwardScanFromAfterBRElementResult.BRElementPtr());
      Result<MoveNodeResult, nsresult> moveBRElementResult =
          mHTMLEditor.MoveNodeWithTransaction(
              MOZ_KnownLive(
                  *forwardScanFromAfterBRElementResult.BRElementPtr()),
              afterBRElement);
      if (MOZ_UNLIKELY(moveBRElementResult.isErr())) {
        NS_WARNING("HTMLEditor::MoveNodeWithTransaction() failed");
        return moveBRElementResult.propagateErr();
      }
      nsresult rv = moveBRElementResult.inspect().SuggestCaretPointTo(
          mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
                        SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
                        SuggestCaret::AndIgnoreTrivialError});
      if (NS_FAILED(rv)) {
        NS_WARNING("MoveNodeResult::SuggestCaretPointTo() failed");
        return Err(rv);
      }
      NS_WARNING_ASSERTION(
          rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
          "MoveNodeResult::SuggestCaretPointTo() failed, but ignored");
      // afterBRElement points after the first <br> with referring an old child.
      // Therefore, we need to update it with new child which is the new
      // invisible <br>.
      afterBRElement.Set(forwardScanFromAfterBRElementResult.BRElementPtr());
    }
  } else if ((forwardScanFromAfterBRElementResult.ReachedBlockBoundary() ||
              // FIXME: This is wrong considering because the inline editing
              // host may be surrounded by visible inline content.  However,
              // WSRunScanner is not aware of block boundary around it and
              // stopping this change causes starting to fail some WPT.
              // Therefore, we need to keep doing this for now.
              forwardScanFromAfterBRElementResult
                  .ReachedInlineEditingHostBoundary()) &&
             !brElementIsAfterBlock) {
    Result<CreateLineBreakResult, nsresult>
        insertPaddingBRElementResultOrError =
            InsertAdditionalInvisibleLineBreak();
    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
      return insertPaddingBRElementResultOrError.propagateErr();
    }
    insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
  }

  // We want the caret to stick to whatever is past the break.  This is because
  // the break is on the same line we were on, but the next content will be on
  // the following line.

  // An exception to this is if the break has a next sibling that is a block
  // node.  Then we stick to the left to avoid an uber caret.
  nsIContent* nextSiblingOfBRElement = brElement->GetNextSibling();
  afterBRElement.SetInterlinePosition(
      nextSiblingOfBRElement && HTMLEditUtils::IsBlockElement(
                                    *nextSiblingOfBRElement,
                                    BlockInlineCheck::UseComputedDisplayStyle)
          ? InterlinePosition::EndOfLine
          : InterlinePosition::StartOfNextLine);
  return CreateElementResult(std::move(brElement), afterBRElement);
}

Result<CaretPoint, nsresult>
HTMLEditor::AutoInsertParagraphHandler::HandleInMailCiteElement(
    Element& aMailCiteElement, const EditorDOMPoint& aPointToSplit) {
  MOZ_ASSERT(aPointToSplit.IsSet());
  NS_ASSERTION(!HTMLEditUtils::IsEmptyNode(
                   aMailCiteElement,
                   {EmptyCheckOption::TreatNonEditableContentAsInvisible}),
               "The mail-cite element will be deleted, does it expected result "
               "for you?");

  auto splitCiteElementResult =
      SplitMailCiteElement(aPointToSplit, aMailCiteElement);
  if (MOZ_UNLIKELY(splitCiteElementResult.isErr())) {
    NS_WARNING("Failed to split a mail-cite element");
    return splitCiteElementResult.propagateErr();
  }
  SplitNodeResult unwrappedSplitCiteElementResult =
      splitCiteElementResult.unwrap();
  // When adding caret suggestion to SplitNodeResult, here didn't change
  // selection so that just ignore it.
  unwrappedSplitCiteElementResult.IgnoreCaretPointSuggestion();

  // Add an invisible <br> to the end of left cite node if it was a <span> of
  // style="display: block".  This is important, since when serializing the cite
  // to plain text, the span which caused the visual break is discarded.  So the
  // added <br> will guarantee that the serializer will insert a break where the
  // user saw one.
  // FYI: unwrappedSplitCiteElementResult grabs the previous node and the next
  //      node with nsCOMPtr or EditorDOMPoint.  So, it's safe to access
  //      leftCiteElement and rightCiteElement even after changing the DOM tree
  //      and/or selection even though it's raw pointer.
  autoconst leftCiteElement =
      unwrappedSplitCiteElementResult.GetPreviousContentAs<Element>();
  autoconst rightCiteElement =
      unwrappedSplitCiteElementResult.GetNextContentAs<Element>();
  if (leftCiteElement && leftCiteElement->IsHTMLElement(nsGkAtoms::span) &&
      // XXX Oh, this depends on layout information of new element, and it's
      //     created by the hacky flush in DoSplitNode().  So we need to
      //     redesign around this for bug 1710784.
      leftCiteElement->GetPrimaryFrame() &&
      leftCiteElement->GetPrimaryFrame()->IsBlockFrameOrSubclass()) {
    nsIContent* lastChild = leftCiteElement->GetLastChild();
    if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
      Result<CreateLineBreakResult, nsresult>
          insertPaddingBRElementResultOrError = mHTMLEditor.InsertLineBreak(
              WithTransaction::Yes, LineBreakType::BRElement,
              EditorDOMPoint::AtEndOf(*leftCiteElement));
      if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
        NS_WARNING(
            "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
            "LineBreakType::BRElement) failed");
        return insertPaddingBRElementResultOrError.propagateErr();
      }
      CreateLineBreakResult insertPaddingBRElementResult =
          insertPaddingBRElementResultOrError.unwrap();
      MOZ_ASSERT(insertPaddingBRElementResult.Handled());
      // We don't need to update selection here because we'll do another
      // InsertLineBreak call soon.
      insertPaddingBRElementResult.IgnoreCaretPointSuggestion();
    }
  }

  // In most cases, <br> should be inserted after current cite.  However, if
  // left cite hasn't been created because the split point was start of the
  // cite node, <br> should be inserted before the current cite.
  Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
      mHTMLEditor.InsertLineBreak(
          WithTransaction::Yes, LineBreakType::BRElement,
          unwrappedSplitCiteElementResult.AtSplitPoint<EditorDOMPoint>());
  if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
    NS_WARNING(
        "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
        "LineBreakType::BRElement) failed");
    return Err(insertBRElementResultOrError.unwrapErr());
  }
  CreateLineBreakResult insertBRElementResult =
      insertBRElementResultOrError.unwrap();
  MOZ_ASSERT(insertBRElementResult.Handled());
  // We'll return with suggesting caret position.  Therefore, we don't need
  // to update selection here.
  insertBRElementResult.IgnoreCaretPointSuggestion();
  // if aMailCiteElement wasn't a block, we might also want another break before
  // it. We need to examine the content both before the br we just added and
  // also just after it.  If we don't have another br or block boundary
  // adjacent, then we will need a 2nd br added to achieve blank line that user
  // expects.
  {
    nsresult rvOfInsertPaddingBRElement =
        MaybeInsertPaddingBRElementToInlineMailCiteElement(
            insertBRElementResult.AtLineBreak<EditorDOMPoint>(),
            aMailCiteElement);
    if (NS_FAILED(rvOfInsertPaddingBRElement)) {
      NS_WARNING(
          "Failed to insert additional <br> element before the inline right "
          "mail-cite element");
      return Err(rvOfInsertPaddingBRElement);
    }
  }

  if (leftCiteElement &&
      HTMLEditUtils::IsEmptyNode(
          *leftCiteElement,
          {EmptyCheckOption::TreatNonEditableContentAsInvisible})) {
    // MOZ_KnownLive(leftCiteElement) because it's grabbed by
    // unwrappedSplitCiteElementResult.
    nsresult rv =
        mHTMLEditor.DeleteNodeWithTransaction(MOZ_KnownLive(*leftCiteElement));
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
      return Err(rv);
    }
  }

  if (rightCiteElement &&
      HTMLEditUtils::IsEmptyNode(
          *rightCiteElement,
          {EmptyCheckOption::TreatNonEditableContentAsInvisible})) {
    // MOZ_KnownLive(rightCiteElement) because it's grabbed by
    // unwrappedSplitCiteElementResult.
    nsresult rv =
        mHTMLEditor.DeleteNodeWithTransaction(MOZ_KnownLive(*rightCiteElement));
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
      return Err(rv);
    }
  }

  if (NS_WARN_IF(!insertBRElementResult.LineBreakIsInComposedDoc())) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  auto pointToPutCaret = insertBRElementResult.AtLineBreak<EditorDOMPoint>();
  pointToPutCaret.SetInterlinePosition(InterlinePosition::StartOfNextLine);
  return CaretPoint(std::move(pointToPutCaret));
}

Result<SplitNodeResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::SplitMailCiteElement(
    const EditorDOMPoint& aPointToSplit, Element& aMailCiteElement) {
  EditorDOMPoint pointToSplit(aPointToSplit);

  // If our selection is just before a break, nudge it to be just after
  // it. This does two things for us.  It saves us the trouble of having
  // to add a break here ourselves to preserve the "blockness" of the
  // inline span mailquote (in the inline case), and : it means the break
  // won't end up making an empty line that happens to be inside a
  // mailquote (in either inline or block case). The latter can confuse a
  // user if they click there and start typing, because being in the
  // mailquote may affect wrapping behavior, or font color, etc.
  const WSScanResult forwardScanFromPointToSplitResult =
      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
          {WSRunScanner::Option::OnlyEditableNodes,
           WSRunScanner::Option::ReferHTMLDefaultStyle},
          pointToSplit);
  if (forwardScanFromPointToSplitResult.Failed()) {
    return Err(NS_ERROR_FAILURE);
  }
  // If selection start point is before a break and it's inside the
  // mailquote, let's split it after the visible node.
  if (forwardScanFromPointToSplitResult.ReachedBRElement() &&
      forwardScanFromPointToSplitResult.BRElementPtr() != &aMailCiteElement &&
      aMailCiteElement.Contains(
          forwardScanFromPointToSplitResult.BRElementPtr())) {
    pointToSplit = forwardScanFromPointToSplitResult
                       .PointAfterReachedContent<EditorDOMPoint>();
  }

  if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
    return Err(NS_ERROR_FAILURE);
  }

  Result<EditorDOMPoint, nsresult> pointToSplitOrError =
      WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
          mHTMLEditor, pointToSplit,
          {WhiteSpaceVisibilityKeeper::NormalizeOption::
               StopIfPrecedingWhiteSpacesEndsWithNBP,
           WhiteSpaceVisibilityKeeper::NormalizeOption::
               StopIfFollowingWhiteSpacesStartsWithNBSP});
  if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) {
    NS_WARNING(
        "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() "
        "failed");
    return pointToSplitOrError.propagateErr();
  }
  pointToSplit = pointToSplitOrError.unwrap();
  if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }

  Result<SplitNodeResult, nsresult> splitResult =
      mHTMLEditor.SplitNodeDeepWithTransaction(
          aMailCiteElement, pointToSplit,
          SplitAtEdges::eDoNotCreateEmptyContainer);
  if (MOZ_UNLIKELY(splitResult.isErr())) {
    NS_WARNING(
        "HTMLEditor::SplitNodeDeepWithTransaction(aMailCiteElement, "
        "SplitAtEdges::eDoNotCreateEmptyContainer) failed");
    return splitResult;
  }
  // FIXME: We should make the caller handle `Selection`.
  nsresult rv = splitResult.inspect().SuggestCaretPointTo(
      mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
                    SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
  if (NS_FAILED(rv)) {
    NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
    return Err(rv);
  }
  return splitResult;
}

nsresult HTMLEditor::AutoInsertParagraphHandler::
    MaybeInsertPaddingBRElementToInlineMailCiteElement(
        const EditorDOMPoint& aPointToInsertBRElement,
        Element& aMailCiteElement) {
  if (!HTMLEditUtils::IsInlineContent(aMailCiteElement,
                                      BlockInlineCheck::UseHTMLDefaultStyle)) {
    return NS_SUCCESS_DOM_NO_OPERATION;
  }
  // XXX Cannot we replace this complicated check with just a call of
  //     HTMLEditUtils::IsVisibleBRElement with
  //     resultOfInsertingBRElement.inspect()?
  const WSScanResult backwardScanFromPointToCreateNewBRElementResult =
      WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
          {WSRunScanner::Option::OnlyEditableNodes,
           WSRunScanner::Option::ReferHTMLDefaultStyle},
          aPointToInsertBRElement);
  if (MOZ_UNLIKELY(backwardScanFromPointToCreateNewBRElementResult.Failed())) {
    NS_WARNING(
        "WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary() "
        "failed");
    return NS_ERROR_FAILURE;
  }
  if (!backwardScanFromPointToCreateNewBRElementResult
           .InVisibleOrCollapsibleCharacters() &&
      !backwardScanFromPointToCreateNewBRElementResult
           .ReachedSpecialContent() &&
      !backwardScanFromPointToCreateNewBRElementResult
           .ReachedEmptyInlineContainerElement()) {
    return NS_SUCCESS_DOM_NO_OPERATION;
  }
  const WSScanResult forwardScanFromPointAfterNewBRElementResult =
      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
          {WSRunScanner::Option::OnlyEditableNodes,
           WSRunScanner::Option::ReferHTMLDefaultStyle},
          EditorRawDOMPoint::After(aPointToInsertBRElement));
  if (MOZ_UNLIKELY(forwardScanFromPointAfterNewBRElementResult.Failed())) {
    NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundary() failed");
    return NS_ERROR_FAILURE;
  }
  if (!forwardScanFromPointAfterNewBRElementResult
           .InVisibleOrCollapsibleCharacters() &&
      !forwardScanFromPointAfterNewBRElementResult.ReachedSpecialContent() &&
      !forwardScanFromPointAfterNewBRElementResult
           .ReachedEmptyInlineContainerElement() &&
      // In case we're at the very end.
      !forwardScanFromPointAfterNewBRElementResult
           .ReachedCurrentBlockBoundary()) {
    return NS_SUCCESS_DOM_NO_OPERATION;
  }
  Result<CreateLineBreakResult, nsresult> insertAnotherBRElementResultOrError =
      mHTMLEditor.InsertLineBreak(WithTransaction::Yes,
                                  LineBreakType::BRElement,
                                  aPointToInsertBRElement);
  if (MOZ_UNLIKELY(insertAnotherBRElementResultOrError.isErr())) {
    NS_WARNING(
        "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
        "LineBreakType::BRElement) failed");
    return insertAnotherBRElementResultOrError.unwrapErr();
  }
  CreateLineBreakResult insertAnotherBRElementResult =
      insertAnotherBRElementResultOrError.unwrap();
  MOZ_ASSERT(insertAnotherBRElementResult.Handled());
  insertAnotherBRElementResult.IgnoreCaretPointSuggestion();
  return NS_OK;
}

Result<InsertParagraphResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::HandleInHeadingElement(
    Element& aHeadingElement, const EditorDOMPoint& aPointToSplit) {
  // Don't preserve empty link at the end of the left heading element nor the
  // start of the right one.
  const EditorDOMPoint pointToSplit = GetBetterPointToSplitParagraph(
      aHeadingElement, aPointToSplit, mEditingHost);
  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());

  // If the split point is end of the heading element, we should not touch the
  // heading element and insert a default paragraph next to the heading element.
  if (SplitPointIsEndOfSplittingBlock(aHeadingElement, pointToSplit,
                                      IgnoreBlockBoundaries::Yes)) {
    Result<InsertParagraphResult, nsresult>
        handleAtEndOfHeadingElementResultOrError =
            HandleAtEndOfHeadingElement(aHeadingElement);
    NS_WARNING_ASSERTION(
        handleAtEndOfHeadingElementResultOrError.isOk(),
        "AutoInsertParagraphHandler::HandleAtEndOfHeadingElement() failed");
    return handleAtEndOfHeadingElementResultOrError;
  }

  Result<SplitNodeResult, nsresult> splitHeadingResultOrError =
      SplitParagraphWithTransaction(aHeadingElement, pointToSplit);
  if (MOZ_UNLIKELY(splitHeadingResultOrError.isErr())) {
    NS_WARNING(
        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() failed");
    return splitHeadingResultOrError.propagateErr();
  }
  SplitNodeResult splitHeadingResult = splitHeadingResultOrError.unwrap();
  splitHeadingResult.IgnoreCaretPointSuggestion();
  if (MOZ_UNLIKELY(!splitHeadingResult.DidSplit())) {
    NS_WARNING(
        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() didn't "
        "split aHeadingElement");
    return Err(NS_ERROR_FAILURE);
  }

  // Put caret at start of the right head element if it's not empty.
  autoconst rightHeadingElement =
      splitHeadingResult.GetNextContentAs<Element>();
  MOZ_ASSERT(rightHeadingElement,
             "SplitNodeResult::GetNextContent() should return something if "
             "DidSplit() returns true");
  return InsertParagraphResult(*rightHeadingElement,
                               splitHeadingResult.UnwrapCaretPoint());
}

Result<InsertParagraphResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::HandleAtEndOfHeadingElement(
    Element& aHeadingElement) {
  // XXX This makes HTMLEditor instance stateful.  So, we should move this out
  // from AutoInsertParagraphHandler with adding a method which HTMLEditor can
  // consider to do this.
  mHTMLEditor.TopLevelEditSubActionDataRef().mCachedPendingStyles->Clear();
  mHTMLEditor.mPendingStylesToApplyToNewContent->ClearAllStyles();

  // Create a paragraph if the right heading element is not followed by an
  // editable <br> element.
  nsStaticAtom& newParagraphTagName =
      &mDefaultParagraphSeparatorTagName == nsGkAtoms::br
          ? *nsGkAtoms::p
          : mDefaultParagraphSeparatorTagName;
  // We want a wrapper element even if we separate with a <br>.
  // FIXME: Chrome does not preserve the style coming from the heading element.
  // However, Chrome preserves the inline ancestors at the split point.
  // Perhaps, we should follow them.
  // MOZ_KnownLive(newParagraphTagName) because it's available until shutdown.
  Result<CreateElementResult, nsresult> createNewParagraphElementResult =
      mHTMLEditor.CreateAndInsertElement(WithTransaction::Yes,
                                         MOZ_KnownLive(newParagraphTagName),
                                         EditorDOMPoint::After(aHeadingElement),
                                         HTMLEditor::InsertNewBRElement);
  if (MOZ_UNLIKELY(createNewParagraphElementResult.isErr())) {
    NS_WARNING(
        "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
    return createNewParagraphElementResult.propagateErr();
  }
  CreateElementResult unwrappedCreateNewParagraphElementResult =
      createNewParagraphElementResult.unwrap();
  // Put caret at the <br> element in the following paragraph.
  unwrappedCreateNewParagraphElementResult.IgnoreCaretPointSuggestion();
  MOZ_ASSERT(unwrappedCreateNewParagraphElementResult.GetNewNode());
  EditorDOMPoint pointToPutCaret(
      unwrappedCreateNewParagraphElementResult.GetNewNode(), 0u);
  return InsertParagraphResult(
      unwrappedCreateNewParagraphElementResult.UnwrapNewNode(),
      std::move(pointToPutCaret));
}

// static
bool HTMLEditor::AutoInsertParagraphHandler::
    IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
        const dom::HTMLBRElement* aBRElement) {
  return !aBRElement ||
         HTMLEditUtils::IsBRElementFollowedByBlockBoundary(*aBRElement) ||
         // XXX It's odd to check the <br> element's flag. It may be wrong if
         // the web app moved the <br> or changed the preceding conent.
         EditorUtils::IsPaddingBRElementForEmptyLastLine(*aBRElement);
}

bool HTMLEditor::AutoInsertParagraphHandler::ShouldCreateNewParagraph(
    Element& aParentDivOrP, const EditorDOMPoint& aPointToSplit) const {
  MOZ_ASSERT(aPointToSplit.IsInContentNodeAndValidInComposedDoc());

  if (MOZ_LIKELY(mHTMLEditor.GetReturnInParagraphCreatesNewParagraph())) {
    // We should always create a new paragraph by default.
    return true;
  }
  if (aPointToSplit.GetContainer() == &aParentDivOrP) {
    // We are trying to split only the current paragraph, let's do it.
    return true;
  }
  if (aPointToSplit.IsInTextNode()) {
    if (aPointToSplit.IsStartOfContainer()) {
      // If we're splitting the paragraph at start of a `Text` and it does
      // not follow a <br> or follows an invisible <br>, we should not create a
      // new paragraph.
      // XXX It seems that here assumes that the paragraph has only this `Text`.
      const autoconst precedingBRElement =
          HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousSibling(
              *aPointToSplit.ContainerAs<Text>(),
              {LeafNodeOption::IgnoreNonEditableNode},
              BlockInlineCheck::UseComputedDisplayOutsideStyle));
      return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
          precedingBRElement);
    }
    if (aPointToSplit.IsEndOfContainer()) {
      // If we're splitting the paragraph at end of a `Text` and it's not
      // followed by a <br> or is followed by an invisible <br>, we should not
      // create a new paragraph.
      // XXX It seems that here assumes that the paragraph has only this `Text`.
      const autoconst followingBRElement =
          HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextSibling(
              *aPointToSplit.ContainerAs<Text>(),
              {LeafNodeOption::IgnoreNonEditableNode},
              BlockInlineCheck::UseComputedDisplayOutsideStyle));
      return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
          followingBRElement);
    }
    // If we're splitting the paragraph at middle of a `Text`, we should create
    // a new paragraph.
    return true;
  }

  // If we're splitting in a child element of the paragraph and it does not
  // follow a <br> or follows an invisible <br>, maybe we should not create a
  // new paragraph.
  // XXX Why? We probably need to do this if we're splitting in an inline
  //     element which and whose parents provide some styles, we should put
  //     the <br> element for making a placeholder in the left paragraph for
  //     moving to the caret, but I think that this could be handled in fewer
  //     cases than this.
  const autoconst precedingBRElement =
      HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousLeafContent(
          aPointToSplit, {LeafNodeOption::IgnoreNonEditableNode},
          BlockInlineCheck::Auto, &mEditingHost));
  if (!IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
          precedingBRElement)) {
    return true;
  }
  // If we're splitting in a child element of the paragraph and it's not
  // followed by a <br> or followed by an invisible <br>, we should not create a
  // new paragraph.
  const auto* followingBRElement =
      HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextLeafContent(
          aPointToSplit, {LeafNodeOption::IgnoreNonEditableNode},
          BlockInlineCheck::Auto, &mEditingHost));
  return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
      followingBRElement);
}

// static
EditorDOMPoint
HTMLEditor::AutoInsertParagraphHandler::GetBetterPointToSplitParagraph(
    const Element& aBlockElementToSplit,
    const EditorDOMPoint& aCandidatePointToSplit, const Element& aEditingHost) {
  EditorDOMPoint pointToSplit = [&]() MOZ_NEVER_INLINE_DEBUG {
    // We shouldn't create new anchor element which has non-empty href unless
    // splitting middle of it because we assume that users don't want to create
    // *same* anchor element across two or more paragraphs in most cases.
    // So, adjust selection start if it's edge of anchor element(s).
    {
      const WSScanResult prevVisibleThing =
          WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
              {}, aCandidatePointToSplit, &aBlockElementToSplit);
      if (prevVisibleThing.GetContent() &&
          // Only if the previous thing is not in the same container.
          prevVisibleThing.GetContent() !=
              aCandidatePointToSplit.GetContainer() &&
          // Only if the previous thing is a preceding node of closest inclusive
          // ancestor element at the split point.
          !prevVisibleThing.GetContent()->IsInclusiveDescendantOf(
              aCandidatePointToSplit.GetContainerOrContainerParentElement())) {
        EditorRawDOMPoint candidatePointToSplit =
            aCandidatePointToSplit.To<EditorRawDOMPoint>();
        const Element* const commonAncestor =
            Element::FromNode(nsContentUtils::GetClosestCommonInclusiveAncestor(
                candidatePointToSplit.GetContainerOrContainerParentElement(),
                prevVisibleThing.GetContent()));
        MOZ_ASSERT(commonAncestor);
        for (const Element* container =
                 candidatePointToSplit.GetContainerOrContainerParentElement();
             container && container != commonAncestor;
             container = container->GetParentElement()) {
          if (!HTMLEditUtils::IsHyperlinkElement(*container)) {
            continue;
          }
          // Found link should be only in right node.  So, we shouldn't split
          // it.
          candidatePointToSplit.Set(container);
          // Even if we found an anchor element, don't break because DOM API
          // allows to nest anchor elements.
        }
        return candidatePointToSplit.To<EditorDOMPoint>();
      }
    }
    const WSScanResult nextVisibleThing =
        HTMLEditUtils::ScanInclusiveNextThingWithIgnoringUnnecessaryLineBreak(
            aCandidatePointToSplit, PaddingForEmptyBlock::Unnecessary,
            aEditingHost, &aBlockElementToSplit);
    if (nextVisibleThing.GetContent() &&
        !nextVisibleThing.ReachedOutsideEditingHost() &&
        // Only if the next thing is not in the same container.
        nextVisibleThing.GetContent() !=
            aCandidatePointToSplit.GetContainer() &&
        // Only if the next thing is a preceding node of closest inclusive
        // ancestor element at the split point.
        !nextVisibleThing.GetContent()->IsInclusiveDescendantOf(
            aCandidatePointToSplit.GetContainerOrContainerParentElement())) {
      EditorRawDOMPoint candidatePointToSplit =
          aCandidatePointToSplit.To<EditorRawDOMPoint>();
      const Element* const commonAncestor =
          Element::FromNode(nsContentUtils::GetClosestCommonInclusiveAncestor(
              candidatePointToSplit.GetContainerOrContainerParentElement(),
              nextVisibleThing.GetContent()));
      MOZ_ASSERT(commonAncestor);
      for (const Element* container =
               candidatePointToSplit.GetContainerOrContainerParentElement();
           container && container != commonAncestor;
           container = container->GetParentElement()) {
        if (!HTMLEditUtils::IsHyperlinkElement(*container)) {
          continue;
        }
        // Found link should be only in left node.  So, we shouldn't split it.
        candidatePointToSplit.SetAfter(container);
        // Even if we found an anchor element, don't break because DOM API
        // allows to nest anchor elements.
      }
      return candidatePointToSplit.To<EditorDOMPoint>();
    }

    // Okay, split the ancestors as-is.
    return aCandidatePointToSplit;
  }();

  // If the candidate split point is not in a splittable node, let's move the
  // point after the parent.
  for (const nsIContent* container = pointToSplit.ContainerAs<nsIContent>();
       container && container != &aBlockElementToSplit &&
       !HTMLEditUtils::IsSplittableNode(*container);
       container = container->GetParent()) {
    pointToSplit = pointToSplit.ParentPoint();
  }
  return pointToSplit;
}

Result<EditorDOMPoint, nsresult> HTMLEditor::AutoInsertParagraphHandler::
    EnsureNoInvisibleLineBreakBeforePointToSplit(
        const Element& aBlockElementToSplit,
        const EditorDOMPoint& aPointToSplit) {
  const WSScanResult nextVisibleThing =
      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
          {}, aPointToSplit, &aBlockElementToSplit);
  if (!nextVisibleThing.ReachedBlockBoundary()) {
    return aPointToSplit;
  }
  const WSScanResult prevVisibleThing =
      WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
          {WSRunScanner::Option::StopAtVisibleEmptyInlineContainers},
          aPointToSplit, &aBlockElementToSplit);
  if (!prevVisibleThing.ReachedLineBreak()) {
    return aPointToSplit;
  }
  EditorDOMPoint pointToSplit = aPointToSplit;
  EditorLineBreak precedingLineBreak =
      prevVisibleThing.CreateEditorLineBreak<EditorLineBreak>();
  {
    // FIXME: Once bug 1951041 is fixed in the layout level, we don't need to
    // treat collapsible white-spaces before invisible <br> elements here.
    AutoTrackDOMPoint trackPointToSplit(mHTMLEditor.RangeUpdaterRef(),
                                        &pointToSplit);
    Maybe<AutoTrackLineBreak> trackPrecedingLineBreak;
    if (precedingLineBreak.IsPreformattedLineBreak()) {
      trackPrecedingLineBreak.emplace(mHTMLEditor.RangeUpdaterRef(),
                                      &precedingLineBreak);
    }
    Result<EditorDOMPoint, nsresult>
        normalizePrecedingWhiteSpacesResultOrError =
            [&]() MOZ_CAN_RUN_SCRIPT -> Result<EditorDOMPoint, nsresult> {
      if (precedingLineBreak.IsHTMLBRElement() ||
          precedingLineBreak.IsPreformattedLineBreakAtStartOfText()) {
        Result<EditorDOMPoint, nsresult> ret =
            WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesBefore(
                mHTMLEditor, precedingLineBreak.To<EditorDOMPoint>(), {});
        NS_WARNING_ASSERTION(
            ret.isOk(),
            "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesBefore() failed");
        return ret;
      }
      Result<EditorDOMPoint, nsresult> ret =
          WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
              mHTMLEditor, precedingLineBreak.To<EditorDOMPoint>(), {});
      NS_WARNING_ASSERTION(
          ret.isOk(),
          "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() failed");
      return ret;
    }();
    if (NS_WARN_IF(normalizePrecedingWhiteSpacesResultOrError.isErr())) {
      return normalizePrecedingWhiteSpacesResultOrError.propagateErr();
    }
  }
  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
          &aBlockElementToSplit)) ||
      NS_WARN_IF(!precedingLineBreak.IsDeletableFromComposedDoc())) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  {
    AutoTrackDOMPoint trackPointToSplit(mHTMLEditor.RangeUpdaterRef(),
                                        &pointToSplit);
    Result<EditorDOMPoint, nsresult> deleteInvisibleLineBreakResult =
        mHTMLEditor.DeleteLineBreakWithTransaction(
            precedingLineBreak, nsIEditor::eNoStrip, aBlockElementToSplit);
    if (MOZ_UNLIKELY(deleteInvisibleLineBreakResult.isErr())) {
      NS_WARNING("HTMLEditor::DeleteLineBreakWithTransaction() failed");
      return deleteInvisibleLineBreakResult.propagateErr();
    }
  }
  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
          &aBlockElementToSplit))) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  return pointToSplit;
}

Result<EditorDOMPoint, nsresult> HTMLEditor::AutoInsertParagraphHandler::
    MaybeInsertFollowingBRElementToPreserveRightBlock(
        const Element& aBlockElementToSplit,
        const EditorDOMPoint& aPointToSplit) {
  MOZ_ASSERT(HTMLEditUtils::IsSplittableNode(aBlockElementToSplit));
  MOZ_ASSERT(aPointToSplit.ContainerAs<nsIContent>()->IsInclusiveDescendantOf(
      &aBlockElementToSplit));

  const Element* const closestContainerElement =
      HTMLEditUtils::GetInclusiveAncestorElement(
          *aPointToSplit.ContainerAs<nsIContent>(),
          HTMLEditUtils::ClosestContainerElementOrVoidAncestorLimiter,
          BlockInlineCheck::UseComputedDisplayOutsideStyle,
          &aBlockElementToSplit);
  MOZ_ASSERT(closestContainerElement);
  MOZ_ASSERT(HTMLEditUtils::IsSplittableNode(*closestContainerElement));

  // If we're at end of the paragraph and there are some inline container
  // elements, we want to preserve the inline containers to preserve their
  // styles.
  Maybe<EditorLineBreak> unnecessaryLineBreak;
  const EditorDOMPoint pointToInsertFollowingBRElement =
      [&]() MOZ_NEVER_INLINE_DEBUG {
        const WSScanResult nextVisibleThing =
            WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
                {}, aPointToSplit, &aBlockElementToSplit);
        if (nextVisibleThing.ReachedLineBreak()) {
          EditorLineBreak lineBreak =
              nextVisibleThing.CreateEditorLineBreak<EditorLineBreak>();
          // If there is a <br> in the closest ancestor container element, we
          // can use it. Note that we don't want to make an empty paragraph
          // which contains only a preformatted linefeed.
          if (lineBreak.IsHTMLBRElement() &&
              lineBreak.BRElementRef().GetParentNode() ==
                  closestContainerElement &&
              // The serializer requires a normal <br> in the empty right
              // paragraph so that we cannot reuse it if it's a padding <br>.
              // See bug 1385905.
              !lineBreak.BRElementRef().IsPaddingForEmptyLastLine() &&
              !lineBreak.BRElementRef().IsPaddingForEmptyEditor()) {
            return EditorDOMPoint();
          }
          // If the line break is not followed by current block boundary, we
          // don't need to insert a padding <br> element for the right paragraph
          // because it'll have some visible content.
          if (!lineBreak.IsFollowedByCurrentBlockBoundary()) {
            return EditorDOMPoint();
          }
          unnecessaryLineBreak.emplace(std::move(lineBreak));
        }
        // If it's not directly followed by current block boundary, we don't
        // need to insert a padding <br> element for the right paragraph because
        // it'll have some visible content.
        else if (!nextVisibleThing.ReachedCurrentBlockBoundary()) {
          return EditorDOMPoint();
        }
        // We want to insert a padding <br> into the closest ancestor container
        // element to preserve the style provided by it.
        EditorDOMPoint candidatePoint = aPointToSplit;
        for (; candidatePoint.GetContainer() != closestContainerElement;
             candidatePoint = candidatePoint.AfterContainer()) {
          MOZ_ASSERT(candidatePoint.GetContainer() != &aBlockElementToSplit);
        }
        return candidatePoint;
      }();

  if (unnecessaryLineBreak) {
    Result<EditorDOMPoint, nsresult> deleteLineBreakResultOrError =
        mHTMLEditor.DeleteLineBreakWithTransaction(
            *unnecessaryLineBreak, nsIEditor::eNoStrip, aBlockElementToSplit);
    if (MOZ_UNLIKELY(deleteLineBreakResultOrError.isErr())) {
      NS_WARNING("HTMLEditor::DeleteLineBreakWithTransaction() failed");
      return deleteLineBreakResultOrError.propagateErr();
    }
    if (NS_WARN_IF(!aPointToSplit.IsSetAndValidInComposedDoc())) {
      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    }
    if (pointToInsertFollowingBRElement.IsSet() &&
        (NS_WARN_IF(!pointToInsertFollowingBRElement
                         .IsInContentNodeAndValidInComposedDoc()) ||
         NS_WARN_IF(!pointToInsertFollowingBRElement.GetContainer()
                         ->IsInclusiveDescendantOf(&aBlockElementToSplit)))) {
      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    }
  }
  EditorDOMPoint pointToSplit(aPointToSplit);
  if (pointToInsertFollowingBRElement.IsSet()) {
    Maybe<AutoTrackDOMPoint> trackPointToSplit;
    if (pointToSplit.GetContainer() ==
        pointToInsertFollowingBRElement.GetContainer()) {
      trackPointToSplit.emplace(mHTMLEditor.RangeUpdaterRef(), &pointToSplit);
    }
    Result<CreateElementResult, nsresult> insertPaddingBRElementResultOrError =
        mHTMLEditor.InsertBRElement(
            WithTransaction::Yes,
            // We don't want to expose the <br> for IME, but the plaintext
            // serializer requires this. See bug 1385905.
            BRElementType::Normal, pointToInsertFollowingBRElement);
    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
      return insertPaddingBRElementResultOrError.propagateErr();
    }
    insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
  }
  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
          &aBlockElementToSplit))) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  if (mHTMLEditor.GetDefaultParagraphSeparator() != ParagraphSeparator::br) {
    return pointToSplit;
  }
  // If we're in the legacy mode, we don't want the right paragraph start with
  // an empty line.  So, if the right paragraph now starts with 2 <br> elements,
  // remove the second one.  (The first one is in the closest container element,
  // so, we want to keep it.)
  const WSScanResult nextVisibleThing =
      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
          {}, pointToSplit, &aBlockElementToSplit);
  if (!nextVisibleThing.ReachedBRElement()) {
    return pointToSplit;
  }
  const WSScanResult nextVisibleThingAfterFirstBRElement =
      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
          {}, nextVisibleThing.PointAfterReachedContent<EditorRawDOMPoint>(),
          &aBlockElementToSplit);
  if (!nextVisibleThingAfterFirstBRElement.ReachedBRElement()) {
    return pointToSplit;
  }
  nsresult rv = mHTMLEditor.DeleteNodeWithTransaction(
      MOZ_KnownLive(*nextVisibleThingAfterFirstBRElement.BRElementPtr()));
  if (NS_FAILED(rv)) {
    NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
    return Err(rv);
  }
  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
          &aBlockElementToSplit))) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  return pointToSplit;
}

Result<SplitNodeResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::SplitParagraphWithTransaction(
    Element& aBlockElementToSplit, const EditorDOMPoint& aPointToSplit) {
  // First, maybe the split point follows an invisible <br>.  E.g., when
  // `<p><a href=foo>foo[]<br></a></p>`,
  // GetBetterSplitPointToAvoidToContinueLink() adjusted the split point as
  // `<p><a href=foo>foo<br></a>{}</p>`.  Then, we shouldn't insert another
  // <br> to end of the left <p> to make the last line visible.  Even though we
  // need to insert an invisible <br> element later, let's delete the invisible
  // line break first to make this method simpler.
  Result<EditorDOMPoint, nsresult> deleteInvisibleLineBreakResultOrError =
      EnsureNoInvisibleLineBreakBeforePointToSplit(aBlockElementToSplit,
                                                   aPointToSplit);
  if (MOZ_UNLIKELY(deleteInvisibleLineBreakResultOrError.isErr())) {
    NS_WARNING(
        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() failed");
    return deleteInvisibleLineBreakResultOrError.propagateErr();
  }
  EditorDOMPoint pointToSplit = deleteInvisibleLineBreakResultOrError.unwrap();
  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
  MOZ_ASSERT(pointToSplit.GetContainer()->IsInclusiveDescendantOf(
      &aBlockElementToSplit));

  // Then, we need to keep the visibility of the surrounding collapsible
  // white-spaces at the split point.
  Result<EditorDOMPoint, nsresult> preparationResult =
      WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
          mHTMLEditor, pointToSplit, aBlockElementToSplit);
  if (MOZ_UNLIKELY(preparationResult.isErr())) {
    NS_WARNING(
        "WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement() failed");
    return preparationResult.propagateErr();
  }
  pointToSplit = preparationResult.unwrap();
  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
  MOZ_ASSERT(pointToSplit.GetContainer()->IsInclusiveDescendantOf(
      &aBlockElementToSplit));

  // Next, if there are some inline elements which we will split and we're
  // splitting the deepest one at end of it, we need to put invisible <br>
  // before splitting to preserve the cloned inline elements in the new
  // paragraph.
  {
    Result<EditorDOMPoint, nsresult> insertPaddingBRElementResultOrError =
        MaybeInsertFollowingBRElementToPreserveRightBlock(aBlockElementToSplit,
                                                          pointToSplit);
    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
      NS_WARNING(
          "AutoInsertParagraphHandler::"
          "MaybeInsertFollowingBRElementToPreserveRightBlock() failed");
      return insertPaddingBRElementResultOrError.propagateErr();
    }
    pointToSplit = insertPaddingBRElementResultOrError.unwrap();
    if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
        NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
            &aBlockElementToSplit))) {
      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    }
  }

  // Then, split current paragraph.
  const RefPtr<Element> deepestContainerElementToSplit =
      HTMLEditUtils::GetInclusiveAncestorElement(
          *pointToSplit.ContainerAs<nsIContent>(),
          HTMLEditUtils::ClosestContainerElementOrVoidAncestorLimiter,
          BlockInlineCheck::UseComputedDisplayOutsideStyle,
          &aBlockElementToSplit);
  if (NS_WARN_IF(!deepestContainerElementToSplit)) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  Result<SplitNodeResult, nsresult> splitDivOrPResultOrError =
      mHTMLEditor.SplitNodeDeepWithTransaction(
          aBlockElementToSplit, pointToSplit,
          SplitAtEdges::eAllowToCreateEmptyContainer);
  if (MOZ_UNLIKELY(splitDivOrPResultOrError.isErr())) {
    NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed");
    return splitDivOrPResultOrError;
  }
  SplitNodeResult splitDivOrPResult = splitDivOrPResultOrError.unwrap();
  if (MOZ_UNLIKELY(!splitDivOrPResult.DidSplit())) {
    NS_WARNING(
        "HTMLEditor::SplitNodeDeepWithTransaction() didn't split any nodes");
    return splitDivOrPResult;
  }

  // We'll compute caret suggestion later.  So the simple result is not needed.
  splitDivOrPResult.IgnoreCaretPointSuggestion();

  autoconst leftDivOrParagraphElement =
      splitDivOrPResult.GetPreviousContentAs<Element>();
  MOZ_ASSERT(leftDivOrParagraphElement,
             "SplitNodeResult::GetPreviousContent() should return something if "
             "DidSplit() returns true");
  autoconst rightDivOrParagraphElement =
      splitDivOrPResult.GetNextContentAs<Element>();
  MOZ_ASSERT(rightDivOrParagraphElement,
             "SplitNodeResult::GetNextContent() should return something if "
             "DidSplit() returns true");

  // Remove ID attribute on the paragraph from the right node.
  // MOZ_KnownLive(rightDivOrParagraphElement) because it's grabbed by
  // splitDivOrPResult.
  nsresult rv = mHTMLEditor.RemoveAttributeWithTransaction(
      MOZ_KnownLive(*rightDivOrParagraphElement), *nsGkAtoms::id);
  if (NS_FAILED(rv)) {
    NS_WARNING(
        "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::id) failed");
    return Err(rv);
  }

  // Finally, we need to ensure that both paragraphs are visible even if they
  // are empty.  Note that we need to use padding <br> element for the empty
  // last line as usual because it won't appear as a line break when serialized
  // by ContentEventHandler.  Thus, if we were using normal <br> elements,
  // disappearing following line break of composition string would make IME
  // confused.
  if (NS_WARN_IF(!deepestContainerElementToSplit->IsInclusiveDescendantOf(
          leftDivOrParagraphElement))) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }
  const EditorDOMPoint pointToInsertBRElement = [&]() MOZ_NEVER_INLINE_DEBUG {
    // If we split the paragraph immediately after a block boundary or a line
    // break, we need to put a padding <br> to make an empty line.
    const WSScanResult prevVisibleThing =
        WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
            {}, EditorRawDOMPoint::AtEndOf(*deepestContainerElementToSplit),
            leftDivOrParagraphElement);
    if (prevVisibleThing.ReachedLineBoundary()) {
      return EditorDOMPoint::AtEndOf(*deepestContainerElementToSplit);
    }
    // If we split a descendant element and it's empty, we need to put a padding
    // <br> element into it to preserve the style of the element.
    if (deepestContainerElementToSplit == leftDivOrParagraphElement) {
      return EditorDOMPoint();
    }
    const WSScanResult nextVisibleThing =
        WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
            {}, EditorRawDOMPoint(deepestContainerElementToSplit, 0),
            leftDivOrParagraphElement);
    return nextVisibleThing.ReachedCurrentBlockBoundary()
               ? EditorDOMPoint::AtEndOf(*deepestContainerElementToSplit)
               : EditorDOMPoint();
  }();
  if (pointToInsertBRElement.IsSet()) {
    Result<CreateElementResult, nsresult> insertPaddingBRElementResultOrError =
        mHTMLEditor.InsertBRElement(
            WithTransaction::Yes,
            // XXX We don't want to expose the <br> for IME, but the plaintext
            // serializer requires this. See bug 1385905.
            BRElementType::Normal, pointToInsertBRElement);
    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
      return insertPaddingBRElementResultOrError.propagateErr();
    }
    insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
  }

  // The right paragraph should not be empty because
  // MaybeInsertFollowingBRElementToPreserveRightBlock() should've already put a
  // padding <br> before splitting the paragraph.
  if (NS_WARN_IF(HTMLEditUtils::IsEmptyNode(
          *rightDivOrParagraphElement,
          {EmptyCheckOption::TreatSingleBRElementAsVisible,
           EmptyCheckOption::TreatListItemAsVisible,
           EmptyCheckOption::TreatTableCellAsVisible}))) {
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }

  // Let's put caret at start of the first leaf container.
  nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
      *rightDivOrParagraphElement, {LeafNodeOption::TreatChildBlockAsLeafNode},
      BlockInlineCheck::UseComputedDisplayStyle);
  if (MOZ_UNLIKELY(!child)) {
    return SplitNodeResult(std::move(splitDivOrPResult),
                           EditorDOMPoint(rightDivOrParagraphElement, 0u));
  }

  return child->IsText() || HTMLEditUtils::IsContainerNode(*child)
             ? SplitNodeResult(std::move(splitDivOrPResult),
                               EditorDOMPoint(child, 0u))
             : SplitNodeResult(std::move(splitDivOrPResult),
                               EditorDOMPoint(child));
}

Result<CreateLineBreakResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::InsertBRElementIfEmptyBlockElement(
    Element& aMaybeBlockElement,
    InsertBRElementIntoEmptyBlock aInsertBRElementIntoEmptyBlock,
    BlockInlineCheck aBlockInlineCheck) {
  if (!HTMLEditUtils::IsBlockElement(aMaybeBlockElement, aBlockInlineCheck)) {
    return CreateLineBreakResult::NotHandled();
  }

  if (!HTMLEditUtils::IsEmptyNode(
          aMaybeBlockElement,
          {EmptyCheckOption::TreatSingleBRElementAsVisible})) {
    return CreateLineBreakResult::NotHandled();
  }

  // XXX: Probably, we should use
  //      InsertPaddingBRElementForEmptyLastLineWithTransaction here, and
  //      if there are some empty inline container, we should put the <br>
  //      into the last one.
  Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
      mHTMLEditor.InsertLineBreak(
          WithTransaction::Yes, LineBreakType::BRElement,
          aInsertBRElementIntoEmptyBlock == InsertBRElementIntoEmptyBlock::Start
              ? EditorDOMPoint(&aMaybeBlockElement, 0u)
              : EditorDOMPoint::AtEndOf(aMaybeBlockElement));
  NS_WARNING_ASSERTION(insertBRElementResultOrError.isOk(),
                       "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
                       "LineBreakType::BRElement) failed");
  return insertBRElementResultOrError;
}

// static
Element* HTMLEditor::AutoInsertParagraphHandler::
    GetDeepestFirstChildInlineContainerElement(Element& aBlockElement) {
  // XXX Should we ignore invisible children like empty Text, Comment, etc?
  Element* result = nullptr;
  for (Element* maybeDeepestInlineContainer =
           Element::FromNodeOrNull(aBlockElement.GetFirstChild());
       maybeDeepestInlineContainer &&
       HTMLEditUtils::IsInlineContent(
           *maybeDeepestInlineContainer,
           BlockInlineCheck::UseComputedDisplayStyle) &&
       HTMLEditUtils::IsContainerNode(*maybeDeepestInlineContainer);
       // FIXME: There may be visible node before first element child, so, here
       // is obviously wrong.
       maybeDeepestInlineContainer =
           maybeDeepestInlineContainer->GetFirstElementChild()) {
    result = maybeDeepestInlineContainer;
  }
  return result;
}

Result<InsertParagraphResult, nsresult>
HTMLEditor::AutoInsertParagraphHandler::HandleInListItemElement(
    Element& aListItemElement, const EditorDOMPoint& aPointToSplit) {
  MOZ_ASSERT(HTMLEditUtils::IsListItemElement(aListItemElement));

  // If aListItemElement is empty, then we want to outdent its content.
  if (&mEditingHost != aListItemElement.GetParentElement() &&
      HTMLEditUtils::IsEmptyBlockElement(
          aListItemElement,
          {EmptyCheckOption::TreatNonEditableContentAsInvisible},
          BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
    RefPtr<Element> leftListElement = aListItemElement.GetParentElement();
    // If the given list item element is not the last list item element of
    // its parent nor not followed by sub list elements, split the parent
    // before it.
    if (!HTMLEditUtils::IsLastChild(
            aListItemElement, {LeafNodeOption::IgnoreNonEditableNode},
            BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
      Result<SplitNodeResult, nsresult> splitListItemParentResult =
          mHTMLEditor.SplitNodeWithTransaction(
              EditorDOMPoint(&aListItemElement));
      if (MOZ_UNLIKELY(splitListItemParentResult.isErr())) {
        NS_WARNING("HTMLEditor::SplitNodeWithTransaction() failed");
        return splitListItemParentResult.propagateErr();
      }
      SplitNodeResult unwrappedSplitListItemParentResult =
          splitListItemParentResult.unwrap();
      if (MOZ_UNLIKELY(!unwrappedSplitListItemParentResult.DidSplit())) {
        NS_WARNING(
            "HTMLEditor::SplitNodeWithTransaction() didn't split the parent of "
            "aListItemElement");
        MOZ_ASSERT(
            !unwrappedSplitListItemParentResult.HasCaretPointSuggestion());
        return Err(NS_ERROR_FAILURE);
      }
      unwrappedSplitListItemParentResult.IgnoreCaretPointSuggestion();
      leftListElement =
          unwrappedSplitListItemParentResult.GetPreviousContentAs<Element>();
      MOZ_DIAGNOSTIC_ASSERT(leftListElement);
    }

    auto afterLeftListElement = EditorDOMPoint::After(leftListElement);
    if (MOZ_UNLIKELY(!afterLeftListElement.IsInContentNode())) {
      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    }

    // If aListItemElement is in an invalid sub-list element, move it into
    // the grand parent list element in order to outdent.
    if (HTMLEditUtils::IsListElement(
            *afterLeftListElement.ContainerAs<nsIContent>())) {
      Result<MoveNodeResult, nsresult> moveListItemElementResult =
          mHTMLEditor.MoveNodeWithTransaction(aListItemElement,
                                              afterLeftListElement);
      if (MOZ_UNLIKELY(moveListItemElementResult.isErr())) {
        NS_WARNING("HTMLEditor::MoveNodeWithTransaction() failed");
        return moveListItemElementResult.propagateErr();
      }
      moveListItemElementResult.inspect().IgnoreCaretPointSuggestion();
      return InsertParagraphResult(aListItemElement,
                                   EditorDOMPoint(&aListItemElement, 0u));
    }

    // Otherwise, replace the empty aListItemElement with a new paragraph.
    nsresult rv = mHTMLEditor.DeleteNodeWithTransaction(aListItemElement);
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
      return Err(rv);
    }
    nsStaticAtom& newParagraphTagName =
        &mDefaultParagraphSeparatorTagName == nsGkAtoms::br
            ? *nsGkAtoms::p
            : mDefaultParagraphSeparatorTagName;
    // MOZ_KnownLive(newParagraphTagName) because it's available until shutdown.
    Result<CreateElementResult, nsresult> createNewParagraphElementResult =
        mHTMLEditor.CreateAndInsertElement(
            WithTransaction::Yes, MOZ_KnownLive(newParagraphTagName),
            afterLeftListElement, HTMLEditor::InsertNewBRElement);
    if (MOZ_UNLIKELY(createNewParagraphElementResult.isErr())) {
      NS_WARNING(
          "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
      return createNewParagraphElementResult.propagateErr();
    }
    createNewParagraphElementResult.inspect().IgnoreCaretPointSuggestion();
    MOZ_ASSERT(createNewParagraphElementResult.inspect().GetNewNode());
    EditorDOMPoint pointToPutCaret(
        createNewParagraphElementResult.inspect().GetNewNode(), 0u);
    return InsertParagraphResult(
        *createNewParagraphElementResult.inspect().GetNewNode(),
        std::move(pointToPutCaret));
  }

  const EditorDOMPoint pointToSplit = GetBetterPointToSplitParagraph(
      aListItemElement, aPointToSplit, mEditingHost);
  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());

  // If insertParagraph at end of <dt> or <dd>, we should put opposite type list
  // item without copying the style of end of aListItemElement.
  // FIXME: Chrome does not do this.  So, we should stop doing this at least on
  // Firefox later.
  if (aListItemElement.IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::dd) &&
      SplitPointIsEndOfSplittingBlock(aListItemElement, pointToSplit,
                                      IgnoreBlockBoundaries::Yes) &&
      // However, don't do that if we're handling it in empty list item.
      !SplitPointIsStartOfSplittingBlock(aListItemElement, pointToSplit,
                                         IgnoreBlockBoundaries::Yes)) {
    nsStaticAtom& oppositeTypeListItemTag =
        aListItemElement.IsHTMLElement(nsGkAtoms::dt) ? *nsGkAtoms::dd
                                                      : *nsGkAtoms::dt;
    // MOZ_KnownLive(oppositeTypeListItemTag) because it's available
    // until shutdown.
    Result<CreateElementResult, nsresult>
        insertOppositeTypeListItemResultOrError =
            mHTMLEditor.CreateAndInsertElement(
                WithTransaction::Yes, MOZ_KnownLive(oppositeTypeListItemTag),
                EditorDOMPoint::After(aListItemElement),
                HTMLEditor::InsertNewBRElement);
    if (MOZ_UNLIKELY(insertOppositeTypeListItemResultOrError.isErr())) {
      NS_WARNING(
          "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
      return insertOppositeTypeListItemResultOrError.propagateErr();
    }
    CreateElementResult insertOppositeTypeListItemResult =
        insertOppositeTypeListItemResultOrError.unwrap();
    insertOppositeTypeListItemResult.IgnoreCaretPointSuggestion();
    RefPtr<Element> oppositeTypeListItemElement =
        insertOppositeTypeListItemResult.UnwrapNewNode();
    EditorDOMPoint startOfOppositeTypeListItem(oppositeTypeListItemElement, 0u);
    MOZ_ASSERT(oppositeTypeListItemElement);
    return InsertParagraphResult(std::move(oppositeTypeListItemElement),
                                 std::move(startOfOppositeTypeListItem));
  }

  // If aListItemElement has some content or aListItemElement is empty but it's
  // a child of editing host, we want a new list item at the same list level.
  // First, sort out white-spaces.
  Result<SplitNodeResult, nsresult> splitListItemResultOrError =
      SplitParagraphWithTransaction(aListItemElement, pointToSplit);
  if (MOZ_UNLIKELY(splitListItemResultOrError.isErr())) {
    NS_WARNING(
        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() failed");
    return splitListItemResultOrError.propagateErr();
  }
  SplitNodeResult splitListItemElement = splitListItemResultOrError.unwrap();
  EditorDOMPoint pointToPutCaret = splitListItemElement.UnwrapCaretPoint();
  if (MOZ_UNLIKELY(!aListItemElement.GetParent())) {
    NS_WARNING("Somebody disconnected the target listitem from the parent");
    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
  }

  // If aListItemElement is not replaced, we should not do anything anymore.
  if (MOZ_UNLIKELY(!splitListItemElement.DidSplit()) ||
      NS_WARN_IF(!splitListItemElement.GetNewContentAs<Element>()) ||
      NS_WARN_IF(!splitListItemElement.GetOriginalContentAs<Element>())) {
    NS_WARNING(
        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() didn't "
        "split the listitem");
    return Err(NS_ERROR_FAILURE);
  }
  autoconst rightListItemElement =
      splitListItemElement.GetNextContentAs<Element>();
  return InsertParagraphResult(*rightListItemElement,
                               std::move(pointToPutCaret));
}

// static
bool HTMLEditor::AutoInsertParagraphHandler::SplitPointIsStartOfSplittingBlock(
    const Element& aBlockElementToSplit, const EditorDOMPoint& aPointToSplit,
    IgnoreBlockBoundaries aIgnoreBlockBoundaries) {
  EditorRawDOMPoint pointToSplit = aPointToSplit.To<EditorRawDOMPoint>();
  while (true) {
    const WSScanResult prevVisibleThing =
        WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary({}, pointToSplit);
    if (!prevVisibleThing.ReachedCurrentBlockBoundary()) {
      return false;
    }
    if (prevVisibleThing.ElementPtr() == &aBlockElementToSplit) {
      return true;
    }
    if (!static_cast<bool>(aIgnoreBlockBoundaries)) {
      return false;
    }
    pointToSplit = pointToSplit.ParentPoint();
  }
}

// static
bool HTMLEditor::AutoInsertParagraphHandler::SplitPointIsEndOfSplittingBlock(
    const Element& aBlockElementToSplit, const EditorDOMPoint& aPointToSplit,
    IgnoreBlockBoundaries aIgnoreBlockBoundaries) {
  bool maybeFollowedByInvisibleBRElement = true;
  EditorRawDOMPoint pointToSplit = aPointToSplit.To<EditorRawDOMPoint>();
  while (true) {
    WSScanResult nextVisibleThing =
        WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
            {}, pointToSplit, &aBlockElementToSplit);
    if (maybeFollowedByInvisibleBRElement &&
        (nextVisibleThing.ReachedBRElement() ||
         nextVisibleThing.ReachedPreformattedLineBreak())) {
      nextVisibleThing =
          WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
              {},
              nextVisibleThing.PointAfterReachedContent<EditorRawDOMPoint>(),
              &aBlockElementToSplit);
    }
    if (!nextVisibleThing.ReachedCurrentBlockBoundary()) {
      return false;
    }
    if (nextVisibleThing.ElementPtr() == &aBlockElementToSplit) {
      return true;
    }
    if (!static_cast<bool>(aIgnoreBlockBoundaries)) {
      return false;
    }
    pointToSplit = pointToSplit.AfterContainer();
    // <br> element after other block boundary creates an empty line so that
    // it's always visible.
    maybeFollowedByInvisibleBRElement = false;
  }
}

}  // namespace mozilla

Messung V0.5 in Prozent
C=89 H=97 G=93

¤ Dauer der Verarbeitung: 0.28 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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 und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142