Commit 07fe22e0 authored by Masayuki Nakano's avatar Masayuki Nakano
Browse files

Bug 1730442 - part 7: Rewrite `HTMLEditor::InsertTableRowsWithTransaction()`...

Bug 1730442 - part 7: Rewrite `HTMLEditor::InsertTableRowsWithTransaction()` to collect any cell information before touching the DOM tree r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D146365
parent 8e652599
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -1277,7 +1277,7 @@ EditActionResult HTMLEditor::HandleTabKeyPressInTable(
  }

  // Find enclosing table cell from selection (cell may be selected element)
  Element* cellElement =
  const RefPtr<Element> cellElement =
      GetInclusiveAncestorByTagNameAtSelection(*nsGkAtoms::td);
  if (!cellElement) {
    NS_WARNING(
@@ -1344,13 +1344,14 @@ EditActionResult HTMLEditor::HandleTabKeyPressInTable(
                         "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
    return EditActionHandled(rv);
  }
  rv = InsertTableRowsWithTransaction(1, InsertPosition::eAfterSelectedCell);
  rv = InsertTableRowsWithTransaction(*cellElement, 1,
                                      InsertPosition::eAfterSelectedCell);
  if (NS_WARN_IF(Destroyed())) {
    return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
  }
  if (NS_FAILED(rv)) {
    NS_WARNING(
        "HTMLEditor::InsertTableRowsWithTransaction(1, "
        "HTMLEditor::InsertTableRowsWithTransaction(*cellElement, 1, "
        "InsertPosition::eAfterSelectedCell) failed");
    return EditActionHandled(rv);
  }
+15 −6
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "mozilla/EditorBase.h"
#include "mozilla/EditorForwards.h"
#include "mozilla/EditorUtils.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/HTMLEditHelpers.h"
#include "mozilla/ManualNAC.h"
#include "mozilla/Result.h"
@@ -3052,6 +3053,13 @@ class HTMLEditor final : public EditorBase,
    [[nodiscard]] static CellData AtIndexInTableElement(
        const HTMLEditor& aHTMLEditor, const Element& aTableElement,
        int32_t aRowIndex, int32_t aColumnIndex);
    [[nodiscard]] static CellData AtIndexInTableElement(
        const HTMLEditor& aHTMLEditor, const Element& aTableElement,
        const CellIndexes& aIndexes) {
      MOZ_ASSERT(!aIndexes.isErr());
      return AtIndexInTableElement(aHTMLEditor, aTableElement, aIndexes.mRow,
                                   aIndexes.mColumn);
    }

    /**
     * Treated as error if fails to compute current index or first index of the
@@ -3612,18 +3620,19 @@ class HTMLEditor final : public EditorBase,

  /**
   * InsertTableRowsWithTransaction() inserts <tr> elements before or after
   * a cell element containing first selection range.  I.e., if the cell
   * spans rows and aInsertPosition is eAfterSelectedCell, new rows will be
   * inserted after the most-bottom row which contains the cell.  If first
   * selection range is not in table cell element, this does nothing but
   * does not return error.
   * aCellElement.  When aCellElement spans rows and aInsertPosition is
   * eAfterSelectedCell, new rows will be inserted after the most-bottom row
   * which contains the cell.
   *
   * @param aCellElement                The cell element pinting where this will
   *                                    insert a row before or after.
   * @param aNumberOfRowsToInsert       Number of rows to insert.
   * @param aInsertPosition             Before or after the target cell which
   *                                    contains first selection range.
   */
  MOZ_CAN_RUN_SCRIPT nsresult InsertTableRowsWithTransaction(
      int32_t aNumberOfRowsToInsert, InsertPosition aInsertPosition);
      Element& aCellElement, int32_t aNumberOfRowsToInsert,
      InsertPosition aInsertPosition);

  /**
   * Insert a new cell after or before supplied aCell.
+8 −6
Original line number Diff line number Diff line
@@ -292,11 +292,12 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
          "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
      return EditorBase::ToGenericNSResult(rv);
    }
    DebugOnly<nsresult> rvIgnored =
        InsertTableRowsWithTransaction(1, InsertPosition::eBeforeSelectedCell);
    OwningNonNull<Element> targetCellElement(*mInlineEditedCell);
    DebugOnly<nsresult> rvIgnored = InsertTableRowsWithTransaction(
        targetCellElement, 1, InsertPosition::eBeforeSelectedCell);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "HTMLEditor::InsertTableRowsWithTransaction(1, "
        "HTMLEditor::InsertTableRowsWithTransaction(targetCellElement, 1, "
        "InsertPosition::eBeforeSelectedCell) failed, but ignored");
  } else if (anonclass.EqualsLiteral("mozTableAddRowAfter")) {
    AutoEditActionDataSetter editActionData(*this,
@@ -308,11 +309,12 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
          "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
      return EditorBase::ToGenericNSResult(rv);
    }
    DebugOnly<nsresult> rvIgnored =
        InsertTableRowsWithTransaction(1, InsertPosition::eAfterSelectedCell);
    OwningNonNull<Element> targetCellElement(*mInlineEditedCell);
    DebugOnly<nsresult> rvIgnored = InsertTableRowsWithTransaction(
        targetCellElement, 1, InsertPosition::eAfterSelectedCell);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "HTMLEditor::InsertTableRowsWithTransaction(1, "
        "HTMLEditor::InsertTableRowsWithTransaction(targetCellElement, 1, "
        "InsertPosition::eAfterSelectedCell) failed, but ignored");
  } else if (anonclass.EqualsLiteral("mozTableRemoveColumn")) {
    AutoEditActionDataSetter editActionData(*this,
+186 −129
Original line number Diff line number Diff line
@@ -857,6 +857,10 @@ nsresult HTMLEditor::InsertTableColumnsWithTransaction(

NS_IMETHODIMP HTMLEditor::InsertTableRow(int32_t aNumberOfRowsToInsert,
                                         bool aInsertAfterSelectedCell) {
  if (aNumberOfRowsToInsert <= 0) {
    return NS_OK;
  }

  AutoEditActionDataSetter editActionData(*this,
                                          EditAction::eInsertTableRowElement);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
@@ -866,9 +870,20 @@ NS_IMETHODIMP HTMLEditor::InsertTableRow(int32_t aNumberOfRowsToInsert,
    return EditorBase::ToGenericNSResult(rv);
  }

  Result<RefPtr<Element>, nsresult> cellElementOrError =
      GetFirstSelectedCellElementInTable();
  if (cellElementOrError.isErr()) {
    NS_WARNING("HTMLEditor::GetFirstSelectedCellElementInTable() failed");
    return EditorBase::ToGenericNSResult(cellElementOrError.unwrapErr());
  }

  if (!cellElementOrError.inspect()) {
    return NS_OK;
  }

  rv = InsertTableRowsWithTransaction(
      aNumberOfRowsToInsert, aInsertAfterSelectedCell
                                 ? InsertPosition::eAfterSelectedCell
      MOZ_KnownLive(*cellElementOrError.inspect()), aNumberOfRowsToInsert,
      aInsertAfterSelectedCell ? InsertPosition::eAfterSelectedCell
                               : InsertPosition::eBeforeSelectedCell);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "HTMLEditor::InsertTableRowsWithTransaction() failed");
@@ -876,38 +891,30 @@ NS_IMETHODIMP HTMLEditor::InsertTableRow(int32_t aNumberOfRowsToInsert,
}

nsresult HTMLEditor::InsertTableRowsWithTransaction(
    int32_t aNumberOfRowsToInsert, InsertPosition aInsertPosition) {
    Element& aCellElement, int32_t aNumberOfRowsToInsert,
    InsertPosition aInsertPosition) {
  MOZ_ASSERT(IsEditActionDataAvailable());
  MOZ_ASSERT(HTMLEditUtils::IsTableCell(&aCellElement));

  RefPtr<Element> table;
  RefPtr<Element> curCell;

  int32_t startRowIndex, startColIndex;
  nsresult rv =
      GetCellContext(getter_AddRefs(table), getter_AddRefs(curCell), nullptr,
                     nullptr, &startRowIndex, &startColIndex);
  if (NS_FAILED(rv)) {
    NS_WARNING("HTMLEditor::GetCellContext() failed");
    return rv;
  }
  if (!table || !curCell) {
    NS_WARNING(
        "HTMLEditor::GetCellContext() didn't return <table> and/or cell");
    // Don't fail if no cell found.
    return NS_OK;
  const RefPtr<PresShell> presShell = GetPresShell();
  if (MOZ_UNLIKELY(NS_WARN_IF(!presShell))) {
    return NS_ERROR_FAILURE;
  }

  // Get more data for current cell in row we are inserting at because we need
  // colspan.
  const auto cellDataAtSelection = CellData::AtIndexInTableElement(
      *this, *table, startRowIndex, startColIndex);
  if (NS_WARN_IF(cellDataAtSelection.FailedOrNotFound())) {
  if (MOZ_UNLIKELY(
          !HTMLEditUtils::IsTableRow(aCellElement.GetParentElement()))) {
    NS_WARNING("Tried to insert columns to non-<tr> element");
    return NS_ERROR_FAILURE;
  }
  MOZ_ASSERT(curCell == cellDataAtSelection.mElement);

  const RefPtr<Element> tableElement =
      HTMLEditUtils::GetClosestAncestorTableElement(aCellElement);
  if (MOZ_UNLIKELY(!tableElement)) {
    return NS_OK;
  }

  const Result<TableSize, nsresult> tableSizeOrError =
      TableSize::Create(*this, *table);
      TableSize::Create(*this, *tableElement);
  if (NS_WARN_IF(tableSizeOrError.isErr())) {
    return tableSizeOrError.inspectErr();
  }
@@ -915,6 +922,20 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction(
  // Should not be empty since we've already found a cell.
  MOZ_ASSERT(!tableSize.IsEmpty());

  const CellIndexes cellIndexes(aCellElement, presShell);
  if (NS_WARN_IF(cellIndexes.isErr())) {
    return NS_ERROR_FAILURE;
  }

  // Get more data for current cell in row we are inserting at because we need
  // rowspan.
  const auto cellData =
      CellData::AtIndexInTableElement(*this, *tableElement, cellIndexes);
  if (NS_WARN_IF(cellData.FailedOrNotFound())) {
    return NS_ERROR_FAILURE;
  }
  MOZ_ASSERT(&aCellElement == cellData.mElement);

  AutoPlaceholderBatch treateAsOneTransaction(
      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
  // Prevent auto insertion of BR in new cell until we're done
@@ -928,80 +949,72 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction(
      !error.Failed(),
      "HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");

  switch (aInsertPosition) {
    case InsertPosition::eBeforeSelectedCell:
      break;
    case InsertPosition::eAfterSelectedCell:
      // Use row after current cell.
      startRowIndex += cellDataAtSelection.mEffectiveRowSpan;
  struct ElementWithNewRowSpan final {
    const OwningNonNull<Element> mCellElement;
    const int32_t mNewRowSpan;

    ElementWithNewRowSpan(Element& aCellElement, int32_t aNewRowSpan)
        : mCellElement(aCellElement), mNewRowSpan(aNewRowSpan) {}
  };
  AutoTArray<ElementWithNewRowSpan, 16> cellElementsToModifyRowSpan;
  if (aInsertPosition == InsertPosition::eAfterSelectedCell &&
      !cellData.mRowSpan) {
    // Detect when user is adding after a rowspan=0 case.
    // Assume they want to stop the "0" behavior and really add a new row.
    // Thus we set the rowspan to its true value.
      if (!cellDataAtSelection.mRowSpan) {
        DebugOnly<nsresult> rvIgnored =
            SetRowSpan(cellDataAtSelection.mElement,
                       cellDataAtSelection.mEffectiveRowSpan);
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                             "HTMLEditor::SetRowSpan() failed, but ignored");
      }
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("Invalid InsertPosition");
    cellElementsToModifyRowSpan.AppendElement(
        ElementWithNewRowSpan(aCellElement, cellData.mEffectiveRowSpan));
  }

  // We control selection resetting after the insert.
  AutoSelectionSetterAfterTableEdit setCaret(
      *this, table, startRowIndex, cellDataAtSelection.mCurrent.mColumn,
      ePreviousColumn, false);
  // TODO: Remove AutoTransactionsConserveSelection here.  It's not necessary
  //       in normal cases.  However, it may be required for nested edit
  //       actions which may be caused by legacy mutation event listeners or
  //       chrome script.
  AutoTransactionsConserveSelection dontChangeSelection(*this);
  const int32_t startRowIndex =
      aInsertPosition == InsertPosition::eBeforeSelectedCell
          ? cellData.mCurrent.mRow
          : cellData.mCurrent.mRow + cellData.mEffectiveRowSpan;

  RefPtr<Element> cellForRowParent;
  int32_t cellsInRow = 0;
  RefPtr<Element> cellElementForRowParent;
  int32_t numberOfCellsInStartRow = 0;
  int32_t offsetInTRElementToPutCaret = 0;
  if (startRowIndex < tableSize.mRowCount) {
    // We are inserting above an existing row.  Get each cell in the insert
    // row to adjust for colspan effects while we count how many cells are
    // row to adjust for rowspan effects while we count how many cells are
    // needed.
    int32_t colIndex = 0;
    while (true) {
      const auto cellData = CellData::AtIndexInTableElement(
          *this, *table, startRowIndex, colIndex);
      if (cellData.FailedOrNotFound()) {
    for (int32_t colIndex = 0;;) {
      const auto cellDataInStartRow = CellData::AtIndexInTableElement(
          *this, *tableElement, startRowIndex, colIndex);
      if (cellDataInStartRow.FailedOrNotFound()) {
        break;  // Perhaps, we reach end of the row.
      }

      // XXX So, this is impossible case. Will be removed.
      if (!cellData.mElement) {
      if (!cellDataInStartRow.mElement) {
        NS_WARNING("CellData::Update() succeeded, but didn't set mElement");
        break;
      }

      if (cellData.IsSpannedFromOtherRow()) {
      if (cellDataInStartRow.IsSpannedFromOtherRow()) {
        // We have a cell spanning this location.  Increase its rowspan.
        // Note that if rowspan is 0, we do nothing since that cell should
        // automatically extend into the new row.
        if (cellData.mRowSpan > 0) {
          DebugOnly<nsresult> rvIgnored = SetRowSpan(
              cellData.mElement, cellData.mRowSpan + aNumberOfRowsToInsert);
          NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                               "HTMLEditor::SetRowSpan() failed, but ignored");
        if (cellDataInStartRow.mRowSpan > 0) {
          cellElementsToModifyRowSpan.AppendElement(ElementWithNewRowSpan(
              *cellDataInStartRow.mElement,
              cellDataInStartRow.mRowSpan + aNumberOfRowsToInsert));
        }
        colIndex = cellData.NextColumnIndex();
        colIndex = cellDataInStartRow.NextColumnIndex();
        continue;
      }

      cellsInRow += cellData.mEffectiveColSpan;
      if (!cellForRowParent) {
        // FYI: Don't use std::move() here since NextColumnIndex() needs it.
        cellForRowParent = cellData.mElement;
      if (colIndex < cellDataInStartRow.mCurrent.mColumn) {
        offsetInTRElementToPutCaret++;
      }

      MOZ_ASSERT(colIndex < cellData.NextColumnIndex());
      colIndex = cellData.NextColumnIndex();
      numberOfCellsInStartRow += cellDataInStartRow.mEffectiveColSpan;
      if (!cellElementForRowParent) {
        // FYI: Don't use std::move() here since NextColumnIndex() needs it.
        cellElementForRowParent = cellDataInStartRow.mElement;
      }
      MOZ_ASSERT(colIndex < cellDataInStartRow.NextColumnIndex());
      colIndex = cellDataInStartRow.NextColumnIndex();
    }
  } else {
    // We are adding a new row after all others.  If it weren't for colspan=0
@@ -1009,44 +1022,47 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction(
    // cells...
    // XXX colspan=0 support has now been removed in table layout so maybe this
    //     can be cleaned up now? (bug 1243183)
    cellsInRow = tableSize.mColumnCount;
    numberOfCellsInStartRow = tableSize.mColumnCount;

    // but we must compensate for all cells with rowspan = 0 in the last row.
    const int32_t kLastRowIndex = tableSize.mRowCount - 1;
    const int32_t lastRowIndex = tableSize.mRowCount - 1;
    for (int32_t colIndex = 0;;) {
      const auto cellData = CellData::AtIndexInTableElement(
          *this, *table, kLastRowIndex, colIndex);
      if (cellData.FailedOrNotFound()) {
      const auto cellDataInLastRow = CellData::AtIndexInTableElement(
          *this, *tableElement, lastRowIndex, colIndex);
      if (cellDataInLastRow.FailedOrNotFound()) {
        break;  // Perhaps, we reach end of the row.
      }

      if (!cellData.mRowSpan) {
        MOZ_ASSERT(cellsInRow >= cellData.mEffectiveColSpan);
        cellsInRow -= cellData.mEffectiveColSpan;
      if (!cellDataInLastRow.mRowSpan) {
        MOZ_ASSERT(numberOfCellsInStartRow >=
                   cellDataInLastRow.mEffectiveColSpan);
        numberOfCellsInStartRow -= cellDataInLastRow.mEffectiveColSpan;
      } else if (colIndex < cellData.mCurrent.mColumn) {
        offsetInTRElementToPutCaret++;
      }

      // Save cell from the last row that we will use below
      if (!cellForRowParent && !cellData.IsSpannedFromOtherRow()) {
      if (!cellElementForRowParent &&
          !cellDataInLastRow.IsSpannedFromOtherRow()) {
        // FYI: Don't use std::move() here since NextColumnIndex() needs it.
        cellForRowParent = cellData.mElement;
        cellElementForRowParent = cellDataInLastRow.mElement;
      }

      MOZ_ASSERT(colIndex < cellData.NextColumnIndex());
      MOZ_ASSERT(colIndex < cellDataInLastRow.NextColumnIndex());
      colIndex = cellData.NextColumnIndex();
    }
  }

  if (!cellsInRow) {
    NS_WARNING("There was no cell element in the last row");
  if (MOZ_UNLIKELY(!numberOfCellsInStartRow)) {
    NS_WARNING("There was no cell element in the start row");
    return NS_OK;
  }

  if (!cellForRowParent) {
  if (MOZ_UNLIKELY(!cellElementForRowParent)) {
    NS_WARNING("There was no cell element for the <tr> element");
    return NS_ERROR_FAILURE;
  }
  Element* parentRow =
      GetInclusiveAncestorByTagNameInternal(*nsGkAtoms::tr, *cellForRowParent);
  Element* parentRow = GetInclusiveAncestorByTagNameInternal(
      *nsGkAtoms::tr, *cellElementForRowParent);
  if (!parentRow) {
    NS_WARNING(
        "HTMLEditor::GetInclusiveAncestorByTagNameInternal(nsGkAtoms::tr) "
@@ -1065,49 +1081,90 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction(
    DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
    NS_WARNING_ASSERTION(advanced, "Failed to advance offset");
  }
  // Note that checking whether the editor destroyed or not should be done
  // after inserting all cell elements.  Otherwise, the table is left as
  // not a rectangle.
  auto firstInsertedTRElementOrError =
      [&]() MOZ_CAN_RUN_SCRIPT -> Result<RefPtr<Element>, nsresult> {
    // Block legacy mutation events for making this job simpler.
    nsAutoScriptBlockerSuppressNodeRemoved blockToRunScript;

    // Suppress Rules System selection munging.
    AutoTransactionsConserveSelection dontChangeSelection(*this);

    for (const ElementWithNewRowSpan& cellElementAndNewRowSpan :
         cellElementsToModifyRowSpan) {
      DebugOnly<nsresult> rvIgnored =
          SetRowSpan(MOZ_KnownLive(cellElementAndNewRowSpan.mCellElement),
                     cellElementAndNewRowSpan.mNewRowSpan);
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                           "HTMLEditor::SetRowSpan() failed, but ignored");
    }

  for (int32_t row = 0; row < aNumberOfRowsToInsert; row++) {
    RefPtr<Element> firstInsertedTRElement;
    IgnoredErrorResult error;
    for ([[maybe_unused]] const int32_t rowIndex :
         IntegerRange(aNumberOfRowsToInsert)) {
      // Create a new row
    RefPtr<Element> newRow = CreateElementWithDefaults(*nsGkAtoms::tr);
    if (!newRow) {
      NS_WARNING("HTMLEditor::CreateElementWithDefaults(nsGkAtoms::tr) failed");
      return NS_ERROR_FAILURE;
      RefPtr<Element> newRowElement = CreateElementWithDefaults(*nsGkAtoms::tr);
      if (!newRowElement) {
        NS_WARNING(
            "HTMLEditor::CreateElementWithDefaults(nsGkAtoms::tr) failed");
        return Err(NS_ERROR_FAILURE);
      }

    for (int32_t i = 0; i < cellsInRow; i++) {
      RefPtr<Element> newCell = CreateElementWithDefaults(*nsGkAtoms::td);
      if (!newCell) {
      for ([[maybe_unused]] const int32_t i :
           IntegerRange(numberOfCellsInStartRow)) {
        const RefPtr<Element> newCellElement =
            CreateElementWithDefaults(*nsGkAtoms::td);
        if (!newCellElement) {
          NS_WARNING(
              "HTMLEditor::CreateElementWithDefaults(nsGkAtoms::td) failed");
        return NS_ERROR_FAILURE;
          return Err(NS_ERROR_FAILURE);
        }
      newRow->AppendChild(*newCell, error);
        newRowElement->AppendChild(*newCellElement, error);
        if (error.Failed()) {
          NS_WARNING("nsINode::AppendChild() failed");
        return error.StealNSResult();
          return Err(error.StealNSResult());
        }
      }

      AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
      CreateElementResult insertNewRowResult =
        InsertNodeWithTransaction<Element>(*newRow, pointToInsert);
    if (insertNewRowResult.isErr()) {
          InsertNodeWithTransaction<Element>(*newRowElement, pointToInsert);
      if (insertNewRowResult.isErr() && !insertNewRowResult.EditorDestroyed()) {
        NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
      return insertNewRowResult.unwrapErr();
        return Err(insertNewRowResult.unwrapErr());
      }
    // Because of dontChangeSelection, we've never allowed to transactions to
    // update selection here.
      // Because of dontChangeSelection, we shouldn't handle selection here.
      insertNewRowResult.IgnoreCaretPointSuggestion();
      if (!firstInsertedTRElement) {
        firstInsertedTRElement = std::move(newRowElement);
      }

  // SetSelectionAfterTableEdit from AutoSelectionSetterAfterTableEdit will
  // access frame selection, so we need reframe.
  // Because GetTableCellElementAt() depends on frame.
  if (RefPtr<PresShell> presShell = GetPresShell()) {
    presShell->FlushPendingNotifications(FlushType::Frames);
    }
    return firstInsertedTRElement;
  }();
  if (NS_WARN_IF(Destroyed())) {
    return NS_ERROR_EDITOR_DESTROYED;
  }
  if (MOZ_UNLIKELY(firstInsertedTRElementOrError.isErr())) {
    return firstInsertedTRElementOrError.unwrapErr();
  }

  return NS_OK;
  const OwningNonNull<Element> cellElementToPutCaret = [&]() {
    if (MOZ_LIKELY(firstInsertedTRElementOrError.inspect())) {
      EditorRawDOMPoint point(firstInsertedTRElementOrError.inspect(),
                              offsetInTRElementToPutCaret);
      if (MOZ_LIKELY(point.IsSetAndValid()) &&
          MOZ_LIKELY(!point.IsEndOfContainer()) &&
          MOZ_LIKELY(HTMLEditUtils::IsTableCell(point.GetChild()))) {
        return OwningNonNull<Element>(*point.GetChild()->AsElement());
      }
    }
    return OwningNonNull<Element>(aCellElement);
  }();
  CollapseSelectionToDeepestNonTableFirstChild(cellElementToPutCaret);
  return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
}

nsresult HTMLEditor::DeleteTableElementAndChildrenWithTransaction(