tor-browser

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

HTMLEditorInsertParagraphHandler.cpp (94903B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 sw=2 et tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "EditorBase.h"
      8 #include "HTMLEditor.h"
      9 #include "HTMLEditorInlines.h"
     10 #include "HTMLEditorNestedClasses.h"
     11 
     12 #include <utility>
     13 
     14 #include "AutoClonedRangeArray.h"
     15 #include "CSSEditUtils.h"
     16 #include "EditAction.h"
     17 #include "EditorDOMPoint.h"
     18 #include "EditorLineBreak.h"
     19 #include "EditorUtils.h"
     20 #include "HTMLEditHelpers.h"
     21 #include "HTMLEditUtils.h"
     22 #include "PendingStyles.h"  // for SpecifiedStyle
     23 #include "WhiteSpaceVisibilityKeeper.h"
     24 #include "WSRunScanner.h"
     25 
     26 #include "ErrorList.h"
     27 #include "mozilla/Assertions.h"
     28 #include "mozilla/Attributes.h"
     29 #include "mozilla/ContentIterator.h"
     30 #include "mozilla/EditorForwards.h"
     31 #include "mozilla/Maybe.h"
     32 #include "mozilla/PresShell.h"
     33 #include "mozilla/dom/Element.h"
     34 #include "mozilla/dom/HTMLBRElement.h"
     35 #include "mozilla/dom/Selection.h"
     36 #include "nsAtom.h"
     37 #include "nsContentUtils.h"
     38 #include "nsDebug.h"
     39 #include "nsError.h"
     40 #include "nsGkAtoms.h"
     41 #include "nsIContent.h"
     42 #include "nsIFrame.h"
     43 #include "nsINode.h"
     44 #include "nsTArray.h"
     45 #include "nsTextNode.h"
     46 
     47 namespace mozilla {
     48 
     49 using namespace dom;
     50 using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
     51 using EmptyCheckOptions = HTMLEditUtils::EmptyCheckOptions;
     52 using LeafNodeType = HTMLEditUtils::LeafNodeType;
     53 using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
     54 using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
     55 
     56 Result<EditActionResult, nsresult>
     57 HTMLEditor::InsertParagraphSeparatorAsSubAction(const Element& aEditingHost) {
     58  if (NS_WARN_IF(!mInitSucceeded)) {
     59    return Err(NS_ERROR_NOT_INITIALIZED);
     60  }
     61 
     62  {
     63    Result<EditActionResult, nsresult> result =
     64        CanHandleHTMLEditSubAction(CheckSelectionInReplacedElement::No);
     65    if (MOZ_UNLIKELY(result.isErr())) {
     66      NS_WARNING("HTMLEditor::CanHandleHTMLEditSubAction() failed");
     67      return result;
     68    }
     69    if (result.inspect().Canceled()) {
     70      return result;
     71    }
     72  }
     73 
     74  // XXX This may be called by execCommand() with "insertParagraph".
     75  //     In such case, naming the transaction "TypingTxnName" is odd.
     76  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName,
     77                                             ScrollSelectionIntoView::Yes,
     78                                             __FUNCTION__);
     79 
     80  IgnoredErrorResult ignoredError;
     81  AutoEditSubActionNotifier startToHandleEditSubAction(
     82      *this, EditSubAction::eInsertParagraphSeparator, nsIEditor::eNext,
     83      ignoredError);
     84  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
     85    return Err(ignoredError.StealNSResult());
     86  }
     87  NS_WARNING_ASSERTION(
     88      !ignoredError.Failed(),
     89      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
     90 
     91  UndefineCaretBidiLevel();
     92 
     93  // If the selection isn't collapsed, delete it.
     94  if (!SelectionRef().IsCollapsed()) {
     95    nsresult rv =
     96        DeleteSelectionAsSubAction(nsIEditor::eNone, nsIEditor::eStrip);
     97    if (NS_FAILED(rv)) {
     98      NS_WARNING(
     99          "EditorBase::DeleteSelectionAsSubAction(eNone, eStrip) failed");
    100      return Err(rv);
    101    }
    102  }
    103 
    104  AutoInsertParagraphHandler insertParagraphHandler(*this, aEditingHost);
    105  Result<EditActionResult, nsresult> insertParagraphResult =
    106      insertParagraphHandler.Run();
    107  NS_WARNING_ASSERTION(insertParagraphResult.isOk(),
    108                       "AutoInsertParagraphHandler::Run() failed");
    109  return insertParagraphResult;
    110 }
    111 
    112 Result<EditActionResult, nsresult>
    113 HTMLEditor::AutoInsertParagraphHandler::Run() {
    114  MOZ_ASSERT(mHTMLEditor.IsEditActionDataAvailable());
    115  MOZ_ASSERT(mHTMLEditor.IsTopLevelEditSubActionDataAvailable());
    116 
    117  nsresult rv = mHTMLEditor.EnsureNoPaddingBRElementForEmptyEditor();
    118  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
    119    return Err(NS_ERROR_EDITOR_DESTROYED);
    120  }
    121  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    122                       "EditorBase::EnsureNoPaddingBRElementForEmptyEditor() "
    123                       "failed, but ignored");
    124 
    125  if (NS_SUCCEEDED(rv) && mHTMLEditor.SelectionRef().IsCollapsed()) {
    126    nsresult rv =
    127        mHTMLEditor.EnsureCaretNotAfterInvisibleBRElement(mEditingHost);
    128    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
    129      return Err(NS_ERROR_EDITOR_DESTROYED);
    130    }
    131    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    132                         "HTMLEditor::EnsureCaretNotAfterInvisibleBRElement() "
    133                         "failed, but ignored");
    134    if (NS_SUCCEEDED(rv)) {
    135      nsresult rv = mHTMLEditor.PrepareInlineStylesForCaret();
    136      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
    137        return Err(NS_ERROR_EDITOR_DESTROYED);
    138      }
    139      NS_WARNING_ASSERTION(
    140          NS_SUCCEEDED(rv),
    141          "HTMLEditor::PrepareInlineStylesForCaret() failed, but ignored");
    142    }
    143  }
    144 
    145  AutoClonedSelectionRangeArray selectionRanges(mHTMLEditor.SelectionRef());
    146  selectionRanges.EnsureOnlyEditableRanges(mEditingHost);
    147 
    148  auto pointToInsert =
    149      selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
    150  if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
    151    return Err(NS_ERROR_FAILURE);
    152  }
    153  // If the element can have a <br> element (it means that the element or its
    154  // container must be able to have <div> or <p> too), we can handle
    155  // insertParagraph at the point.
    156  pointToInsert = HTMLEditUtils::GetPossiblePointToInsert(
    157      pointToInsert, *nsGkAtoms::br, mEditingHost);
    158  if (NS_WARN_IF(!pointToInsert.IsSet())) {
    159    return Err(NS_ERROR_FAILURE);
    160  }
    161  MOZ_ASSERT(pointToInsert.IsInContentNode());
    162 
    163  if (mHTMLEditor.IsMailEditor()) {
    164    if (const RefPtr<Element> mailCiteElement =
    165            mHTMLEditor.GetMostDistantAncestorMailCiteElement(
    166                *pointToInsert.ContainerAs<nsIContent>())) {
    167      // Split any mailcites in the way.  Should we abort this if we encounter
    168      // table cell boundaries?
    169      Result<CaretPoint, nsresult> caretPointOrError =
    170          HandleInMailCiteElement(*mailCiteElement, pointToInsert);
    171      if (MOZ_UNLIKELY(caretPointOrError.isErr())) {
    172        NS_WARNING(
    173            "AutoInsertParagraphHandler::HandleInMailCiteElement() failed");
    174        return caretPointOrError.propagateErr();
    175      }
    176      CaretPoint caretPoint = caretPointOrError.unwrap();
    177      MOZ_ASSERT(caretPoint.HasCaretPointSuggestion());
    178      MOZ_ASSERT(caretPoint.CaretPointRef().GetInterlinePosition() ==
    179                 InterlinePosition::StartOfNextLine);
    180      MOZ_ASSERT(caretPoint.CaretPointRef().GetChild());
    181      MOZ_ASSERT(
    182          caretPoint.CaretPointRef().GetChild()->IsHTMLElement(nsGkAtoms::br));
    183      nsresult rv = caretPoint.SuggestCaretPointTo(mHTMLEditor, {});
    184      if (NS_FAILED(rv)) {
    185        NS_WARNING("CaretPoint::SuggestCaretPointTo() failed");
    186        return Err(rv);
    187      }
    188      return EditActionResult::HandledResult();
    189    }
    190  }
    191 
    192  // If the active editing host is an inline element, or if the active editing
    193  // host is the block parent itself and we're configured to use <br> as a
    194  // paragraph separator, just append a <br>.
    195  // If the editing host parent element is editable, it means that the editing
    196  // host must be a <body> element and the selection may be outside the body
    197  // element.  If the selection is outside the editing host, we should not
    198  // insert new paragraph nor <br> element.
    199  // XXX Currently, we don't support editing outside <body> element, but Blink
    200  //     does it.
    201  if (mEditingHost.GetParentElement() &&
    202      HTMLEditUtils::IsSimplyEditableNode(*mEditingHost.GetParentElement()) &&
    203      !nsContentUtils::ContentIsFlattenedTreeDescendantOf(
    204          pointToInsert.ContainerAs<nsIContent>(), &mEditingHost)) {
    205    return Err(NS_ERROR_EDITOR_NO_EDITABLE_RANGE);
    206  }
    207 
    208  // Look for the nearest parent block.  However, don't return error even if
    209  // there is no block parent here because in such case, i.e., editing host
    210  // is an inline element, we should insert <br> simply.
    211  RefPtr<Element> editableBlockElement =
    212      HTMLEditUtils::GetInclusiveAncestorElement(
    213          *pointToInsert.ContainerAs<nsIContent>(),
    214          HTMLEditUtils::ClosestEditableBlockElementOrButtonElement,
    215          BlockInlineCheck::UseComputedDisplayOutsideStyle);
    216 
    217  // If we cannot insert a <p>/<div> element at the selection, we should insert
    218  // a <br> element or a linefeed instead.
    219  if (ShouldInsertLineBreakInstead(editableBlockElement, pointToInsert)) {
    220    const Maybe<LineBreakType> lineBreakType =
    221        mHTMLEditor.GetPreferredLineBreakType(
    222            *pointToInsert.ContainerAs<nsIContent>(), mEditingHost);
    223    if (MOZ_UNLIKELY(!lineBreakType)) {
    224      // Cannot insert a line break there.
    225      return EditActionResult::IgnoredResult();
    226    }
    227    if (lineBreakType.value() == LineBreakType::Linefeed) {
    228      Result<EditActionResult, nsresult> insertLinefeedResultOrError =
    229          HandleInsertLinefeed(pointToInsert);
    230      NS_WARNING_ASSERTION(
    231          insertLinefeedResultOrError.isOk(),
    232          "AutoInsertParagraphHandler::HandleInsertLinefeed() failed");
    233      return insertLinefeedResultOrError;
    234    }
    235    Result<EditActionResult, nsresult> insertBRElementResultOrError =
    236        HandleInsertBRElement(pointToInsert);
    237    NS_WARNING_ASSERTION(
    238        insertBRElementResultOrError.isOk(),
    239        "AutoInsertParagraphHandler::HandleInsertBRElement() failed");
    240    return insertBRElementResultOrError;
    241  }
    242 
    243  RefPtr<Element> blockElementToPutCaret;
    244  // If the default paragraph separator is not <br> and selection is not in
    245  // a splittable block element, we should wrap selected contents in a new
    246  // paragraph, then, split it.
    247  if (!HTMLEditUtils::IsSplittableNode(*editableBlockElement) &&
    248      mDefaultParagraphSeparator != ParagraphSeparator::br) {
    249    MOZ_ASSERT(mDefaultParagraphSeparator == ParagraphSeparator::div ||
    250               mDefaultParagraphSeparator == ParagraphSeparator::p);
    251    // FIXME: If there is no splittable block element, the other browsers wrap
    252    // the right nodes into new paragraph, but keep the left node as-is.
    253    // We should follow them to make here simpler and better compatibility.
    254    Result<RefPtr<Element>, nsresult> suggestBlockElementToPutCaretOrError =
    255        mHTMLEditor.FormatBlockContainerWithTransaction(
    256            selectionRanges,
    257            MOZ_KnownLive(HTMLEditor::ToParagraphSeparatorTagName(
    258                mDefaultParagraphSeparator)),
    259            // For keeping the traditional behavior at insertParagraph command,
    260            // let's use the XUL paragraph state command targets even if we're
    261            // handling HTML insertParagraph command.
    262            FormatBlockMode::XULParagraphStateCommand, mEditingHost);
    263    if (MOZ_UNLIKELY(suggestBlockElementToPutCaretOrError.isErr())) {
    264      NS_WARNING("HTMLEditor::FormatBlockContainerWithTransaction() failed");
    265      return suggestBlockElementToPutCaretOrError.propagateErr();
    266    }
    267    if (selectionRanges.HasSavedRanges()) {
    268      selectionRanges.RestoreFromSavedRanges();
    269    }
    270    pointToInsert = selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
    271    if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
    272      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    273    }
    274    MOZ_ASSERT(pointToInsert.IsSetAndValidInComposedDoc());
    275 
    276    editableBlockElement = HTMLEditUtils::GetInclusiveAncestorElement(
    277        *pointToInsert.ContainerAs<nsIContent>(),
    278        HTMLEditUtils::ClosestEditableBlockElementOrButtonElement,
    279        BlockInlineCheck::UseComputedDisplayOutsideStyle);
    280    if (NS_WARN_IF(!editableBlockElement)) {
    281      return Err(NS_ERROR_UNEXPECTED);
    282    }
    283    if (NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(*editableBlockElement))) {
    284      // Didn't create a new block for some reason, fall back to <br>
    285      Result<EditActionResult, nsresult> insertBRElementResultOrError =
    286          HandleInsertBRElement(pointToInsert, blockElementToPutCaret);
    287      NS_WARNING_ASSERTION(
    288          insertBRElementResultOrError.isOk(),
    289          "AutoInsertParagraphHandler::HandleInsertBRElement() failed");
    290      return insertBRElementResultOrError;
    291    }
    292    // We want to collapse selection in the editable block element.
    293    blockElementToPutCaret = editableBlockElement;
    294  }
    295 
    296  // If block is empty, populate with br.  (For example, imagine a div that
    297  // contains the word "text".  The user selects "text" and types return.
    298  // "Text" is deleted leaving an empty block.  We want to put in one br to
    299  // make block have a line.  Then code further below will put in a second br.)
    300  RefPtr<Element> insertedPaddingBRElement;
    301  {
    302    Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
    303        InsertBRElementIfEmptyBlockElement(
    304            *editableBlockElement, InsertBRElementIntoEmptyBlock::End,
    305            BlockInlineCheck::UseComputedDisplayOutsideStyle);
    306    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
    307      NS_WARNING(
    308          "AutoInsertParagraphHandler::InsertBRElementIfEmptyBlockElement("
    309          "InsertBRElementIntoEmptyBlock::End, "
    310          "BlockInlineCheck::UseComputedDisplayOutsideStyle) failed");
    311      return insertBRElementResultOrError.propagateErr();
    312    }
    313 
    314    CreateLineBreakResult insertBRElementResult =
    315        insertBRElementResultOrError.unwrap();
    316    insertBRElementResult.IgnoreCaretPointSuggestion();
    317    if (insertBRElementResult.Handled()) {
    318      insertedPaddingBRElement = &insertBRElementResult->BRElementRef();
    319    }
    320 
    321    pointToInsert = selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
    322    if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
    323      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    324    }
    325  }
    326 
    327  RefPtr<Element> maybeNonEditableListItem =
    328      HTMLEditUtils::GetClosestInclusiveAncestorListItemElement(
    329          *editableBlockElement, &mEditingHost);
    330  if (maybeNonEditableListItem &&
    331      HTMLEditUtils::IsSplittableNode(*maybeNonEditableListItem)) {
    332    Result<InsertParagraphResult, nsresult> insertParagraphInListItemResult =
    333        HandleInListItemElement(*maybeNonEditableListItem, pointToInsert);
    334    if (MOZ_UNLIKELY(insertParagraphInListItemResult.isErr())) {
    335      if (NS_WARN_IF(insertParagraphInListItemResult.unwrapErr() ==
    336                     NS_ERROR_EDITOR_DESTROYED)) {
    337        return Err(NS_ERROR_EDITOR_DESTROYED);
    338      }
    339      NS_WARNING(
    340          "AutoInsertParagraphHandler::HandleInListItemElement() failed, but "
    341          "ignored");
    342      return EditActionResult::HandledResult();
    343    }
    344    InsertParagraphResult unwrappedInsertParagraphInListItemResult =
    345        insertParagraphInListItemResult.unwrap();
    346    MOZ_ASSERT(unwrappedInsertParagraphInListItemResult.Handled());
    347    MOZ_ASSERT(unwrappedInsertParagraphInListItemResult.GetNewNode());
    348    const RefPtr<Element> listItemOrParagraphElement =
    349        unwrappedInsertParagraphInListItemResult.UnwrapNewNode();
    350    const EditorDOMPoint pointToPutCaret =
    351        unwrappedInsertParagraphInListItemResult.UnwrapCaretPoint();
    352    nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
    353        pointToPutCaret, listItemOrParagraphElement,
    354        {SuggestCaret::AndIgnoreTrivialError});
    355    if (NS_FAILED(rv)) {
    356      NS_WARNING(
    357          "AutoInsertParagraphHandler::"
    358          "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() failed");
    359      return Err(rv);
    360    }
    361    NS_WARNING_ASSERTION(rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
    362                         "CollapseSelection() failed, but ignored");
    363    return EditActionResult::HandledResult();
    364  }
    365 
    366  if (HTMLEditUtils::IsHeadingElement(*editableBlockElement)) {
    367    Result<InsertParagraphResult, nsresult>
    368        insertParagraphInHeadingElementResult =
    369            HandleInHeadingElement(*editableBlockElement, pointToInsert);
    370    if (MOZ_UNLIKELY(insertParagraphInHeadingElementResult.isErr())) {
    371      NS_WARNING(
    372          "AutoInsertParagraphHandler::HandleInHeadingElement() failed, but "
    373          "ignored");
    374      return EditActionResult::HandledResult();
    375    }
    376    InsertParagraphResult unwrappedInsertParagraphInHeadingElementResult =
    377        insertParagraphInHeadingElementResult.unwrap();
    378    if (unwrappedInsertParagraphInHeadingElementResult.Handled()) {
    379      MOZ_ASSERT(unwrappedInsertParagraphInHeadingElementResult.GetNewNode());
    380      blockElementToPutCaret =
    381          unwrappedInsertParagraphInHeadingElementResult.UnwrapNewNode();
    382    }
    383    const EditorDOMPoint pointToPutCaret =
    384        unwrappedInsertParagraphInHeadingElementResult.UnwrapCaretPoint();
    385    nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
    386        pointToPutCaret, blockElementToPutCaret,
    387        {SuggestCaret::OnlyIfHasSuggestion,
    388         SuggestCaret::AndIgnoreTrivialError});
    389    if (NS_FAILED(rv)) {
    390      NS_WARNING(
    391          "AutoInsertParagraphHandler::"
    392          "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() failed");
    393      return Err(rv);
    394    }
    395    NS_WARNING_ASSERTION(rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
    396                         "CollapseSelection() failed, but ignored");
    397    return EditActionResult::HandledResult();
    398  }
    399 
    400  // XXX Ideally, we should take same behavior with both <p> container and
    401  //     <div> container.  However, we are still using <br> as default
    402  //     paragraph separator (non-standard) and we've split only <p> container
    403  //     long time.  Therefore, some web apps may depend on this behavior like
    404  //     Gmail.  So, let's use traditional odd behavior only when the default
    405  //     paragraph separator is <br>.  Otherwise, take consistent behavior
    406  //     between <p> container and <div> container.
    407  if ((mDefaultParagraphSeparator == ParagraphSeparator::br &&
    408       editableBlockElement->IsHTMLElement(nsGkAtoms::p)) ||
    409      (mDefaultParagraphSeparator != ParagraphSeparator::br &&
    410       editableBlockElement->IsAnyOfHTMLElements(nsGkAtoms::p,
    411                                                 nsGkAtoms::div))) {
    412    const EditorDOMPoint pointToSplit = GetBetterPointToSplitParagraph(
    413        *editableBlockElement, insertedPaddingBRElement
    414                                   ? EditorDOMPoint(insertedPaddingBRElement)
    415                                   : pointToInsert);
    416    if (ShouldCreateNewParagraph(*editableBlockElement, pointToSplit)) {
    417      MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
    418      // Paragraphs: special rules to look for <br>s
    419      Result<SplitNodeResult, nsresult> splitNodeResult =
    420          SplitParagraphWithTransaction(*editableBlockElement, pointToSplit);
    421      if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
    422        NS_WARNING("HTMLEditor::SplitParagraphWithTransaction() failed");
    423        return splitNodeResult.propagateErr();
    424      }
    425      if (splitNodeResult.inspect().Handled()) {
    426        SplitNodeResult unwrappedSplitNodeResult = splitNodeResult.unwrap();
    427        const RefPtr<Element> rightParagraphElement =
    428            unwrappedSplitNodeResult.DidSplit()
    429                ? unwrappedSplitNodeResult.GetNextContentAs<Element>()
    430                : blockElementToPutCaret.get();
    431        const EditorDOMPoint pointToPutCaret =
    432            unwrappedSplitNodeResult.UnwrapCaretPoint();
    433        nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
    434            pointToPutCaret, rightParagraphElement,
    435            {SuggestCaret::AndIgnoreTrivialError});
    436        if (NS_FAILED(rv)) {
    437          NS_WARNING(
    438              "AutoInsertParagraphHandler::"
    439              "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() "
    440              "failed");
    441          return Err(rv);
    442        }
    443        NS_WARNING_ASSERTION(
    444            rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
    445            "AutoInsertParagraphHandler::"
    446            "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() "
    447            "failed, but ignored");
    448        return EditActionResult::HandledResult();
    449      }
    450      MOZ_ASSERT(!splitNodeResult.inspect().HasCaretPointSuggestion());
    451    }
    452 
    453    // Fall through, if we didn't handle it above.
    454  }
    455 
    456  // If nobody handles this edit action, let's insert new <br> at the selection.
    457  Result<EditActionResult, nsresult> insertBRElementResultOrError =
    458      HandleInsertBRElement(pointToInsert, blockElementToPutCaret);
    459  NS_WARNING_ASSERTION(
    460      insertBRElementResultOrError.isOk(),
    461      "AutoInsertParagraphHandler::HandleInsertBRElement() failed");
    462  return insertBRElementResultOrError;
    463 }
    464 
    465 Result<EditActionResult, nsresult>
    466 HTMLEditor::AutoInsertParagraphHandler::HandleInsertBRElement(
    467    const EditorDOMPoint& aPointToInsert,
    468    const Element* aBlockElementWhichShouldHaveCaret /* = nullptr */) {
    469  Result<CreateElementResult, nsresult> insertBRElementResult =
    470      InsertBRElement(aPointToInsert);
    471  if (MOZ_UNLIKELY(insertBRElementResult.isErr())) {
    472    NS_WARNING("AutoInsertParagraphHandler::InsertBRElement() failed");
    473    return insertBRElementResult.propagateErr();
    474  }
    475  const EditorDOMPoint pointToPutCaret =
    476      insertBRElementResult.unwrap().UnwrapCaretPoint();
    477  if (MOZ_UNLIKELY(!pointToPutCaret.IsSet())) {
    478    NS_WARNING(
    479        "AutoInsertParagraphHandler::InsertBRElement() didn't suggest a "
    480        "point to put caret");
    481    return Err(NS_ERROR_FAILURE);
    482  }
    483  nsresult rv = CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
    484      pointToPutCaret, aBlockElementWhichShouldHaveCaret, {});
    485  if (NS_FAILED(rv)) {
    486    NS_WARNING(
    487        "AutoInsertParagraphHandler::"
    488        "CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret() failed");
    489    return Err(rv);
    490  }
    491  return EditActionResult::HandledResult();
    492 }
    493 
    494 Result<EditActionResult, nsresult>
    495 HTMLEditor::AutoInsertParagraphHandler::HandleInsertLinefeed(
    496    const EditorDOMPoint& aPointToInsert) {
    497  Result<EditorDOMPoint, nsresult> insertLineFeedResult =
    498      AutoInsertLineBreakHandler::InsertLinefeed(mHTMLEditor, aPointToInsert,
    499                                                 mEditingHost);
    500  if (MOZ_UNLIKELY(insertLineFeedResult.isErr())) {
    501    NS_WARNING("AutoInsertLineBreakHandler::InsertLinefeed() failed");
    502    return insertLineFeedResult.propagateErr();
    503  }
    504  nsresult rv = mHTMLEditor.CollapseSelectionTo(insertLineFeedResult.inspect());
    505  if (NS_FAILED(rv)) {
    506    NS_WARNING("EditorBase::CollapseSelectionTo() failed");
    507    return Err(rv);
    508  }
    509  return EditActionResult::HandledResult();
    510 }
    511 
    512 bool HTMLEditor::AutoInsertParagraphHandler::ShouldInsertLineBreakInstead(
    513    const Element* aEditableBlockElement,
    514    const EditorDOMPoint& aCandidatePointToSplit) {
    515  // If there is no block parent in the editing host, i.e., the editing
    516  // host itself is also a non-block element, we should insert a line
    517  // break.
    518  if (!aEditableBlockElement) {
    519    // XXX Chromium checks if the CSS box of the editing host is a block.
    520    return true;
    521  }
    522 
    523  // If the editable block element is not splittable, e.g., it's an
    524  // editing host, and the default paragraph separator is <br> or the
    525  // element cannot contain a <p> element, we should insert a <br>
    526  // element.
    527  if (!HTMLEditUtils::IsSplittableNode(*aEditableBlockElement)) {
    528    return mDefaultParagraphSeparator == ParagraphSeparator::br ||
    529           !HTMLEditUtils::CanElementContainParagraph(*aEditableBlockElement) ||
    530           (aCandidatePointToSplit.IsInContentNode() &&
    531            mHTMLEditor
    532                    .GetPreferredLineBreakType(
    533                        *aCandidatePointToSplit.ContainerAs<nsIContent>(),
    534                        mEditingHost)
    535                    .valueOr(LineBreakType::BRElement) ==
    536                LineBreakType::Linefeed &&
    537            HTMLEditUtils::IsDisplayOutsideInline(mEditingHost));
    538  }
    539 
    540  // If the nearest block parent is a single-line container declared in
    541  // the execCommand spec and not the editing host, we should separate the
    542  // block even if the default paragraph separator is <br> element.
    543  if (HTMLEditUtils::IsSingleLineContainer(*aEditableBlockElement)) {
    544    return false;
    545  }
    546 
    547  // Otherwise, unless there is no block ancestor which can contain <p>
    548  // element, we shouldn't insert a line break here.
    549  for (const Element* editableBlockAncestor = aEditableBlockElement;
    550       editableBlockAncestor;
    551       editableBlockAncestor = HTMLEditUtils::GetAncestorElement(
    552           *editableBlockAncestor,
    553           HTMLEditUtils::ClosestEditableBlockElementOrButtonElement,
    554           BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
    555    if (HTMLEditUtils::CanElementContainParagraph(*editableBlockAncestor)) {
    556      return false;
    557    }
    558  }
    559  return true;
    560 }
    561 
    562 // static
    563 nsresult HTMLEditor::AutoInsertParagraphHandler::
    564    CollapseSelectionToPointOrIntoBlockWhichShouldHaveCaret(
    565        const EditorDOMPoint& aCandidatePointToPutCaret,
    566        const Element* aBlockElementShouldHaveCaret,
    567        const SuggestCaretOptions& aOptions) {
    568  if (!aCandidatePointToPutCaret.IsSet()) {
    569    if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion)) {
    570      return NS_OK;
    571    }
    572    return aOptions.contains(SuggestCaret::AndIgnoreTrivialError)
    573               ? NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR
    574               : NS_ERROR_FAILURE;
    575  }
    576  EditorDOMPoint pointToPutCaret(aCandidatePointToPutCaret);
    577  if (aBlockElementShouldHaveCaret) {
    578    Result<EditorDOMPoint, nsresult> pointToPutCaretOrError =
    579        HTMLEditUtils::ComputePointToPutCaretInElementIfOutside<EditorDOMPoint>(
    580            *aBlockElementShouldHaveCaret, aCandidatePointToPutCaret);
    581    if (MOZ_UNLIKELY(pointToPutCaretOrError.isErr())) {
    582      NS_WARNING(
    583          "HTMLEditUtils::ComputePointToPutCaretInElementIfOutside() "
    584          "failed, but ignored");
    585    } else if (pointToPutCaretOrError.inspect().IsSet()) {
    586      pointToPutCaret = pointToPutCaretOrError.unwrap();
    587    }
    588  }
    589  nsresult rv = mHTMLEditor.CollapseSelectionTo(pointToPutCaret);
    590  if (NS_FAILED(rv) && MOZ_LIKELY(rv != NS_ERROR_EDITOR_DESTROYED) &&
    591      aOptions.contains(SuggestCaret::AndIgnoreTrivialError)) {
    592    rv = NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR;
    593  }
    594  return rv;
    595 }
    596 
    597 Result<CreateElementResult, nsresult>
    598 HTMLEditor::AutoInsertParagraphHandler::InsertBRElement(
    599    const EditorDOMPoint& aPointToBreak) {
    600  MOZ_ASSERT(aPointToBreak.IsInContentNode());
    601 
    602  const bool editingHostIsEmpty = HTMLEditUtils::IsEmptyNode(
    603      mEditingHost, {EmptyCheckOption::TreatNonEditableContentAsInvisible});
    604  const WSRunScanner wsRunScanner({WSRunScanner::Option::OnlyEditableNodes},
    605                                  aPointToBreak);
    606  const WSScanResult backwardScanResult =
    607      wsRunScanner.ScanPreviousVisibleNodeOrBlockBoundaryFrom(aPointToBreak);
    608  if (MOZ_UNLIKELY(backwardScanResult.Failed())) {
    609    NS_WARNING(
    610        "WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom() failed");
    611    return Err(NS_ERROR_FAILURE);
    612  }
    613  const bool brElementIsAfterBlock =
    614      backwardScanResult.ReachedBlockBoundary() ||
    615      // FIXME: This is wrong considering because the inline editing host may
    616      // be surrounded by visible inline content.  However, WSRunScanner is
    617      // not aware of block boundary around it and stopping this change causes
    618      // starting to fail some WPT.  Therefore, we need to keep doing this for
    619      // now.
    620      backwardScanResult.ReachedInlineEditingHostBoundary();
    621  const WSScanResult forwardScanResult =
    622      wsRunScanner.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
    623          aPointToBreak);
    624  if (MOZ_UNLIKELY(forwardScanResult.Failed())) {
    625    NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom() failed");
    626    return Err(NS_ERROR_FAILURE);
    627  }
    628  const bool brElementIsBeforeBlock =
    629      forwardScanResult.ReachedBlockBoundary() ||
    630      // FIXME: See above comment
    631      forwardScanResult.ReachedInlineEditingHostBoundary();
    632 
    633  // First, insert a <br> element.
    634  RefPtr<Element> brElement;
    635  if (mHTMLEditor.IsPlaintextMailComposer()) {
    636    Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
    637        mHTMLEditor.InsertLineBreak(WithTransaction::Yes,
    638                                    LineBreakType::BRElement, aPointToBreak);
    639    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
    640      NS_WARNING(
    641          "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
    642          "LineBreakType::BRElement) failed");
    643      return insertBRElementResultOrError.propagateErr();
    644    }
    645    CreateLineBreakResult insertBRElementResult =
    646        insertBRElementResultOrError.unwrap();
    647    // We'll return with suggesting new caret position and nobody refers
    648    // selection after here.  So we don't need to update selection here.
    649    insertBRElementResult.IgnoreCaretPointSuggestion();
    650    brElement = &insertBRElementResult->BRElementRef();
    651  } else {
    652    EditorDOMPoint pointToBreak(aPointToBreak);
    653    // If the container of the break is a link, we need to split it and
    654    // insert new <br> between the split links.
    655    RefPtr<Element> linkNode =
    656        HTMLEditor::GetLinkElement(pointToBreak.GetContainer());
    657    if (linkNode) {
    658      // FIXME: Normalize surrounding white-spaces before splitting the
    659      // insertion point here.
    660      Result<SplitNodeResult, nsresult> splitLinkNodeResult =
    661          mHTMLEditor.SplitNodeDeepWithTransaction(
    662              *linkNode, pointToBreak,
    663              SplitAtEdges::eDoNotCreateEmptyContainer);
    664      if (MOZ_UNLIKELY(splitLinkNodeResult.isErr())) {
    665        NS_WARNING(
    666            "HTMLEditor::SplitNodeDeepWithTransaction(SplitAtEdges::"
    667            "eDoNotCreateEmptyContainer) failed");
    668        return splitLinkNodeResult.propagateErr();
    669      }
    670      // TODO: Some methods called by
    671      //       WhiteSpaceVisibilityKeeper::InsertLineBreak() use
    672      //       ComputeEditingHost() which depends on selection.  Therefore,
    673      //       we cannot skip updating selection here.
    674      nsresult rv = splitLinkNodeResult.inspect().SuggestCaretPointTo(
    675          mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
    676                        SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
    677      if (NS_FAILED(rv)) {
    678        NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
    679        return Err(rv);
    680      }
    681      pointToBreak =
    682          splitLinkNodeResult.inspect().AtSplitPoint<EditorDOMPoint>();
    683    }
    684    Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
    685        WhiteSpaceVisibilityKeeper::InsertLineBreak(LineBreakType::BRElement,
    686                                                    mHTMLEditor, pointToBreak);
    687    if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
    688      NS_WARNING(
    689          "WhiteSpaceVisibilityKeeper::InsertLineBreak(LineBreakType::"
    690          "BRElement) failed");
    691      return insertBRElementResultOrError.propagateErr();
    692    }
    693    CreateLineBreakResult insertBRElementResult =
    694        insertBRElementResultOrError.unwrap();
    695    // We'll return with suggesting new caret position and nobody refers
    696    // selection after here.  So we don't need to update selection here.
    697    insertBRElementResult.IgnoreCaretPointSuggestion();
    698    brElement = &insertBRElementResult->BRElementRef();
    699  }
    700 
    701  if (MOZ_UNLIKELY(!brElement->GetParentNode())) {
    702    NS_WARNING("Inserted <br> element was removed by the web app");
    703    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    704  }
    705  auto afterBRElement = EditorDOMPoint::After(brElement);
    706 
    707  const auto InsertAdditionalInvisibleLineBreak =
    708      [this, &afterBRElement]()
    709          MOZ_CAN_RUN_SCRIPT -> Result<CreateLineBreakResult, nsresult> {
    710    // Empty last line is invisible if it's immediately before either parent or
    711    // another block's boundary so that we need to put invisible <br> element
    712    // here for making it visible.
    713    Result<CreateLineBreakResult, nsresult>
    714        insertPaddingBRElementResultOrError =
    715            WhiteSpaceVisibilityKeeper::InsertLineBreak(
    716                LineBreakType::BRElement, mHTMLEditor, afterBRElement);
    717    NS_WARNING_ASSERTION(insertPaddingBRElementResultOrError.isOk(),
    718                         "WhiteSpaceVisibilityKeeper::InsertLineBreak("
    719                         "LineBreakType::BRElement) failed");
    720    // afterBRElement points after the first <br> with referring an old child.
    721    // Therefore, we need to update it with new child which is the new invisible
    722    // <br>.
    723    afterBRElement = insertPaddingBRElementResultOrError.inspect()
    724                         .AtLineBreak<EditorDOMPoint>();
    725    return insertPaddingBRElementResultOrError;
    726  };
    727 
    728  if (brElementIsAfterBlock && brElementIsBeforeBlock) {
    729    // We just placed a <br> between block boundaries.  This is the one case
    730    // where we want the selection to be before the br we just placed, as the
    731    // br will be on a new line, rather than at end of prior line.
    732    // XXX brElementIsAfterBlock and brElementIsBeforeBlock were set before
    733    //     modifying the DOM tree.  So, now, the <br> element may not be
    734    //     between blocks.
    735    EditorDOMPoint pointToPutCaret;
    736    if (editingHostIsEmpty) {
    737      Result<CreateLineBreakResult, nsresult>
    738          insertPaddingBRElementResultOrError =
    739              InsertAdditionalInvisibleLineBreak();
    740      if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
    741        return insertPaddingBRElementResultOrError.propagateErr();
    742      }
    743      insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
    744      pointToPutCaret = std::move(afterBRElement);
    745    } else {
    746      pointToPutCaret =
    747          EditorDOMPoint(brElement, InterlinePosition::StartOfNextLine);
    748    }
    749    return CreateElementResult(std::move(brElement),
    750                               std::move(pointToPutCaret));
    751  }
    752 
    753  const WSScanResult forwardScanFromAfterBRElementResult =
    754      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
    755          {WSRunScanner::Option::OnlyEditableNodes}, afterBRElement);
    756  if (MOZ_UNLIKELY(forwardScanFromAfterBRElementResult.Failed())) {
    757    NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundary() failed");
    758    return Err(NS_ERROR_FAILURE);
    759  }
    760  if (forwardScanFromAfterBRElementResult.ReachedBRElement()) {
    761    // The next thing after the break we inserted is another break.  Move the
    762    // second break to be the first break's sibling.  This will prevent them
    763    // from being in different inline nodes, which would break
    764    // SetInterlinePosition().  It will also assure that if the user clicks
    765    // away and then clicks back on their new blank line, they will still get
    766    // the style from the line above.
    767    if (brElement->GetNextSibling() !=
    768        forwardScanFromAfterBRElementResult.BRElementPtr()) {
    769      MOZ_ASSERT(forwardScanFromAfterBRElementResult.BRElementPtr());
    770      Result<MoveNodeResult, nsresult> moveBRElementResult =
    771          mHTMLEditor.MoveNodeWithTransaction(
    772              MOZ_KnownLive(
    773                  *forwardScanFromAfterBRElementResult.BRElementPtr()),
    774              afterBRElement);
    775      if (MOZ_UNLIKELY(moveBRElementResult.isErr())) {
    776        NS_WARNING("HTMLEditor::MoveNodeWithTransaction() failed");
    777        return moveBRElementResult.propagateErr();
    778      }
    779      nsresult rv = moveBRElementResult.inspect().SuggestCaretPointTo(
    780          mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
    781                        SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
    782                        SuggestCaret::AndIgnoreTrivialError});
    783      if (NS_FAILED(rv)) {
    784        NS_WARNING("MoveNodeResult::SuggestCaretPointTo() failed");
    785        return Err(rv);
    786      }
    787      NS_WARNING_ASSERTION(
    788          rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
    789          "MoveNodeResult::SuggestCaretPointTo() failed, but ignored");
    790      // afterBRElement points after the first <br> with referring an old child.
    791      // Therefore, we need to update it with new child which is the new
    792      // invisible <br>.
    793      afterBRElement.Set(forwardScanFromAfterBRElementResult.BRElementPtr());
    794    }
    795  } else if ((forwardScanFromAfterBRElementResult.ReachedBlockBoundary() ||
    796              // FIXME: This is wrong considering because the inline editing
    797              // host may be surrounded by visible inline content.  However,
    798              // WSRunScanner is not aware of block boundary around it and
    799              // stopping this change causes starting to fail some WPT.
    800              // Therefore, we need to keep doing this for now.
    801              forwardScanFromAfterBRElementResult
    802                  .ReachedInlineEditingHostBoundary()) &&
    803             !brElementIsAfterBlock) {
    804    Result<CreateLineBreakResult, nsresult>
    805        insertPaddingBRElementResultOrError =
    806            InsertAdditionalInvisibleLineBreak();
    807    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
    808      return insertPaddingBRElementResultOrError.propagateErr();
    809    }
    810    insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
    811  }
    812 
    813  // We want the caret to stick to whatever is past the break.  This is because
    814  // the break is on the same line we were on, but the next content will be on
    815  // the following line.
    816 
    817  // An exception to this is if the break has a next sibling that is a block
    818  // node.  Then we stick to the left to avoid an uber caret.
    819  nsIContent* nextSiblingOfBRElement = brElement->GetNextSibling();
    820  afterBRElement.SetInterlinePosition(
    821      nextSiblingOfBRElement && HTMLEditUtils::IsBlockElement(
    822                                    *nextSiblingOfBRElement,
    823                                    BlockInlineCheck::UseComputedDisplayStyle)
    824          ? InterlinePosition::EndOfLine
    825          : InterlinePosition::StartOfNextLine);
    826  return CreateElementResult(std::move(brElement), afterBRElement);
    827 }
    828 
    829 Result<CaretPoint, nsresult>
    830 HTMLEditor::AutoInsertParagraphHandler::HandleInMailCiteElement(
    831    Element& aMailCiteElement, const EditorDOMPoint& aPointToSplit) {
    832  MOZ_ASSERT(aPointToSplit.IsSet());
    833  NS_ASSERTION(!HTMLEditUtils::IsEmptyNode(
    834                   aMailCiteElement,
    835                   {EmptyCheckOption::TreatNonEditableContentAsInvisible}),
    836               "The mail-cite element will be deleted, does it expected result "
    837               "for you?");
    838 
    839  auto splitCiteElementResult =
    840      SplitMailCiteElement(aPointToSplit, aMailCiteElement);
    841  if (MOZ_UNLIKELY(splitCiteElementResult.isErr())) {
    842    NS_WARNING("Failed to split a mail-cite element");
    843    return splitCiteElementResult.propagateErr();
    844  }
    845  SplitNodeResult unwrappedSplitCiteElementResult =
    846      splitCiteElementResult.unwrap();
    847  // When adding caret suggestion to SplitNodeResult, here didn't change
    848  // selection so that just ignore it.
    849  unwrappedSplitCiteElementResult.IgnoreCaretPointSuggestion();
    850 
    851  // Add an invisible <br> to the end of left cite node if it was a <span> of
    852  // style="display: block".  This is important, since when serializing the cite
    853  // to plain text, the span which caused the visual break is discarded.  So the
    854  // added <br> will guarantee that the serializer will insert a break where the
    855  // user saw one.
    856  // FYI: unwrappedSplitCiteElementResult grabs the previous node and the next
    857  //      node with nsCOMPtr or EditorDOMPoint.  So, it's safe to access
    858  //      leftCiteElement and rightCiteElement even after changing the DOM tree
    859  //      and/or selection even though it's raw pointer.
    860  auto* const leftCiteElement =
    861      unwrappedSplitCiteElementResult.GetPreviousContentAs<Element>();
    862  auto* const rightCiteElement =
    863      unwrappedSplitCiteElementResult.GetNextContentAs<Element>();
    864  if (leftCiteElement && leftCiteElement->IsHTMLElement(nsGkAtoms::span) &&
    865      // XXX Oh, this depends on layout information of new element, and it's
    866      //     created by the hacky flush in DoSplitNode().  So we need to
    867      //     redesign around this for bug 1710784.
    868      leftCiteElement->GetPrimaryFrame() &&
    869      leftCiteElement->GetPrimaryFrame()->IsBlockFrameOrSubclass()) {
    870    nsIContent* lastChild = leftCiteElement->GetLastChild();
    871    if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
    872      Result<CreateLineBreakResult, nsresult>
    873          insertPaddingBRElementResultOrError = mHTMLEditor.InsertLineBreak(
    874              WithTransaction::Yes, LineBreakType::BRElement,
    875              EditorDOMPoint::AtEndOf(*leftCiteElement));
    876      if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
    877        NS_WARNING(
    878            "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
    879            "LineBreakType::BRElement) failed");
    880        return insertPaddingBRElementResultOrError.propagateErr();
    881      }
    882      CreateLineBreakResult insertPaddingBRElementResult =
    883          insertPaddingBRElementResultOrError.unwrap();
    884      MOZ_ASSERT(insertPaddingBRElementResult.Handled());
    885      // We don't need to update selection here because we'll do another
    886      // InsertLineBreak call soon.
    887      insertPaddingBRElementResult.IgnoreCaretPointSuggestion();
    888    }
    889  }
    890 
    891  // In most cases, <br> should be inserted after current cite.  However, if
    892  // left cite hasn't been created because the split point was start of the
    893  // cite node, <br> should be inserted before the current cite.
    894  Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
    895      mHTMLEditor.InsertLineBreak(
    896          WithTransaction::Yes, LineBreakType::BRElement,
    897          unwrappedSplitCiteElementResult.AtSplitPoint<EditorDOMPoint>());
    898  if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
    899    NS_WARNING(
    900        "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
    901        "LineBreakType::BRElement) failed");
    902    return Err(insertBRElementResultOrError.unwrapErr());
    903  }
    904  CreateLineBreakResult insertBRElementResult =
    905      insertBRElementResultOrError.unwrap();
    906  MOZ_ASSERT(insertBRElementResult.Handled());
    907  // We'll return with suggesting caret position.  Therefore, we don't need
    908  // to update selection here.
    909  insertBRElementResult.IgnoreCaretPointSuggestion();
    910  // if aMailCiteElement wasn't a block, we might also want another break before
    911  // it. We need to examine the content both before the br we just added and
    912  // also just after it.  If we don't have another br or block boundary
    913  // adjacent, then we will need a 2nd br added to achieve blank line that user
    914  // expects.
    915  {
    916    nsresult rvOfInsertPaddingBRElement =
    917        MaybeInsertPaddingBRElementToInlineMailCiteElement(
    918            insertBRElementResult.AtLineBreak<EditorDOMPoint>(),
    919            aMailCiteElement);
    920    if (NS_FAILED(rvOfInsertPaddingBRElement)) {
    921      NS_WARNING(
    922          "Failed to insert additional <br> element before the inline right "
    923          "mail-cite element");
    924      return Err(rvOfInsertPaddingBRElement);
    925    }
    926  }
    927 
    928  if (leftCiteElement &&
    929      HTMLEditUtils::IsEmptyNode(
    930          *leftCiteElement,
    931          {EmptyCheckOption::TreatNonEditableContentAsInvisible})) {
    932    // MOZ_KnownLive(leftCiteElement) because it's grabbed by
    933    // unwrappedSplitCiteElementResult.
    934    nsresult rv =
    935        mHTMLEditor.DeleteNodeWithTransaction(MOZ_KnownLive(*leftCiteElement));
    936    if (NS_FAILED(rv)) {
    937      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
    938      return Err(rv);
    939    }
    940  }
    941 
    942  if (rightCiteElement &&
    943      HTMLEditUtils::IsEmptyNode(
    944          *rightCiteElement,
    945          {EmptyCheckOption::TreatNonEditableContentAsInvisible})) {
    946    // MOZ_KnownLive(rightCiteElement) because it's grabbed by
    947    // unwrappedSplitCiteElementResult.
    948    nsresult rv =
    949        mHTMLEditor.DeleteNodeWithTransaction(MOZ_KnownLive(*rightCiteElement));
    950    if (NS_FAILED(rv)) {
    951      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
    952      return Err(rv);
    953    }
    954  }
    955 
    956  if (NS_WARN_IF(!insertBRElementResult.LineBreakIsInComposedDoc())) {
    957    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
    958  }
    959  auto pointToPutCaret = insertBRElementResult.AtLineBreak<EditorDOMPoint>();
    960  pointToPutCaret.SetInterlinePosition(InterlinePosition::StartOfNextLine);
    961  return CaretPoint(std::move(pointToPutCaret));
    962 }
    963 
    964 Result<SplitNodeResult, nsresult>
    965 HTMLEditor::AutoInsertParagraphHandler::SplitMailCiteElement(
    966    const EditorDOMPoint& aPointToSplit, Element& aMailCiteElement) {
    967  EditorDOMPoint pointToSplit(aPointToSplit);
    968 
    969  // If our selection is just before a break, nudge it to be just after
    970  // it. This does two things for us.  It saves us the trouble of having
    971  // to add a break here ourselves to preserve the "blockness" of the
    972  // inline span mailquote (in the inline case), and : it means the break
    973  // won't end up making an empty line that happens to be inside a
    974  // mailquote (in either inline or block case). The latter can confuse a
    975  // user if they click there and start typing, because being in the
    976  // mailquote may affect wrapping behavior, or font color, etc.
    977  const WSScanResult forwardScanFromPointToSplitResult =
    978      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
    979          {WSRunScanner::Option::OnlyEditableNodes,
    980           WSRunScanner::Option::ReferHTMLDefaultStyle},
    981          pointToSplit);
    982  if (forwardScanFromPointToSplitResult.Failed()) {
    983    return Err(NS_ERROR_FAILURE);
    984  }
    985  // If selection start point is before a break and it's inside the
    986  // mailquote, let's split it after the visible node.
    987  if (forwardScanFromPointToSplitResult.ReachedBRElement() &&
    988      forwardScanFromPointToSplitResult.BRElementPtr() != &aMailCiteElement &&
    989      aMailCiteElement.Contains(
    990          forwardScanFromPointToSplitResult.BRElementPtr())) {
    991    pointToSplit = forwardScanFromPointToSplitResult
    992                       .PointAfterReachedContent<EditorDOMPoint>();
    993  }
    994 
    995  if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
    996    return Err(NS_ERROR_FAILURE);
    997  }
    998 
    999  Result<EditorDOMPoint, nsresult> pointToSplitOrError =
   1000      WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
   1001          mHTMLEditor, pointToSplit,
   1002          {WhiteSpaceVisibilityKeeper::NormalizeOption::
   1003               StopIfPrecedingWhiteSpacesEndsWithNBP,
   1004           WhiteSpaceVisibilityKeeper::NormalizeOption::
   1005               StopIfFollowingWhiteSpacesStartsWithNBSP});
   1006  if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) {
   1007    NS_WARNING(
   1008        "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() "
   1009        "failed");
   1010    return pointToSplitOrError.propagateErr();
   1011  }
   1012  pointToSplit = pointToSplitOrError.unwrap();
   1013  if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
   1014    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1015  }
   1016 
   1017  Result<SplitNodeResult, nsresult> splitResult =
   1018      mHTMLEditor.SplitNodeDeepWithTransaction(
   1019          aMailCiteElement, pointToSplit,
   1020          SplitAtEdges::eDoNotCreateEmptyContainer);
   1021  if (MOZ_UNLIKELY(splitResult.isErr())) {
   1022    NS_WARNING(
   1023        "HTMLEditor::SplitNodeDeepWithTransaction(aMailCiteElement, "
   1024        "SplitAtEdges::eDoNotCreateEmptyContainer) failed");
   1025    return splitResult;
   1026  }
   1027  // FIXME: We should make the caller handle `Selection`.
   1028  nsresult rv = splitResult.inspect().SuggestCaretPointTo(
   1029      mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
   1030                    SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
   1031  if (NS_FAILED(rv)) {
   1032    NS_WARNING("SplitNodeResult::SuggestCaretPointTo() failed");
   1033    return Err(rv);
   1034  }
   1035  return splitResult;
   1036 }
   1037 
   1038 nsresult HTMLEditor::AutoInsertParagraphHandler::
   1039    MaybeInsertPaddingBRElementToInlineMailCiteElement(
   1040        const EditorDOMPoint& aPointToInsertBRElement,
   1041        Element& aMailCiteElement) {
   1042  if (!HTMLEditUtils::IsInlineContent(aMailCiteElement,
   1043                                      BlockInlineCheck::UseHTMLDefaultStyle)) {
   1044    return NS_SUCCESS_DOM_NO_OPERATION;
   1045  }
   1046  // XXX Cannot we replace this complicated check with just a call of
   1047  //     HTMLEditUtils::IsVisibleBRElement with
   1048  //     resultOfInsertingBRElement.inspect()?
   1049  const WSScanResult backwardScanFromPointToCreateNewBRElementResult =
   1050      WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
   1051          {WSRunScanner::Option::OnlyEditableNodes,
   1052           WSRunScanner::Option::ReferHTMLDefaultStyle},
   1053          aPointToInsertBRElement);
   1054  if (MOZ_UNLIKELY(backwardScanFromPointToCreateNewBRElementResult.Failed())) {
   1055    NS_WARNING(
   1056        "WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary() "
   1057        "failed");
   1058    return NS_ERROR_FAILURE;
   1059  }
   1060  if (!backwardScanFromPointToCreateNewBRElementResult
   1061           .InVisibleOrCollapsibleCharacters() &&
   1062      !backwardScanFromPointToCreateNewBRElementResult
   1063           .ReachedSpecialContent()) {
   1064    return NS_SUCCESS_DOM_NO_OPERATION;
   1065  }
   1066  const WSScanResult forwardScanFromPointAfterNewBRElementResult =
   1067      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1068          {WSRunScanner::Option::OnlyEditableNodes,
   1069           WSRunScanner::Option::ReferHTMLDefaultStyle},
   1070          EditorRawDOMPoint::After(aPointToInsertBRElement));
   1071  if (MOZ_UNLIKELY(forwardScanFromPointAfterNewBRElementResult.Failed())) {
   1072    NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundary() failed");
   1073    return NS_ERROR_FAILURE;
   1074  }
   1075  if (!forwardScanFromPointAfterNewBRElementResult
   1076           .InVisibleOrCollapsibleCharacters() &&
   1077      !forwardScanFromPointAfterNewBRElementResult.ReachedSpecialContent() &&
   1078      // In case we're at the very end.
   1079      !forwardScanFromPointAfterNewBRElementResult
   1080           .ReachedCurrentBlockBoundary()) {
   1081    return NS_SUCCESS_DOM_NO_OPERATION;
   1082  }
   1083  Result<CreateLineBreakResult, nsresult> insertAnotherBRElementResultOrError =
   1084      mHTMLEditor.InsertLineBreak(WithTransaction::Yes,
   1085                                  LineBreakType::BRElement,
   1086                                  aPointToInsertBRElement);
   1087  if (MOZ_UNLIKELY(insertAnotherBRElementResultOrError.isErr())) {
   1088    NS_WARNING(
   1089        "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   1090        "LineBreakType::BRElement) failed");
   1091    return insertAnotherBRElementResultOrError.unwrapErr();
   1092  }
   1093  CreateLineBreakResult insertAnotherBRElementResult =
   1094      insertAnotherBRElementResultOrError.unwrap();
   1095  MOZ_ASSERT(insertAnotherBRElementResult.Handled());
   1096  insertAnotherBRElementResult.IgnoreCaretPointSuggestion();
   1097  return NS_OK;
   1098 }
   1099 
   1100 Result<InsertParagraphResult, nsresult>
   1101 HTMLEditor::AutoInsertParagraphHandler::HandleInHeadingElement(
   1102    Element& aHeadingElement, const EditorDOMPoint& aPointToSplit) {
   1103  // Don't preserve empty link at the end of the left heading element nor the
   1104  // start of the right one.
   1105  const EditorDOMPoint pointToSplit =
   1106      GetBetterPointToSplitParagraph(aHeadingElement, aPointToSplit);
   1107  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
   1108 
   1109  // If the split point is end of the heading element, we should not touch the
   1110  // heading element and insert a default paragraph next to the heading element.
   1111  if (SplitPointIsEndOfSplittingBlock(aHeadingElement, pointToSplit,
   1112                                      IgnoreBlockBoundaries::Yes)) {
   1113    Result<InsertParagraphResult, nsresult>
   1114        handleAtEndOfHeadingElementResultOrError =
   1115            HandleAtEndOfHeadingElement(aHeadingElement);
   1116    NS_WARNING_ASSERTION(
   1117        handleAtEndOfHeadingElementResultOrError.isOk(),
   1118        "AutoInsertParagraphHandler::HandleAtEndOfHeadingElement() failed");
   1119    return handleAtEndOfHeadingElementResultOrError;
   1120  }
   1121 
   1122  Result<SplitNodeResult, nsresult> splitHeadingResultOrError =
   1123      SplitParagraphWithTransaction(aHeadingElement, pointToSplit);
   1124  if (MOZ_UNLIKELY(splitHeadingResultOrError.isErr())) {
   1125    NS_WARNING(
   1126        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() failed");
   1127    return splitHeadingResultOrError.propagateErr();
   1128  }
   1129  SplitNodeResult splitHeadingResult = splitHeadingResultOrError.unwrap();
   1130  splitHeadingResult.IgnoreCaretPointSuggestion();
   1131  if (MOZ_UNLIKELY(!splitHeadingResult.DidSplit())) {
   1132    NS_WARNING(
   1133        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() didn't "
   1134        "split aHeadingElement");
   1135    return Err(NS_ERROR_FAILURE);
   1136  }
   1137 
   1138  // Put caret at start of the right head element if it's not empty.
   1139  auto* const rightHeadingElement =
   1140      splitHeadingResult.GetNextContentAs<Element>();
   1141  MOZ_ASSERT(rightHeadingElement,
   1142             "SplitNodeResult::GetNextContent() should return something if "
   1143             "DidSplit() returns true");
   1144  return InsertParagraphResult(*rightHeadingElement,
   1145                               splitHeadingResult.UnwrapCaretPoint());
   1146 }
   1147 
   1148 Result<InsertParagraphResult, nsresult>
   1149 HTMLEditor::AutoInsertParagraphHandler::HandleAtEndOfHeadingElement(
   1150    Element& aHeadingElement) {
   1151  // XXX This makes HTMLEditor instance stateful.  So, we should move this out
   1152  // from AutoInsertParagraphHandler with adding a method which HTMLEditor can
   1153  // consider to do this.
   1154  mHTMLEditor.TopLevelEditSubActionDataRef().mCachedPendingStyles->Clear();
   1155  mHTMLEditor.mPendingStylesToApplyToNewContent->ClearAllStyles();
   1156 
   1157  // Create a paragraph if the right heading element is not followed by an
   1158  // editable <br> element.
   1159  nsStaticAtom& newParagraphTagName =
   1160      &mDefaultParagraphSeparatorTagName == nsGkAtoms::br
   1161          ? *nsGkAtoms::p
   1162          : mDefaultParagraphSeparatorTagName;
   1163  // We want a wrapper element even if we separate with a <br>.
   1164  // FIXME: Chrome does not preserve the style coming from the heading element.
   1165  // However, Chrome preserves the inline ancestors at the split point.
   1166  // Perhaps, we should follow them.
   1167  // MOZ_KnownLive(newParagraphTagName) because it's available until shutdown.
   1168  Result<CreateElementResult, nsresult> createNewParagraphElementResult =
   1169      mHTMLEditor.CreateAndInsertElement(WithTransaction::Yes,
   1170                                         MOZ_KnownLive(newParagraphTagName),
   1171                                         EditorDOMPoint::After(aHeadingElement),
   1172                                         HTMLEditor::InsertNewBRElement);
   1173  if (MOZ_UNLIKELY(createNewParagraphElementResult.isErr())) {
   1174    NS_WARNING(
   1175        "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
   1176    return createNewParagraphElementResult.propagateErr();
   1177  }
   1178  CreateElementResult unwrappedCreateNewParagraphElementResult =
   1179      createNewParagraphElementResult.unwrap();
   1180  // Put caret at the <br> element in the following paragraph.
   1181  unwrappedCreateNewParagraphElementResult.IgnoreCaretPointSuggestion();
   1182  MOZ_ASSERT(unwrappedCreateNewParagraphElementResult.GetNewNode());
   1183  EditorDOMPoint pointToPutCaret(
   1184      unwrappedCreateNewParagraphElementResult.GetNewNode(), 0u);
   1185  return InsertParagraphResult(
   1186      unwrappedCreateNewParagraphElementResult.UnwrapNewNode(),
   1187      std::move(pointToPutCaret));
   1188 }
   1189 
   1190 // static
   1191 bool HTMLEditor::AutoInsertParagraphHandler::
   1192    IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
   1193        const dom::HTMLBRElement* aBRElement) {
   1194  return !aBRElement || HTMLEditUtils::IsInvisibleBRElement(*aBRElement) ||
   1195         EditorUtils::IsPaddingBRElementForEmptyLastLine(*aBRElement);
   1196 }
   1197 
   1198 bool HTMLEditor::AutoInsertParagraphHandler::ShouldCreateNewParagraph(
   1199    Element& aParentDivOrP, const EditorDOMPoint& aPointToSplit) const {
   1200  MOZ_ASSERT(aPointToSplit.IsInContentNodeAndValidInComposedDoc());
   1201 
   1202  if (MOZ_LIKELY(mHTMLEditor.GetReturnInParagraphCreatesNewParagraph())) {
   1203    // We should always create a new paragraph by default.
   1204    return true;
   1205  }
   1206  if (aPointToSplit.GetContainer() == &aParentDivOrP) {
   1207    // We are trying to split only the current paragraph, let's do it.
   1208    return true;
   1209  }
   1210  if (aPointToSplit.IsInTextNode()) {
   1211    if (aPointToSplit.IsStartOfContainer()) {
   1212      // If we're splitting the paragraph at start of a `Text` and it does
   1213      // not follow a <br> or follows an invisible <br>, we should not create a
   1214      // new paragraph.
   1215      // XXX It seems that here assumes that the paragraph has only this `Text`.
   1216      const auto* const precedingBRElement =
   1217          HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousSibling(
   1218              *aPointToSplit.ContainerAs<Text>(),
   1219              {WalkTreeOption::IgnoreNonEditableNode}));
   1220      return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
   1221          precedingBRElement);
   1222    }
   1223    if (aPointToSplit.IsEndOfContainer()) {
   1224      // If we're splitting the paragraph at end of a `Text` and it's not
   1225      // followed by a <br> or is followed by an invisible <br>, we should not
   1226      // create a new paragraph.
   1227      // XXX It seems that here assumes that the paragraph has only this `Text`.
   1228      const auto* const followingBRElement =
   1229          HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextSibling(
   1230              *aPointToSplit.ContainerAs<Text>(),
   1231              {WalkTreeOption::IgnoreNonEditableNode}));
   1232      return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
   1233          followingBRElement);
   1234    }
   1235    // If we're splitting the paragraph at middle of a `Text`, we should create
   1236    // a new paragraph.
   1237    return true;
   1238  }
   1239 
   1240  // If we're splitting in a child element of the paragraph and it does not
   1241  // follow a <br> or follows an invisible <br>, maybe we should not create a
   1242  // new paragraph.
   1243  // XXX Why? We probably need to do this if we're splitting in an inline
   1244  //     element which and whose parents provide some styles, we should put
   1245  //     the <br> element for making a placeholder in the left paragraph for
   1246  //     moving to the caret, but I think that this could be handled in fewer
   1247  //     cases than this.
   1248  const auto* const precedingBRElement =
   1249      HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousContent(
   1250          aPointToSplit, {WalkTreeOption::IgnoreNonEditableNode},
   1251          BlockInlineCheck::Unused, &mEditingHost));
   1252  if (!IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
   1253          precedingBRElement)) {
   1254    return true;
   1255  }
   1256  // If we're splitting in a child element of the paragraph and it's not
   1257  // followed by a <br> or followed by an invisible <br>, we should not create a
   1258  // new paragraph.
   1259  const auto* followingBRElement =
   1260      HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextContent(
   1261          aPointToSplit, {WalkTreeOption::IgnoreNonEditableNode},
   1262          BlockInlineCheck::Unused, &mEditingHost));
   1263  return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
   1264      followingBRElement);
   1265 }
   1266 
   1267 // static
   1268 EditorDOMPoint
   1269 HTMLEditor::AutoInsertParagraphHandler::GetBetterPointToSplitParagraph(
   1270    const Element& aBlockElementToSplit,
   1271    const EditorDOMPoint& aCandidatePointToSplit) {
   1272  EditorDOMPoint pointToSplit = [&]() MOZ_NEVER_INLINE_DEBUG {
   1273    // We shouldn't create new anchor element which has non-empty href unless
   1274    // splitting middle of it because we assume that users don't want to create
   1275    // *same* anchor element across two or more paragraphs in most cases.
   1276    // So, adjust selection start if it's edge of anchor element(s).
   1277    {
   1278      const WSScanResult prevVisibleThing =
   1279          WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
   1280              {}, aCandidatePointToSplit, &aBlockElementToSplit);
   1281      if (prevVisibleThing.GetContent() &&
   1282          // Only if the previous thing is not in the same container.
   1283          prevVisibleThing.GetContent() !=
   1284              aCandidatePointToSplit.GetContainer() &&
   1285          // Only if the previous thing is a preceding node of closest inclusive
   1286          // ancestor element at the split point.
   1287          !prevVisibleThing.GetContent()->IsInclusiveDescendantOf(
   1288              aCandidatePointToSplit.GetContainerOrContainerParentElement())) {
   1289        EditorRawDOMPoint candidatePointToSplit =
   1290            aCandidatePointToSplit.To<EditorRawDOMPoint>();
   1291        const Element* const commonAncestor =
   1292            Element::FromNode(nsContentUtils::GetClosestCommonInclusiveAncestor(
   1293                candidatePointToSplit.GetContainerOrContainerParentElement(),
   1294                prevVisibleThing.GetContent()));
   1295        MOZ_ASSERT(commonAncestor);
   1296        for (const Element* container =
   1297                 candidatePointToSplit.GetContainerOrContainerParentElement();
   1298             container && container != commonAncestor;
   1299             container = container->GetParentElement()) {
   1300          if (!HTMLEditUtils::IsHyperlinkElement(*container)) {
   1301            continue;
   1302          }
   1303          // Found link should be only in right node.  So, we shouldn't split
   1304          // it.
   1305          candidatePointToSplit.Set(container);
   1306          // Even if we found an anchor element, don't break because DOM API
   1307          // allows to nest anchor elements.
   1308        }
   1309        return candidatePointToSplit.To<EditorDOMPoint>();
   1310      }
   1311    }
   1312    WSScanResult nextVisibleThing =
   1313        WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1314            {}, aCandidatePointToSplit, &aBlockElementToSplit);
   1315    if (nextVisibleThing.ReachedInvisibleBRElement()) {
   1316      nextVisibleThing =
   1317          WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1318              {},
   1319              nextVisibleThing.PointAfterReachedContent<EditorRawDOMPoint>(),
   1320              &aBlockElementToSplit);
   1321    }
   1322    if (nextVisibleThing.GetContent() &&
   1323        // Only if the next thing is not in the same container.
   1324        nextVisibleThing.GetContent() !=
   1325            aCandidatePointToSplit.GetContainer() &&
   1326        // Only if the next thing is a preceding node of closest inclusive
   1327        // ancestor element at the split point.
   1328        !nextVisibleThing.GetContent()->IsInclusiveDescendantOf(
   1329            aCandidatePointToSplit.GetContainerOrContainerParentElement())) {
   1330      EditorRawDOMPoint candidatePointToSplit =
   1331          aCandidatePointToSplit.To<EditorRawDOMPoint>();
   1332      const Element* const commonAncestor =
   1333          Element::FromNode(nsContentUtils::GetClosestCommonInclusiveAncestor(
   1334              candidatePointToSplit.GetContainerOrContainerParentElement(),
   1335              nextVisibleThing.GetContent()));
   1336      MOZ_ASSERT(commonAncestor);
   1337      for (const Element* container =
   1338               candidatePointToSplit.GetContainerOrContainerParentElement();
   1339           container && container != commonAncestor;
   1340           container = container->GetParentElement()) {
   1341        if (!HTMLEditUtils::IsHyperlinkElement(*container)) {
   1342          continue;
   1343        }
   1344        // Found link should be only in left node.  So, we shouldn't split it.
   1345        candidatePointToSplit.SetAfter(container);
   1346        // Even if we found an anchor element, don't break because DOM API
   1347        // allows to nest anchor elements.
   1348      }
   1349      return candidatePointToSplit.To<EditorDOMPoint>();
   1350    }
   1351 
   1352    // Okay, split the ancestors as-is.
   1353    return aCandidatePointToSplit;
   1354  }();
   1355 
   1356  // If the candidate split point is not in a splittable node, let's move the
   1357  // point after the parent.
   1358  for (const nsIContent* container = pointToSplit.ContainerAs<nsIContent>();
   1359       container && container != &aBlockElementToSplit &&
   1360       !HTMLEditUtils::IsSplittableNode(*container);
   1361       container = container->GetParent()) {
   1362    pointToSplit = pointToSplit.ParentPoint();
   1363  }
   1364  return pointToSplit;
   1365 }
   1366 
   1367 Result<EditorDOMPoint, nsresult> HTMLEditor::AutoInsertParagraphHandler::
   1368    EnsureNoInvisibleLineBreakBeforePointToSplit(
   1369        const Element& aBlockElementToSplit,
   1370        const EditorDOMPoint& aPointToSplit) {
   1371  const WSScanResult nextVisibleThing =
   1372      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1373          {}, aPointToSplit, &aBlockElementToSplit);
   1374  if (!nextVisibleThing.ReachedBlockBoundary()) {
   1375    return aPointToSplit;
   1376  }
   1377  const WSScanResult prevVisibleThing =
   1378      WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
   1379          {}, aPointToSplit, &aBlockElementToSplit);
   1380  Maybe<EditorLineBreak> precedingInvisibleLineBreak;
   1381  if (prevVisibleThing.ReachedBRElement()) {
   1382    precedingInvisibleLineBreak.emplace(*prevVisibleThing.BRElementPtr());
   1383  } else if (prevVisibleThing.ReachedPreformattedLineBreak()) {
   1384    precedingInvisibleLineBreak.emplace(*prevVisibleThing.TextPtr(),
   1385                                        prevVisibleThing.Offset_Deprecated());
   1386  } else {
   1387    return aPointToSplit;
   1388  }
   1389  EditorDOMPoint pointToSplit = aPointToSplit;
   1390  {
   1391    // FIXME: Once bug 1951041 is fixed in the layout level, we don't need to
   1392    // treat collapsible white-spaces before invisible <br> elements here.
   1393    AutoTrackDOMPoint trackPointToSplit(mHTMLEditor.RangeUpdaterRef(),
   1394                                        &pointToSplit);
   1395    Result<EditorDOMPoint, nsresult>
   1396        normalizePrecedingWhiteSpacesResultOrError =
   1397            WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesBefore(
   1398                mHTMLEditor, precedingInvisibleLineBreak->To<EditorDOMPoint>(),
   1399                {});
   1400    if (MOZ_UNLIKELY(normalizePrecedingWhiteSpacesResultOrError.isErr())) {
   1401      NS_WARNING(
   1402          "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesBefore() failed");
   1403      return normalizePrecedingWhiteSpacesResultOrError.propagateErr();
   1404    }
   1405  }
   1406  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
   1407      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1408          &aBlockElementToSplit))) {
   1409    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1410  }
   1411  {
   1412    AutoTrackDOMPoint trackPointToSplit(mHTMLEditor.RangeUpdaterRef(),
   1413                                        &pointToSplit);
   1414    Result<EditorDOMPoint, nsresult> deleteInvisibleLineBreakResult =
   1415        mHTMLEditor.DeleteLineBreakWithTransaction(*precedingInvisibleLineBreak,
   1416                                                   nsIEditor::eNoStrip,
   1417                                                   aBlockElementToSplit);
   1418    if (MOZ_UNLIKELY(deleteInvisibleLineBreakResult.isErr())) {
   1419      NS_WARNING("HTMLEditor::DeleteLineBreakWithTransaction() failed");
   1420      return deleteInvisibleLineBreakResult.propagateErr();
   1421    }
   1422  }
   1423  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
   1424      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1425          &aBlockElementToSplit))) {
   1426    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1427  }
   1428  return pointToSplit;
   1429 }
   1430 
   1431 Result<EditorDOMPoint, nsresult> HTMLEditor::AutoInsertParagraphHandler::
   1432    MaybeInsertFollowingBRElementToPreserveRightBlock(
   1433        const Element& aBlockElementToSplit,
   1434        const EditorDOMPoint& aPointToSplit) {
   1435  MOZ_ASSERT(HTMLEditUtils::IsSplittableNode(aBlockElementToSplit));
   1436  MOZ_ASSERT(aPointToSplit.ContainerAs<nsIContent>()->IsInclusiveDescendantOf(
   1437      &aBlockElementToSplit));
   1438 
   1439  const Element* const closestContainerElement =
   1440      HTMLEditUtils::GetInclusiveAncestorElement(
   1441          *aPointToSplit.ContainerAs<nsIContent>(),
   1442          HTMLEditUtils::ClosestContainerElementOrVoidAncestorLimiter,
   1443          BlockInlineCheck::UseComputedDisplayOutsideStyle,
   1444          &aBlockElementToSplit);
   1445  MOZ_ASSERT(closestContainerElement);
   1446  MOZ_ASSERT(HTMLEditUtils::IsSplittableNode(*closestContainerElement));
   1447 
   1448  // If we're at end of the paragraph and there are some inline container
   1449  // elements, we want to preserve the inline containers to preserve their
   1450  // styles.
   1451  Maybe<EditorLineBreak> unnecessaryLineBreak;
   1452  const EditorDOMPoint pointToInsertFollowingBRElement =
   1453      [&]() MOZ_NEVER_INLINE_DEBUG {
   1454        const WSScanResult nextVisibleThing =
   1455            WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1456                {}, aPointToSplit, &aBlockElementToSplit);
   1457        if (nextVisibleThing.ReachedBRElement() ||
   1458            nextVisibleThing.ReachedPreformattedLineBreak()) {
   1459          // If it's followed by a line break in the closest ancestor container
   1460          // element, we can use it.
   1461          if ((nextVisibleThing.ReachedBRElement() &&
   1462               nextVisibleThing.BRElementPtr()->GetParentNode() ==
   1463                   closestContainerElement) ||
   1464              (nextVisibleThing.ReachedPreformattedLineBreak() &&
   1465               nextVisibleThing.TextPtr()->GetParentNode() ==
   1466                   closestContainerElement)) {
   1467            return EditorDOMPoint();
   1468          }
   1469          const WSScanResult nextVisibleThingAfterLineBreak =
   1470              WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1471                  {},
   1472                  nextVisibleThing
   1473                      .PointAfterReachedContent<EditorRawDOMPoint>(),
   1474                  &aBlockElementToSplit);
   1475          // If the line break is visible, we don't need to insert a padding
   1476          // <br> element for the right paragraph because it'll have some
   1477          // visible content.
   1478          if (!nextVisibleThingAfterLineBreak.ReachedCurrentBlockBoundary()) {
   1479            return EditorDOMPoint();
   1480          }
   1481        }
   1482        // If it's not directly followed by current block boundary, we don't
   1483        // need to insert a padding <br> element for the right paragraph because
   1484        // it'll have some visible content.
   1485        else if (!nextVisibleThing.ReachedCurrentBlockBoundary()) {
   1486          return EditorDOMPoint();
   1487        }
   1488        // We want to insert a padding <br> into the closest ancestor container
   1489        // element to preserve the style provided by it.
   1490        EditorDOMPoint candidatePoint = aPointToSplit;
   1491        for (; candidatePoint.GetContainer() != closestContainerElement;
   1492             candidatePoint = candidatePoint.AfterContainer()) {
   1493          MOZ_ASSERT(candidatePoint.GetContainer() != &aBlockElementToSplit);
   1494        }
   1495        // If we reached invisible line break which is not in the closest
   1496        // container element, we don't want it anymore once we put invisible
   1497        // <br> element into the closest container element.
   1498        if (nextVisibleThing.ReachedBRElement()) {
   1499          unnecessaryLineBreak.emplace(*nextVisibleThing.BRElementPtr());
   1500        } else if (nextVisibleThing.ReachedPreformattedLineBreak()) {
   1501          unnecessaryLineBreak.emplace(*nextVisibleThing.TextPtr(),
   1502                                       nextVisibleThing.Offset_Deprecated());
   1503        }
   1504        return candidatePoint;
   1505      }();
   1506 
   1507  if (unnecessaryLineBreak) {
   1508    Result<EditorDOMPoint, nsresult> deleteLineBreakResultOrError =
   1509        mHTMLEditor.DeleteLineBreakWithTransaction(
   1510            *unnecessaryLineBreak, nsIEditor::eNoStrip, aBlockElementToSplit);
   1511    if (MOZ_UNLIKELY(deleteLineBreakResultOrError.isErr())) {
   1512      NS_WARNING("HTMLEditor::DeleteLineBreakWithTransaction() failed");
   1513      return deleteLineBreakResultOrError.propagateErr();
   1514    }
   1515    if (NS_WARN_IF(!aPointToSplit.IsSetAndValidInComposedDoc())) {
   1516      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1517    }
   1518    if (pointToInsertFollowingBRElement.IsSet() &&
   1519        (NS_WARN_IF(!pointToInsertFollowingBRElement
   1520                         .IsInContentNodeAndValidInComposedDoc()) ||
   1521         NS_WARN_IF(!pointToInsertFollowingBRElement.GetContainer()
   1522                         ->IsInclusiveDescendantOf(&aBlockElementToSplit)))) {
   1523      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1524    }
   1525  }
   1526  EditorDOMPoint pointToSplit(aPointToSplit);
   1527  if (pointToInsertFollowingBRElement.IsSet()) {
   1528    Maybe<AutoTrackDOMPoint> trackPointToSplit;
   1529    if (pointToSplit.GetContainer() ==
   1530        pointToInsertFollowingBRElement.GetContainer()) {
   1531      trackPointToSplit.emplace(mHTMLEditor.RangeUpdaterRef(), &pointToSplit);
   1532    }
   1533    Result<CreateElementResult, nsresult> insertPaddingBRElementResultOrError =
   1534        mHTMLEditor.InsertBRElement(
   1535            WithTransaction::Yes,
   1536            // XXX We don't want to expose the <br> for IME, but the plaintext
   1537            // serializer requires this. See bug 1385905.
   1538            BRElementType::Normal, pointToInsertFollowingBRElement);
   1539    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
   1540      return insertPaddingBRElementResultOrError.propagateErr();
   1541    }
   1542    insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
   1543  }
   1544  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
   1545      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1546          &aBlockElementToSplit))) {
   1547    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1548  }
   1549  if (mHTMLEditor.GetDefaultParagraphSeparator() != ParagraphSeparator::br) {
   1550    return pointToSplit;
   1551  }
   1552  // If we're in the legacy mode, we don't want the right paragraph start with
   1553  // an empty line.  So, if the right paragraph now starts with 2 <br> elements,
   1554  // remove the second one.  (The first one is in the closest container element,
   1555  // so, we want to keep it.)
   1556  const WSScanResult nextVisibleThing =
   1557      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1558          {}, pointToSplit, &aBlockElementToSplit);
   1559  if (!nextVisibleThing.ReachedBRElement()) {
   1560    return pointToSplit;
   1561  }
   1562  const WSScanResult nextVisibleThingAfterFirstBRElement =
   1563      WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1564          {}, nextVisibleThing.PointAfterReachedContent<EditorRawDOMPoint>(),
   1565          &aBlockElementToSplit);
   1566  if (!nextVisibleThingAfterFirstBRElement.ReachedBRElement()) {
   1567    return pointToSplit;
   1568  }
   1569  nsresult rv = mHTMLEditor.DeleteNodeWithTransaction(
   1570      MOZ_KnownLive(*nextVisibleThingAfterFirstBRElement.BRElementPtr()));
   1571  if (NS_FAILED(rv)) {
   1572    NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   1573    return Err(rv);
   1574  }
   1575  if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
   1576      NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1577          &aBlockElementToSplit))) {
   1578    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1579  }
   1580  return pointToSplit;
   1581 }
   1582 
   1583 Result<SplitNodeResult, nsresult>
   1584 HTMLEditor::AutoInsertParagraphHandler::SplitParagraphWithTransaction(
   1585    Element& aBlockElementToSplit, const EditorDOMPoint& aPointToSplit) {
   1586  // First, maybe the split point follows an invisible <br>.  E.g., when
   1587  // `<p><a href=foo>foo[]<br></a></p>`,
   1588  // GetBetterSplitPointToAvoidToContinueLink() adjusted the split point as
   1589  // `<p><a href=foo>foo<br></a>{}</p>`.  Then, we shouldn't insert another
   1590  // <br> to end of the left <p> to make the last line visible.  Even though we
   1591  // need to insert an invisible <br> element later, let's delete the invisible
   1592  // line break first to make this method simpler.
   1593  Result<EditorDOMPoint, nsresult> deleteInvisibleLineBreakResultOrError =
   1594      EnsureNoInvisibleLineBreakBeforePointToSplit(aBlockElementToSplit,
   1595                                                   aPointToSplit);
   1596  if (MOZ_UNLIKELY(deleteInvisibleLineBreakResultOrError.isErr())) {
   1597    NS_WARNING(
   1598        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() failed");
   1599    return deleteInvisibleLineBreakResultOrError.propagateErr();
   1600  }
   1601  EditorDOMPoint pointToSplit = deleteInvisibleLineBreakResultOrError.unwrap();
   1602  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
   1603  MOZ_ASSERT(pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1604      &aBlockElementToSplit));
   1605 
   1606  // Then, we need to keep the visibility of the surrounding collapsible
   1607  // white-spaces at the split point.
   1608  Result<EditorDOMPoint, nsresult> preparationResult =
   1609      WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
   1610          mHTMLEditor, aPointToSplit, aBlockElementToSplit);
   1611  if (MOZ_UNLIKELY(preparationResult.isErr())) {
   1612    NS_WARNING(
   1613        "WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement() failed");
   1614    return preparationResult.propagateErr();
   1615  }
   1616  pointToSplit = preparationResult.unwrap();
   1617  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
   1618  MOZ_ASSERT(pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1619      &aBlockElementToSplit));
   1620 
   1621  // Next, if there are some inline elements which we will split and we're
   1622  // splitting the deepest one at end of it, we need to put invisible <br>
   1623  // before splitting to preserve the cloned inline elements in the new
   1624  // paragraph.
   1625  {
   1626    Result<EditorDOMPoint, nsresult> insertPaddingBRElementResultOrError =
   1627        MaybeInsertFollowingBRElementToPreserveRightBlock(aBlockElementToSplit,
   1628                                                          pointToSplit);
   1629    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
   1630      NS_WARNING(
   1631          "AutoInsertParagraphHandler::"
   1632          "MaybeInsertFollowingBRElementToPreserveRightBlock() failed");
   1633      return insertPaddingBRElementResultOrError.propagateErr();
   1634    }
   1635    pointToSplit = insertPaddingBRElementResultOrError.unwrap();
   1636    if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
   1637        NS_WARN_IF(!pointToSplit.GetContainer()->IsInclusiveDescendantOf(
   1638            &aBlockElementToSplit))) {
   1639      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1640    }
   1641  }
   1642 
   1643  // Then, split current paragraph.
   1644  const RefPtr<Element> deepestContainerElementToSplit =
   1645      HTMLEditUtils::GetInclusiveAncestorElement(
   1646          *pointToSplit.ContainerAs<nsIContent>(),
   1647          HTMLEditUtils::ClosestContainerElementOrVoidAncestorLimiter,
   1648          BlockInlineCheck::UseComputedDisplayOutsideStyle,
   1649          &aBlockElementToSplit);
   1650  if (NS_WARN_IF(!deepestContainerElementToSplit)) {
   1651    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1652  }
   1653  Result<SplitNodeResult, nsresult> splitDivOrPResultOrError =
   1654      mHTMLEditor.SplitNodeDeepWithTransaction(
   1655          aBlockElementToSplit, pointToSplit,
   1656          SplitAtEdges::eAllowToCreateEmptyContainer);
   1657  if (MOZ_UNLIKELY(splitDivOrPResultOrError.isErr())) {
   1658    NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed");
   1659    return splitDivOrPResultOrError;
   1660  }
   1661  SplitNodeResult splitDivOrPResult = splitDivOrPResultOrError.unwrap();
   1662  if (MOZ_UNLIKELY(!splitDivOrPResult.DidSplit())) {
   1663    NS_WARNING(
   1664        "HTMLEditor::SplitNodeDeepWithTransaction() didn't split any nodes");
   1665    return splitDivOrPResult;
   1666  }
   1667 
   1668  // We'll compute caret suggestion later.  So the simple result is not needed.
   1669  splitDivOrPResult.IgnoreCaretPointSuggestion();
   1670 
   1671  auto* const leftDivOrParagraphElement =
   1672      splitDivOrPResult.GetPreviousContentAs<Element>();
   1673  MOZ_ASSERT(leftDivOrParagraphElement,
   1674             "SplitNodeResult::GetPreviousContent() should return something if "
   1675             "DidSplit() returns true");
   1676  auto* const rightDivOrParagraphElement =
   1677      splitDivOrPResult.GetNextContentAs<Element>();
   1678  MOZ_ASSERT(rightDivOrParagraphElement,
   1679             "SplitNodeResult::GetNextContent() should return something if "
   1680             "DidSplit() returns true");
   1681 
   1682  // Remove ID attribute on the paragraph from the right node.
   1683  // MOZ_KnownLive(rightDivOrParagraphElement) because it's grabbed by
   1684  // splitDivOrPResult.
   1685  nsresult rv = mHTMLEditor.RemoveAttributeWithTransaction(
   1686      MOZ_KnownLive(*rightDivOrParagraphElement), *nsGkAtoms::id);
   1687  if (NS_FAILED(rv)) {
   1688    NS_WARNING(
   1689        "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::id) failed");
   1690    return Err(rv);
   1691  }
   1692 
   1693  // Finally, we need to ensure that both paragraphs are visible even if they
   1694  // are empty.  Note that we need to use padding <br> element for the empty
   1695  // last line as usual because it won't appear as a line break when serialized
   1696  // by ContentEventHandler.  Thus, if we were using normal <br> elements,
   1697  // disappearing following line break of composition string would make IME
   1698  // confused.
   1699  if (NS_WARN_IF(!deepestContainerElementToSplit->IsInclusiveDescendantOf(
   1700          leftDivOrParagraphElement))) {
   1701    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1702  }
   1703  const EditorDOMPoint pointToInsertBRElement = [&]() MOZ_NEVER_INLINE_DEBUG {
   1704    // If we split the paragraph immediately after a block boundary or a line
   1705    // break, we need to put a padding <br> to make an empty line.
   1706    const WSScanResult prevVisibleThing =
   1707        WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
   1708            {}, EditorRawDOMPoint::AtEndOf(*deepestContainerElementToSplit),
   1709            leftDivOrParagraphElement);
   1710    if (prevVisibleThing.ReachedLineBoundary()) {
   1711      return EditorDOMPoint::AtEndOf(*deepestContainerElementToSplit);
   1712    }
   1713    // If we split a descendant element and it's empty, we need to put a padding
   1714    // <br> element into it to preserve the style of the element.
   1715    if (deepestContainerElementToSplit == leftDivOrParagraphElement) {
   1716      return EditorDOMPoint();
   1717    }
   1718    const WSScanResult nextVisibleThing =
   1719        WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   1720            {}, EditorRawDOMPoint(deepestContainerElementToSplit, 0),
   1721            leftDivOrParagraphElement);
   1722    return nextVisibleThing.ReachedCurrentBlockBoundary()
   1723               ? EditorDOMPoint::AtEndOf(*deepestContainerElementToSplit)
   1724               : EditorDOMPoint();
   1725  }();
   1726  if (pointToInsertBRElement.IsSet()) {
   1727    Result<CreateElementResult, nsresult> insertPaddingBRElementResultOrError =
   1728        mHTMLEditor.InsertBRElement(
   1729            WithTransaction::Yes,
   1730            // XXX We don't want to expose the <br> for IME, but the plaintext
   1731            // serializer requires this. See bug 1385905.
   1732            BRElementType::Normal, pointToInsertBRElement);
   1733    if (MOZ_UNLIKELY(insertPaddingBRElementResultOrError.isErr())) {
   1734      return insertPaddingBRElementResultOrError.propagateErr();
   1735    }
   1736    insertPaddingBRElementResultOrError.unwrap().IgnoreCaretPointSuggestion();
   1737  }
   1738 
   1739  // The right paragraph should not be empty because
   1740  // MaybeInsertFollowingBRElementToPreserveRightBlock() should've already put a
   1741  // padding <br> before splitting the paragraph.
   1742  if (NS_WARN_IF(HTMLEditUtils::IsEmptyNode(
   1743          *rightDivOrParagraphElement,
   1744          {EmptyCheckOption::TreatSingleBRElementAsVisible,
   1745           EmptyCheckOption::TreatListItemAsVisible,
   1746           EmptyCheckOption::TreatTableCellAsVisible}))) {
   1747    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1748  }
   1749 
   1750  // Let's put caret at start of the first leaf container.
   1751  nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
   1752      *rightDivOrParagraphElement, {LeafNodeType::LeafNodeOrChildBlock},
   1753      BlockInlineCheck::UseComputedDisplayStyle);
   1754  if (MOZ_UNLIKELY(!child)) {
   1755    return SplitNodeResult(std::move(splitDivOrPResult),
   1756                           EditorDOMPoint(rightDivOrParagraphElement, 0u));
   1757  }
   1758 
   1759  return child->IsText() || HTMLEditUtils::IsContainerNode(*child)
   1760             ? SplitNodeResult(std::move(splitDivOrPResult),
   1761                               EditorDOMPoint(child, 0u))
   1762             : SplitNodeResult(std::move(splitDivOrPResult),
   1763                               EditorDOMPoint(child));
   1764 }
   1765 
   1766 Result<CreateLineBreakResult, nsresult>
   1767 HTMLEditor::AutoInsertParagraphHandler::InsertBRElementIfEmptyBlockElement(
   1768    Element& aMaybeBlockElement,
   1769    InsertBRElementIntoEmptyBlock aInsertBRElementIntoEmptyBlock,
   1770    BlockInlineCheck aBlockInlineCheck) {
   1771  if (!HTMLEditUtils::IsBlockElement(aMaybeBlockElement, aBlockInlineCheck)) {
   1772    return CreateLineBreakResult::NotHandled();
   1773  }
   1774 
   1775  if (!HTMLEditUtils::IsEmptyNode(
   1776          aMaybeBlockElement,
   1777          {EmptyCheckOption::TreatSingleBRElementAsVisible})) {
   1778    return CreateLineBreakResult::NotHandled();
   1779  }
   1780 
   1781  // XXX: Probably, we should use
   1782  //      InsertPaddingBRElementForEmptyLastLineWithTransaction here, and
   1783  //      if there are some empty inline container, we should put the <br>
   1784  //      into the last one.
   1785  Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
   1786      mHTMLEditor.InsertLineBreak(
   1787          WithTransaction::Yes, LineBreakType::BRElement,
   1788          aInsertBRElementIntoEmptyBlock == InsertBRElementIntoEmptyBlock::Start
   1789              ? EditorDOMPoint(&aMaybeBlockElement, 0u)
   1790              : EditorDOMPoint::AtEndOf(aMaybeBlockElement));
   1791  NS_WARNING_ASSERTION(insertBRElementResultOrError.isOk(),
   1792                       "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
   1793                       "LineBreakType::BRElement) failed");
   1794  return insertBRElementResultOrError;
   1795 }
   1796 
   1797 // static
   1798 Element* HTMLEditor::AutoInsertParagraphHandler::
   1799    GetDeepestFirstChildInlineContainerElement(Element& aBlockElement) {
   1800  // XXX Should we ignore invisible children like empty Text, Comment, etc?
   1801  Element* result = nullptr;
   1802  for (Element* maybeDeepestInlineContainer =
   1803           Element::FromNodeOrNull(aBlockElement.GetFirstChild());
   1804       maybeDeepestInlineContainer &&
   1805       HTMLEditUtils::IsInlineContent(
   1806           *maybeDeepestInlineContainer,
   1807           BlockInlineCheck::UseComputedDisplayStyle) &&
   1808       HTMLEditUtils::IsContainerNode(*maybeDeepestInlineContainer);
   1809       // FIXME: There may be visible node before first element child, so, here
   1810       // is obviously wrong.
   1811       maybeDeepestInlineContainer =
   1812           maybeDeepestInlineContainer->GetFirstElementChild()) {
   1813    result = maybeDeepestInlineContainer;
   1814  }
   1815  return result;
   1816 }
   1817 
   1818 Result<InsertParagraphResult, nsresult>
   1819 HTMLEditor::AutoInsertParagraphHandler::HandleInListItemElement(
   1820    Element& aListItemElement, const EditorDOMPoint& aPointToSplit) {
   1821  MOZ_ASSERT(HTMLEditUtils::IsListItemElement(aListItemElement));
   1822 
   1823  // If aListItemElement is empty, then we want to outdent its content.
   1824  if (&mEditingHost != aListItemElement.GetParentElement() &&
   1825      HTMLEditUtils::IsEmptyBlockElement(
   1826          aListItemElement,
   1827          {EmptyCheckOption::TreatNonEditableContentAsInvisible},
   1828          BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
   1829    RefPtr<Element> leftListElement = aListItemElement.GetParentElement();
   1830    // If the given list item element is not the last list item element of
   1831    // its parent nor not followed by sub list elements, split the parent
   1832    // before it.
   1833    if (!HTMLEditUtils::IsLastChild(aListItemElement,
   1834                                    {WalkTreeOption::IgnoreNonEditableNode})) {
   1835      Result<SplitNodeResult, nsresult> splitListItemParentResult =
   1836          mHTMLEditor.SplitNodeWithTransaction(
   1837              EditorDOMPoint(&aListItemElement));
   1838      if (MOZ_UNLIKELY(splitListItemParentResult.isErr())) {
   1839        NS_WARNING("HTMLEditor::SplitNodeWithTransaction() failed");
   1840        return splitListItemParentResult.propagateErr();
   1841      }
   1842      SplitNodeResult unwrappedSplitListItemParentResult =
   1843          splitListItemParentResult.unwrap();
   1844      if (MOZ_UNLIKELY(!unwrappedSplitListItemParentResult.DidSplit())) {
   1845        NS_WARNING(
   1846            "HTMLEditor::SplitNodeWithTransaction() didn't split the parent of "
   1847            "aListItemElement");
   1848        MOZ_ASSERT(
   1849            !unwrappedSplitListItemParentResult.HasCaretPointSuggestion());
   1850        return Err(NS_ERROR_FAILURE);
   1851      }
   1852      unwrappedSplitListItemParentResult.IgnoreCaretPointSuggestion();
   1853      leftListElement =
   1854          unwrappedSplitListItemParentResult.GetPreviousContentAs<Element>();
   1855      MOZ_DIAGNOSTIC_ASSERT(leftListElement);
   1856    }
   1857 
   1858    auto afterLeftListElement = EditorDOMPoint::After(leftListElement);
   1859    if (MOZ_UNLIKELY(!afterLeftListElement.IsInContentNode())) {
   1860      return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1861    }
   1862 
   1863    // If aListItemElement is in an invalid sub-list element, move it into
   1864    // the grand parent list element in order to outdent.
   1865    if (HTMLEditUtils::IsListElement(
   1866            *afterLeftListElement.ContainerAs<nsIContent>())) {
   1867      Result<MoveNodeResult, nsresult> moveListItemElementResult =
   1868          mHTMLEditor.MoveNodeWithTransaction(aListItemElement,
   1869                                              afterLeftListElement);
   1870      if (MOZ_UNLIKELY(moveListItemElementResult.isErr())) {
   1871        NS_WARNING("HTMLEditor::MoveNodeWithTransaction() failed");
   1872        return moveListItemElementResult.propagateErr();
   1873      }
   1874      moveListItemElementResult.inspect().IgnoreCaretPointSuggestion();
   1875      return InsertParagraphResult(aListItemElement,
   1876                                   EditorDOMPoint(&aListItemElement, 0u));
   1877    }
   1878 
   1879    // Otherwise, replace the empty aListItemElement with a new paragraph.
   1880    nsresult rv = mHTMLEditor.DeleteNodeWithTransaction(aListItemElement);
   1881    if (NS_FAILED(rv)) {
   1882      NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
   1883      return Err(rv);
   1884    }
   1885    nsStaticAtom& newParagraphTagName =
   1886        &mDefaultParagraphSeparatorTagName == nsGkAtoms::br
   1887            ? *nsGkAtoms::p
   1888            : mDefaultParagraphSeparatorTagName;
   1889    // MOZ_KnownLive(newParagraphTagName) because it's available until shutdown.
   1890    Result<CreateElementResult, nsresult> createNewParagraphElementResult =
   1891        mHTMLEditor.CreateAndInsertElement(
   1892            WithTransaction::Yes, MOZ_KnownLive(newParagraphTagName),
   1893            afterLeftListElement, HTMLEditor::InsertNewBRElement);
   1894    if (MOZ_UNLIKELY(createNewParagraphElementResult.isErr())) {
   1895      NS_WARNING(
   1896          "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
   1897      return createNewParagraphElementResult.propagateErr();
   1898    }
   1899    createNewParagraphElementResult.inspect().IgnoreCaretPointSuggestion();
   1900    MOZ_ASSERT(createNewParagraphElementResult.inspect().GetNewNode());
   1901    EditorDOMPoint pointToPutCaret(
   1902        createNewParagraphElementResult.inspect().GetNewNode(), 0u);
   1903    return InsertParagraphResult(
   1904        *createNewParagraphElementResult.inspect().GetNewNode(),
   1905        std::move(pointToPutCaret));
   1906  }
   1907 
   1908  const EditorDOMPoint pointToSplit =
   1909      GetBetterPointToSplitParagraph(aListItemElement, aPointToSplit);
   1910  MOZ_ASSERT(pointToSplit.IsInContentNodeAndValidInComposedDoc());
   1911 
   1912  // If insertParagraph at end of <dt> or <dd>, we should put opposite type list
   1913  // item without copying the style of end of aListItemElement.
   1914  // FIXME: Chrome does not do this.  So, we should stop doing this at least on
   1915  // Firefox later.
   1916  if (aListItemElement.IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::dd) &&
   1917      SplitPointIsEndOfSplittingBlock(aListItemElement, pointToSplit,
   1918                                      IgnoreBlockBoundaries::Yes) &&
   1919      // However, don't do that if we're handling it in empty list item.
   1920      !SplitPointIsStartOfSplittingBlock(aListItemElement, pointToSplit,
   1921                                         IgnoreBlockBoundaries::Yes)) {
   1922    nsStaticAtom& oppositeTypeListItemTag =
   1923        aListItemElement.IsHTMLElement(nsGkAtoms::dt) ? *nsGkAtoms::dd
   1924                                                      : *nsGkAtoms::dt;
   1925    // MOZ_KnownLive(oppositeTypeListItemTag) because it's available
   1926    // until shutdown.
   1927    Result<CreateElementResult, nsresult>
   1928        insertOppositeTypeListItemResultOrError =
   1929            mHTMLEditor.CreateAndInsertElement(
   1930                WithTransaction::Yes, MOZ_KnownLive(oppositeTypeListItemTag),
   1931                EditorDOMPoint::After(aListItemElement),
   1932                HTMLEditor::InsertNewBRElement);
   1933    if (MOZ_UNLIKELY(insertOppositeTypeListItemResultOrError.isErr())) {
   1934      NS_WARNING(
   1935          "HTMLEditor::CreateAndInsertElement(WithTransaction::Yes) failed");
   1936      return insertOppositeTypeListItemResultOrError.propagateErr();
   1937    }
   1938    CreateElementResult insertOppositeTypeListItemResult =
   1939        insertOppositeTypeListItemResultOrError.unwrap();
   1940    insertOppositeTypeListItemResult.IgnoreCaretPointSuggestion();
   1941    RefPtr<Element> oppositeTypeListItemElement =
   1942        insertOppositeTypeListItemResult.UnwrapNewNode();
   1943    EditorDOMPoint startOfOppositeTypeListItem(oppositeTypeListItemElement, 0u);
   1944    MOZ_ASSERT(oppositeTypeListItemElement);
   1945    return InsertParagraphResult(std::move(oppositeTypeListItemElement),
   1946                                 std::move(startOfOppositeTypeListItem));
   1947  }
   1948 
   1949  // If aListItemElement has some content or aListItemElement is empty but it's
   1950  // a child of editing host, we want a new list item at the same list level.
   1951  // First, sort out white-spaces.
   1952  Result<SplitNodeResult, nsresult> splitListItemResultOrError =
   1953      SplitParagraphWithTransaction(aListItemElement, pointToSplit);
   1954  if (MOZ_UNLIKELY(splitListItemResultOrError.isErr())) {
   1955    NS_WARNING(
   1956        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() failed");
   1957    return splitListItemResultOrError.propagateErr();
   1958  }
   1959  SplitNodeResult splitListItemElement = splitListItemResultOrError.unwrap();
   1960  EditorDOMPoint pointToPutCaret = splitListItemElement.UnwrapCaretPoint();
   1961  if (MOZ_UNLIKELY(!aListItemElement.GetParent())) {
   1962    NS_WARNING("Somebody disconnected the target listitem from the parent");
   1963    return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   1964  }
   1965 
   1966  // If aListItemElement is not replaced, we should not do anything anymore.
   1967  if (MOZ_UNLIKELY(!splitListItemElement.DidSplit()) ||
   1968      NS_WARN_IF(!splitListItemElement.GetNewContentAs<Element>()) ||
   1969      NS_WARN_IF(!splitListItemElement.GetOriginalContentAs<Element>())) {
   1970    NS_WARNING(
   1971        "AutoInsertParagraphHandler::SplitParagraphWithTransaction() didn't "
   1972        "split the listitem");
   1973    return Err(NS_ERROR_FAILURE);
   1974  }
   1975  auto* const rightListItemElement =
   1976      splitListItemElement.GetNextContentAs<Element>();
   1977  return InsertParagraphResult(*rightListItemElement,
   1978                               std::move(pointToPutCaret));
   1979 }
   1980 
   1981 // static
   1982 bool HTMLEditor::AutoInsertParagraphHandler::SplitPointIsStartOfSplittingBlock(
   1983    const Element& aBlockElementToSplit, const EditorDOMPoint& aPointToSplit,
   1984    IgnoreBlockBoundaries aIgnoreBlockBoundaries) {
   1985  EditorRawDOMPoint pointToSplit = aPointToSplit.To<EditorRawDOMPoint>();
   1986  while (true) {
   1987    const WSScanResult prevVisibleThing =
   1988        WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary({}, pointToSplit);
   1989    if (!prevVisibleThing.ReachedCurrentBlockBoundary()) {
   1990      return false;
   1991    }
   1992    if (prevVisibleThing.ElementPtr() == &aBlockElementToSplit) {
   1993      return true;
   1994    }
   1995    if (!static_cast<bool>(aIgnoreBlockBoundaries)) {
   1996      return false;
   1997    }
   1998    pointToSplit = pointToSplit.ParentPoint();
   1999  }
   2000 }
   2001 
   2002 // static
   2003 bool HTMLEditor::AutoInsertParagraphHandler::SplitPointIsEndOfSplittingBlock(
   2004    const Element& aBlockElementToSplit, const EditorDOMPoint& aPointToSplit,
   2005    IgnoreBlockBoundaries aIgnoreBlockBoundaries) {
   2006  bool maybeFollowedByInvisibleBRElement = true;
   2007  EditorRawDOMPoint pointToSplit = aPointToSplit.To<EditorRawDOMPoint>();
   2008  while (true) {
   2009    WSScanResult nextVisibleThing =
   2010        WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   2011            {}, pointToSplit, &aBlockElementToSplit);
   2012    if (maybeFollowedByInvisibleBRElement &&
   2013        (nextVisibleThing.ReachedBRElement() ||
   2014         nextVisibleThing.ReachedPreformattedLineBreak())) {
   2015      nextVisibleThing =
   2016          WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
   2017              {},
   2018              nextVisibleThing.PointAfterReachedContent<EditorRawDOMPoint>(),
   2019              &aBlockElementToSplit);
   2020    }
   2021    if (!nextVisibleThing.ReachedCurrentBlockBoundary()) {
   2022      return false;
   2023    }
   2024    if (nextVisibleThing.ElementPtr() == &aBlockElementToSplit) {
   2025      return true;
   2026    }
   2027    if (!static_cast<bool>(aIgnoreBlockBoundaries)) {
   2028      return false;
   2029    }
   2030    pointToSplit = pointToSplit.AfterContainer();
   2031    // <br> element after other block boundary creates an empty line so that
   2032    // it's always visible.
   2033    maybeFollowedByInvisibleBRElement = false;
   2034  }
   2035 }
   2036 
   2037 }  // namespace mozilla