From f3b99207af9d32c39bb47f95d240e4b52da5559b Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Thu, 5 Sep 2019 13:37:57 +0000 Subject: [PATCH] Bug 1574852 - part 67-3: Split `HTMLEditRules::WillDeleteSelection()` to 3 methods r=m_kato First, we can split it with 3 parts: 1. preparation with current selection part. 2. handling deletion with collapsed selection part. 3. handling deletion with non-collapsed selection part. The first should leave in `WillDeleteSelection()`, but the others should be moved to independent methods. With this change, we need to fix an odd case when there is no visible node and `GetFirstSelectedTableCellElement()` returned error due to crossing the method boundary. The method returns error only when there is no selection ranges. In such case, we cannot handle deletion of current selection anyway. So, it must not be a problem to handle the error immediately after calling `GetFirstSelectedTableCellElement()`. Note that this shouldn't occur in normal cases since `WillDoAction()` checks it before calling `WillDeleteSelection()`. Differential Revision: https://phabricator.services.mozilla.com/D44453 --HG-- extra : moz-landing-system : lando --- editor/libeditor/HTMLEditRules.cpp | 1007 ++++++++++++++-------------- editor/libeditor/HTMLEditRules.h | 33 + 2 files changed, 552 insertions(+), 488 deletions(-) diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp index 17120e609854..6e66e48d889f 100644 --- a/editor/libeditor/HTMLEditRules.cpp +++ b/editor/libeditor/HTMLEditRules.cpp @@ -2348,9 +2348,8 @@ EditActionResult HTMLEditRules::WillDeleteSelection( // First check for table selection mode. If so, hand off to table editor. ErrorResult error; - RefPtr cellElement = - HTMLEditorRef().GetFirstSelectedTableCellElement(error); - if (cellElement) { + if (RefPtr cellElement = + HTMLEditorRef().GetFirstSelectedTableCellElement(error)) { error.SuppressException(); nsresult rv = MOZ_KnownLive(HTMLEditorRef()).DeleteTableCellContentsWithTransaction(); @@ -2361,18 +2360,26 @@ EditActionResult HTMLEditRules::WillDeleteSelection( "DeleteTableCellContentsWithTransaction() failed"); return EditActionHandled(rv); } - nsresult rv = error.StealNSResult(); - cellElement = nullptr; - // origCollapsed is used later to determine whether we should join blocks. We - // don't really care about bCollapsed because it will be modified by - // ExtendSelectionForDelete later. TryToJoinBlocksWithTransaction() should - // happen if the original selection is collapsed and the cursor is at the end - // of a block element, in which case ExtendSelectionForDelete would always - // make the selection not collapsed. - bool origCollapsed = SelectionRefPtr()->IsCollapsed(); + // Only when there is no selection range, GetFirstSelectedTableCellElement() + // returns error and in this case, anyway we cannot handle delete selection. + // So, let's return error here even though we haven't handled this. + if (NS_WARN_IF(error.Failed())) { + return EditActionResult(error.StealNSResult()); + } - if (origCollapsed) { + // selectionWasCollapsed is used later to determine whether we should join + // blocks in HandleDeleteNonCollapasedSelection(). We don't really care + // about collapsed because it will be modified by ExtendSelectionForDelete() + // later. TryToJoinBlocksWithTransaction() should happen if the original + // selection is collapsed and the cursor is at the end of a block element, + // in which case ExtendSelectionForDelete() would always make the selection + // not collapsed. + SelectionWasCollapsed selectionWasCollapsed = SelectionRefPtr()->IsCollapsed() + ? SelectionWasCollapsed::Yes + : SelectionWasCollapsed::No; + + if (selectionWasCollapsed == SelectionWasCollapsed::Yes) { EditorDOMPoint startPoint(EditorBase::GetStartPoint(*SelectionRefPtr())); if (NS_WARN_IF(!startPoint.IsSet())) { return EditActionResult(NS_ERROR_FAILURE); @@ -2411,7 +2418,8 @@ EditActionResult HTMLEditRules::WillDeleteSelection( AutoSetTemporaryAncestorLimiter autoSetter( HTMLEditorRef(), *SelectionRefPtr(), *startPoint.GetContainer()); - rv = HTMLEditorRef().ExtendSelectionForDelete(&aDirectionAndAmount); + nsresult rv = + HTMLEditorRef().ExtendSelectionForDelete(&aDirectionAndAmount); if (NS_WARN_IF(NS_FAILED(rv))) { return EditActionResult(rv); } @@ -2420,541 +2428,564 @@ EditActionResult HTMLEditRules::WillDeleteSelection( if (aDirectionAndAmount == nsIEditor::eNone) { return EditActionIgnored(); } + + if (SelectionRefPtr()->IsCollapsed()) { + EditActionResult result = HandleDeleteAroundCollapsedSelection( + aDirectionAndAmount, aStripWrappers); + NS_WARNING_ASSERTION(result.Succeeded(), + "HandleDeleteAroundCollapsedSelection() failed"); + return result; + } } - if (SelectionRefPtr()->IsCollapsed()) { - // ExtendSelectionForDelete() won't change the selection. + EditActionResult result = HandleDeleteNonCollapsedSelection( + aDirectionAndAmount, aStripWrappers, selectionWasCollapsed); + NS_WARNING_ASSERTION(result.Succeeded(), + "HandleDeleteNonCollapasedSelection() failed"); + return result; +} - EditorDOMPoint startPoint(EditorBase::GetStartPoint(*SelectionRefPtr())); - if (NS_WARN_IF(!startPoint.IsSet())) { - return EditActionResult(NS_ERROR_FAILURE); - } +EditActionResult HTMLEditRules::HandleDeleteAroundCollapsedSelection( + nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers) { + MOZ_ASSERT(IsEditorDataAvailable()); + MOZ_ASSERT(HTMLEditorRef().IsTopLevelEditSubActionDataAvailable()); + MOZ_ASSERT(SelectionRefPtr()->IsCollapsed()); + MOZ_ASSERT(aDirectionAndAmount != nsIEditor::eNone); - // What's in the direction we are deleting? - WSRunObject wsObj(&HTMLEditorRef(), startPoint); - nsCOMPtr visibleNode; - int32_t visibleNodeOffset; - WSType wsType; + EditorDOMPoint startPoint(EditorBase::GetStartPoint(*SelectionRefPtr())); + if (NS_WARN_IF(!startPoint.IsSet())) { + return EditActionResult(NS_ERROR_FAILURE); + } - // Find next visible node + // What's in the direction we are deleting? + WSRunObject wsObj(&HTMLEditorRef(), startPoint); + nsCOMPtr visibleNode; + int32_t visibleNodeOffset; + WSType wsType; + + // Find next visible node + if (aDirectionAndAmount == nsIEditor::eNext) { + wsObj.NextVisibleNode(startPoint, address_of(visibleNode), + &visibleNodeOffset, &wsType); + } else { + wsObj.PriorVisibleNode(startPoint, address_of(visibleNode), + &visibleNodeOffset, &wsType); + } + + if (!visibleNode) { + return EditActionCanceled(); + } + + if (wsType == WSType::normalWS) { + // We found some visible ws to delete. Let ws code handle it. if (aDirectionAndAmount == nsIEditor::eNext) { - wsObj.NextVisibleNode(startPoint, address_of(visibleNode), - &visibleNodeOffset, &wsType); + nsresult rv = wsObj.DeleteWSForward(); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionHandled(rv); + } } else { - wsObj.PriorVisibleNode(startPoint, address_of(visibleNode), - &visibleNodeOffset, &wsType); + nsresult rv = wsObj.DeleteWSBackward(); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionHandled(rv); + } } + nsresult rv = MOZ_KnownLive(HTMLEditorRef()) + .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary( + EditorBase::GetStartPoint(*SelectionRefPtr())); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() failed"); + return EditActionHandled(rv); + } - if (!visibleNode) { - // XXX This is the result of GetFirstSelectedTableCellElement(). - // This must be a bug. - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), - "GetFirstSelectedTableCellElement() failed"); - return EditActionCanceled(rv); - } - - if (wsType == WSType::normalWS) { - // We found some visible ws to delete. Let ws code handle it. - if (aDirectionAndAmount == nsIEditor::eNext) { - nsresult rv = wsObj.DeleteWSForward(); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionHandled(rv); - } - } else { - nsresult rv = wsObj.DeleteWSBackward(); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionHandled(rv); + if (wsType == WSType::text) { + // Found normal text to delete. + OwningNonNull visibleTextNode = *visibleNode->GetAsText(); + int32_t startOffset = visibleNodeOffset; + int32_t endOffset = visibleNodeOffset + 1; + if (aDirectionAndAmount == nsIEditor::ePrevious) { + if (!startOffset) { + return EditActionResult(NS_ERROR_UNEXPECTED); + } + startOffset--; + endOffset--; + // Bug 1068979: delete both codepoints if surrogate pair + if (startOffset > 0) { + const nsTextFragment* text = &visibleTextNode->TextFragment(); + if (text->IsLowSurrogateFollowingHighSurrogateAt(startOffset)) { + startOffset--; } } - nsresult rv = - MOZ_KnownLive(HTMLEditorRef()) - .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary( - EditorBase::GetStartPoint(*SelectionRefPtr())); - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv), - "InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() failed"); + } else { + RefPtr range = SelectionRefPtr()->GetRangeAt(0); + if (NS_WARN_IF(!range)) { + return EditActionResult(NS_ERROR_FAILURE); + } + + NS_ASSERTION(range->GetStartContainer() == visibleNode, + "selection start not in visibleNode"); + NS_ASSERTION(range->GetEndContainer() == visibleNode, + "selection end not in visibleNode"); + + startOffset = range->StartOffset(); + endOffset = range->EndOffset(); + } + nsresult rv = WSRunObject::PrepareToDeleteRange( + MOZ_KnownLive(&HTMLEditorRef()), address_of(visibleNode), &startOffset, + address_of(visibleNode), &endOffset); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionResult(rv); + } + rv = MOZ_KnownLive(HTMLEditorRef()) + .DeleteTextWithTransaction(visibleTextNode, + std::min(startOffset, endOffset), + DeprecatedAbs(endOffset - startOffset)); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { return EditActionHandled(rv); } - if (wsType == WSType::text) { - // Found normal text to delete. - OwningNonNull visibleTextNode = *visibleNode->GetAsText(); - int32_t startOffset = visibleNodeOffset; - int32_t endOffset = visibleNodeOffset + 1; - if (aDirectionAndAmount == nsIEditor::ePrevious) { - if (!startOffset) { - return EditActionResult(NS_ERROR_UNEXPECTED); - } - startOffset--; - endOffset--; - // Bug 1068979: delete both codepoints if surrogate pair - if (startOffset > 0) { - const nsTextFragment* text = &visibleTextNode->TextFragment(); - if (text->IsLowSurrogateFollowingHighSurrogateAt(startOffset)) { - startOffset--; - } - } - } else { - RefPtr range = SelectionRefPtr()->GetRangeAt(0); - if (NS_WARN_IF(!range)) { - return EditActionResult(NS_ERROR_FAILURE); - } + // XXX When Backspace key is pressed, Chromium removes following empty + // text nodes when removing the last character of the non-empty text + // node. However, Edge never removes empty text nodes even if + // selection is in the following empty text node(s). For now, we + // should keep our traditional behavior same as Edge for backward + // compatibility. + // XXX When Delete key is pressed, Edge removes all preceding empty + // text nodes when removing the first character of the non-empty + // text node. Chromium removes only selected empty text node and + // following empty text nodes and the first character of the + // non-empty text node. For now, we should keep our traditional + // behavior same as Chromium for backward compatibility. - NS_ASSERTION(range->GetStartContainer() == visibleNode, - "selection start not in visibleNode"); - NS_ASSERTION(range->GetEndContainer() == visibleNode, - "selection end not in visibleNode"); + rv = MOZ_KnownLive(HTMLEditorRef()) + .DeleteNodeIfInvisibleAndEditableTextNode(visibleTextNode); + if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + } + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "DeleteNodeIfInvisibleAndEditableTextNode() failed, but ignored"); - startOffset = range->StartOffset(); - endOffset = range->EndOffset(); - } - nsresult rv = WSRunObject::PrepareToDeleteRange( - MOZ_KnownLive(&HTMLEditorRef()), address_of(visibleNode), - &startOffset, address_of(visibleNode), &endOffset); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionResult(rv); - } - rv = MOZ_KnownLive(HTMLEditorRef()) - .DeleteTextWithTransaction( - visibleTextNode, std::min(startOffset, endOffset), - DeprecatedAbs(endOffset - startOffset)); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionHandled(rv); - } + rv = MOZ_KnownLive(HTMLEditorRef()) + .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary( + EditorBase::GetStartPoint(*SelectionRefPtr())); + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionHandled(rv); + } - // XXX When Backspace key is pressed, Chromium removes following empty - // text nodes when removing the last character of the non-empty text - // node. However, Edge never removes empty text nodes even if - // selection is in the following empty text node(s). For now, we - // should keep our traditional behavior same as Edge for backward - // compatibility. - // XXX When Delete key is pressed, Edge removes all preceding empty - // text nodes when removing the first character of the non-empty - // text node. Chromium removes only selected empty text node and - // following empty text nodes and the first character of the - // non-empty text node. For now, we should keep our traditional - // behavior same as Chromium for backward compatibility. + // Remember that we did a ranged delete for the benefit of + // AfterEditInner(). + HTMLEditorRef().TopLevelEditSubActionDataRef().mDidDeleteNonCollapsedRange = + true; - rv = MOZ_KnownLive(HTMLEditorRef()) - .DeleteNodeIfInvisibleAndEditableTextNode(visibleTextNode); - if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); - } - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv), - "DeleteNodeIfInvisibleAndEditableTextNode() failed, but ignored"); - - rv = MOZ_KnownLive(HTMLEditorRef()) - .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary( - EditorBase::GetStartPoint(*SelectionRefPtr())); - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionHandled(rv); - } - - // Remember that we did a ranged delete for the benefit of - // AfterEditInner(). - HTMLEditorRef() - .TopLevelEditSubActionDataRef() - .mDidDeleteNonCollapsedRange = true; + return EditActionHandled(); + } + if (wsType == WSType::special || wsType == WSType::br || + visibleNode->IsHTMLElement(nsGkAtoms::hr)) { + // If the void element is editing host, we should do nothing. + if (visibleNode == wsObj.GetEditingHost()) { return EditActionHandled(); } - if (wsType == WSType::special || wsType == WSType::br || + // Short circuit for invisible breaks. delete them and recurse. + if (visibleNode->IsHTMLElement(nsGkAtoms::br) && + !HTMLEditorRef().IsVisibleBRElement(visibleNode)) { + nsresult rv = MOZ_KnownLive(HTMLEditorRef()) + .DeleteNodeWithTransaction(*visibleNode); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionResult(rv); + } + EditActionResult result = + WillDeleteSelection(aDirectionAndAmount, aStripWrappers); + NS_WARNING_ASSERTION(result.Succeeded(), + "Nested WillDeleteSelection() failed"); + return result; + } + + // Special handling for backspace when positioned after
+ if (aDirectionAndAmount == nsIEditor::ePrevious && visibleNode->IsHTMLElement(nsGkAtoms::hr)) { - // If the void element is editing host, we should do nothing. - if (visibleNode == wsObj.GetEditingHost()) { - return EditActionHandled(); + // Only if the caret is positioned at the end-of-hr-line position, we + // want to delete the
. + // + // In other words, we only want to delete, if our selection position + // (indicated by startPoint) is the position directly + // after the
, on the same line as the
. + // + // To detect this case we check: + // startPoint's container == parentOfVisNode + // and + // startPoint's offset -1 == visibleNodeOffsetToVisNodeParent + // and + // interline position is false (left) + // + // In any other case we set the position to startPoint's container -1 + // and interlineposition to false, only moving the caret to the + // end-of-hr-line position. + bool moveOnly = true; + + EditorRawDOMPoint atHRElement(visibleNode); + + ErrorResult err; + bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(err); + if (NS_WARN_IF(err.Failed())) { + return EditActionResult(err.StealNSResult()); } - // Short circuit for invisible breaks. delete them and recurse. - if (visibleNode->IsHTMLElement(nsGkAtoms::br) && - !HTMLEditorRef().IsVisibleBRElement(visibleNode)) { - nsresult rv = MOZ_KnownLive(HTMLEditorRef()) - .DeleteNodeWithTransaction(*visibleNode); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionResult(rv); - } - EditActionResult result = - WillDeleteSelection(aDirectionAndAmount, aStripWrappers); - NS_WARNING_ASSERTION(result.Succeeded(), - "Nested WillDeleteSelection() failed"); - return result; + if (startPoint.GetContainer() == atHRElement.GetContainer() && + startPoint.Offset() - 1 == atHRElement.Offset() && + !interLineIsRight) { + moveOnly = false; } - // Special handling for backspace when positioned after
- if (aDirectionAndAmount == nsIEditor::ePrevious && - visibleNode->IsHTMLElement(nsGkAtoms::hr)) { - // Only if the caret is positioned at the end-of-hr-line position, we - // want to delete the
. - // - // In other words, we only want to delete, if our selection position - // (indicated by startPoint) is the position directly - // after the
, on the same line as the
. - // - // To detect this case we check: - // startPoint's container == parentOfVisNode - // and - // startPoint's offset -1 == visibleNodeOffsetToVisNodeParent - // and - // interline position is false (left) - // - // In any other case we set the position to startPoint's container -1 - // and interlineposition to false, only moving the caret to the - // end-of-hr-line position. - bool moveOnly = true; + if (moveOnly) { + // Go to the position after the
, but to the end of the
line + // by setting the interline position to left. + EditorDOMPoint atNextOfHRElement(visibleNode); + DebugOnly advanced = atNextOfHRElement.AdvanceOffset(); + NS_WARNING_ASSERTION(advanced, + "Failed to advance offset after
element"); - EditorRawDOMPoint atHRElement(visibleNode); - - ErrorResult err; - bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(err); - if (NS_WARN_IF(err.Failed())) { - return EditActionResult(err.StealNSResult()); - } - - if (startPoint.GetContainer() == atHRElement.GetContainer() && - startPoint.Offset() - 1 == atHRElement.Offset() && - !interLineIsRight) { - moveOnly = false; - } - - if (moveOnly) { - // Go to the position after the
, but to the end of the
line - // by setting the interline position to left. - EditorDOMPoint atNextOfHRElement(visibleNode); - DebugOnly advanced = atNextOfHRElement.AdvanceOffset(); - NS_WARNING_ASSERTION(advanced, - "Failed to advance offset after
element"); - - { - AutoEditorDOMPointChildInvalidator lockOffset(atNextOfHRElement); - - IgnoredErrorResult ignoredError; - SelectionRefPtr()->Collapse(atNextOfHRElement, ignoredError); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); - } - NS_WARNING_ASSERTION( - !ignoredError.Failed(), - "Failed to collapse selection at after the
"); - } + { + AutoEditorDOMPointChildInvalidator lockOffset(atNextOfHRElement); IgnoredErrorResult ignoredError; - SelectionRefPtr()->SetInterlinePosition(false, ignoredError); - NS_WARNING_ASSERTION(!ignoredError.Failed(), - "Failed to unset interline position"); - HTMLEditorRef() - .TopLevelEditSubActionDataRef() - .mDidExplicitlySetInterLine = true; - - // There is one exception to the move only case. If the
is - // followed by a
we want to delete the
. - - WSType otherWSType; - nsCOMPtr otherNode; - - wsObj.NextVisibleNode(startPoint, address_of(otherNode), nullptr, - &otherWSType); - - if (otherWSType != WSType::br) { - return EditActionHandled(); - } - - // Delete the
- if (NS_WARN_IF(!otherNode->IsContent())) { - return EditActionHandled(NS_ERROR_FAILURE); - } - nsIContent* otherContent = otherNode->AsContent(); - nsresult rv = WSRunObject::PrepareToDeleteNode( - MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(otherContent)); + SelectionRefPtr()->Collapse(atNextOfHRElement, ignoredError); if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionHandled(rv); - } - rv = MOZ_KnownLive(HTMLEditorRef()) - .DeleteNodeWithTransaction(MOZ_KnownLive(*otherContent)); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); - } - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), - "DeleteNodeWithTransaction() failed"); - return EditActionHandled(rv); + NS_WARNING_ASSERTION( + !ignoredError.Failed(), + "Failed to collapse selection at after the
"); } - // Else continue with normal delete code - } - if (NS_WARN_IF(!visibleNode->IsContent())) { - return EditActionResult(NS_ERROR_FAILURE); - } - // Found break or image, or hr. - nsresult rv = WSRunObject::PrepareToDeleteNode( - MOZ_KnownLive(&HTMLEditorRef()), - MOZ_KnownLive(visibleNode->AsContent())); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionResult(rv); - } - // Remember sibling to visnode, if any - nsCOMPtr previousEditableSibling = - HTMLEditorRef().GetPriorHTMLSibling(visibleNode); - // Delete the node, and join like nodes if appropriate - rv = MOZ_KnownLive(HTMLEditorRef()) - .DeleteNodeWithTransaction(*visibleNode); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionResult(rv); - } - // Is there a prior node and are they siblings? - nsCOMPtr nextEditableSibling; - if (previousEditableSibling) { - nextEditableSibling = - HTMLEditorRef().GetNextHTMLSibling(previousEditableSibling); - } - // Are they both text nodes? If so, join them! - if (startPoint.GetContainer() == nextEditableSibling && - startPoint.GetContainerAsText() && - previousEditableSibling->GetAsText()) { - EditorDOMPoint atFirstChildOfRightNode; - nsresult rv = - MOZ_KnownLive(HTMLEditorRef()) - .JoinNearestEditableNodesWithTransaction( - *previousEditableSibling, - MOZ_KnownLive(*startPoint.GetContainerAsContent()), - &atFirstChildOfRightNode); - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionHandled(rv); - } - if (NS_WARN_IF(!atFirstChildOfRightNode.IsSet())) { - return EditActionHandled(NS_ERROR_FAILURE); - } - // Fix up selection - ErrorResult error; - SelectionRefPtr()->Collapse(atFirstChildOfRightNode, error); - if (NS_WARN_IF(!CanHandleEditAction())) { - error.SuppressException(); - return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_WARN_IF(error.Failed())) { - return EditActionHandled(error.StealNSResult()); - } - } - rv = MOZ_KnownLive(HTMLEditorRef()) - .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary( - EditorBase::GetStartPoint(*SelectionRefPtr())); - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv), - "InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() failed"); - return EditActionHandled(rv); - } + IgnoredErrorResult ignoredError; + SelectionRefPtr()->SetInterlinePosition(false, ignoredError); + NS_WARNING_ASSERTION(!ignoredError.Failed(), + "Failed to unset interline position"); + HTMLEditorRef() + .TopLevelEditSubActionDataRef() + .mDidExplicitlySetInterLine = true; - if (wsType == WSType::otherBlock) { - // Make sure it's not a table element. If so, cancel the operation - // (translation: users cannot backspace or delete across table cells) - if (HTMLEditUtils::IsTableElement(visibleNode)) { - return EditActionCanceled(); - } + // There is one exception to the move only case. If the
is + // followed by a
we want to delete the
. - // Next to a block. See if we are between a block and a br. If so, we - // really want to delete the br. Else join content at selection to the - // block. - WSType otherWSType; - nsCOMPtr otherNode; + WSType otherWSType; + nsCOMPtr otherNode; - // Find node in other direction - if (aDirectionAndAmount == nsIEditor::eNext) { - wsObj.PriorVisibleNode(startPoint, address_of(otherNode), nullptr, - &otherWSType); - } else { wsObj.NextVisibleNode(startPoint, address_of(otherNode), nullptr, &otherWSType); - } - // First find the adjacent node in the block - nsCOMPtr leafNode; - nsCOMPtr leftNode, rightNode; - if (aDirectionAndAmount == nsIEditor::ePrevious) { - leafNode = HTMLEditorRef().GetLastEditableLeaf(*visibleNode); - leftNode = leafNode; - rightNode = startPoint.GetContainer(); - } else { - leafNode = HTMLEditorRef().GetFirstEditableLeaf(*visibleNode); - leftNode = startPoint.GetContainer(); - rightNode = leafNode; - } - - bool didBRElementDeleted = false; - if (otherNode->IsHTMLElement(nsGkAtoms::br)) { - nsresult rv = MOZ_KnownLive(HTMLEditorRef()) - .DeleteNodeWithTransaction(*otherNode); - if (NS_WARN_IF(!CanHandleEditAction())) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + if (otherWSType != WSType::br) { + return EditActionHandled(); } - if (NS_WARN_IF(NS_FAILED(rv))) { - return EditActionResult(rv); - } - didBRElementDeleted = true; - } - // Don't cross table boundaries - if (leftNode && rightNode && - HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) { - return didBRElementDeleted ? EditActionHandled() : EditActionIgnored(); - } - - if (didBRElementDeleted) { - // Put selection at edge of block and we are done. - if (NS_WARN_IF(!leafNode)) { + // Delete the
+ if (NS_WARN_IF(!otherNode->IsContent())) { return EditActionHandled(NS_ERROR_FAILURE); } - EditorDOMPoint newSel = HTMLEditorRef().GetGoodCaretPointFor( - *leafNode, aDirectionAndAmount); - if (NS_WARN_IF(!newSel.IsSet())) { - return EditActionHandled(NS_ERROR_FAILURE); - } - IgnoredErrorResult error; - SelectionRefPtr()->Collapse(newSel, error); + nsIContent* otherContent = otherNode->AsContent(); + nsresult rv = WSRunObject::PrepareToDeleteNode( + MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(otherContent)); if (NS_WARN_IF(!CanHandleEditAction())) { return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); } - NS_WARNING_ASSERTION(!error.Failed(), - "Selection::Collapse() failed, but ignored"); - return EditActionHandled(); - } - - // Else we are joining content to block - EditActionResult result(NS_OK); - EditorDOMPoint pointToPutCaret(startPoint); - { - AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), - &pointToPutCaret); - if (NS_WARN_IF(!leftNode) || NS_WARN_IF(!leftNode->IsContent()) || - NS_WARN_IF(!rightNode) || NS_WARN_IF(!rightNode->IsContent())) { - return EditActionResult(NS_ERROR_FAILURE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionHandled(rv); } - result |= MOZ_KnownLive(HTMLEditorRef()) - .TryToJoinBlocksWithTransaction( - MOZ_KnownLive(*leftNode->AsContent()), - MOZ_KnownLive(*rightNode->AsContent())); - if (NS_WARN_IF(result.Failed())) { - return result; - } - } - - // If TryToJoinBlocksWithTransaction() didn't handle it and it's not - // canceled, user may want to modify the start leaf node or the last leaf - // node of the block. - if (!result.Handled() && !result.Canceled() && - leafNode != startPoint.GetContainer()) { - int32_t offset = aDirectionAndAmount == nsIEditor::ePrevious - ? static_cast(leafNode->Length()) - : 0; - DebugOnly rv = SelectionRefPtr()->Collapse(leafNode, offset); + rv = MOZ_KnownLive(HTMLEditorRef()) + .DeleteNodeWithTransaction(MOZ_KnownLive(*otherContent)); if (NS_WARN_IF(!CanHandleEditAction())) { - return result.SetResult(NS_ERROR_EDITOR_DESTROYED); + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); } NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), - "Selection::Collapse() failed, but ignored"); - EditActionResult result = - WillDeleteSelection(aDirectionAndAmount, aStripWrappers); - NS_WARNING_ASSERTION(result.Succeeded(), - "Nested WillDeleteSelection() failed"); + "DeleteNodeWithTransaction() failed"); + return EditActionHandled(rv); + } + // Else continue with normal delete code + } + + if (NS_WARN_IF(!visibleNode->IsContent())) { + return EditActionResult(NS_ERROR_FAILURE); + } + // Found break or image, or hr. + nsresult rv = WSRunObject::PrepareToDeleteNode( + MOZ_KnownLive(&HTMLEditorRef()), + MOZ_KnownLive(visibleNode->AsContent())); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionResult(rv); + } + // Remember sibling to visnode, if any + nsCOMPtr previousEditableSibling = + HTMLEditorRef().GetPriorHTMLSibling(visibleNode); + // Delete the node, and join like nodes if appropriate + rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*visibleNode); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionResult(rv); + } + // Is there a prior node and are they siblings? + nsCOMPtr nextEditableSibling; + if (previousEditableSibling) { + nextEditableSibling = + HTMLEditorRef().GetNextHTMLSibling(previousEditableSibling); + } + // Are they both text nodes? If so, join them! + if (startPoint.GetContainer() == nextEditableSibling && + startPoint.GetContainerAsText() && + previousEditableSibling->GetAsText()) { + EditorDOMPoint atFirstChildOfRightNode; + nsresult rv = MOZ_KnownLive(HTMLEditorRef()) + .JoinNearestEditableNodesWithTransaction( + *previousEditableSibling, + MOZ_KnownLive(*startPoint.GetContainerAsContent()), + &atFirstChildOfRightNode); + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionHandled(rv); + } + if (NS_WARN_IF(!atFirstChildOfRightNode.IsSet())) { + return EditActionHandled(NS_ERROR_FAILURE); + } + // Fix up selection + ErrorResult error; + SelectionRefPtr()->Collapse(atFirstChildOfRightNode, error); + if (NS_WARN_IF(!CanHandleEditAction())) { + error.SuppressException(); + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(error.Failed())) { + return EditActionHandled(error.StealNSResult()); + } + } + rv = MOZ_KnownLive(HTMLEditorRef()) + .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary( + EditorBase::GetStartPoint(*SelectionRefPtr())); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() failed"); + return EditActionHandled(rv); + } + + if (wsType == WSType::otherBlock) { + // Make sure it's not a table element. If so, cancel the operation + // (translation: users cannot backspace or delete across table cells) + if (HTMLEditUtils::IsTableElement(visibleNode)) { + return EditActionCanceled(); + } + + // Next to a block. See if we are between a block and a br. If so, we + // really want to delete the br. Else join content at selection to the + // block. + WSType otherWSType; + nsCOMPtr otherNode; + + // Find node in other direction + if (aDirectionAndAmount == nsIEditor::eNext) { + wsObj.PriorVisibleNode(startPoint, address_of(otherNode), nullptr, + &otherWSType); + } else { + wsObj.NextVisibleNode(startPoint, address_of(otherNode), nullptr, + &otherWSType); + } + + // First find the adjacent node in the block + nsCOMPtr leafNode; + nsCOMPtr leftNode, rightNode; + if (aDirectionAndAmount == nsIEditor::ePrevious) { + leafNode = HTMLEditorRef().GetLastEditableLeaf(*visibleNode); + leftNode = leafNode; + rightNode = startPoint.GetContainer(); + } else { + leafNode = HTMLEditorRef().GetFirstEditableLeaf(*visibleNode); + leftNode = startPoint.GetContainer(); + rightNode = leafNode; + } + + bool didBRElementDeleted = false; + if (otherNode->IsHTMLElement(nsGkAtoms::br)) { + nsresult rv = + MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*otherNode); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return EditActionResult(rv); + } + didBRElementDeleted = true; + } + + // Don't cross table boundaries + if (leftNode && rightNode && + HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) { + return didBRElementDeleted ? EditActionHandled() : EditActionIgnored(); + } + + if (didBRElementDeleted) { + // Put selection at edge of block and we are done. + if (NS_WARN_IF(!leafNode)) { + return EditActionHandled(NS_ERROR_FAILURE); + } + EditorDOMPoint newSel = + HTMLEditorRef().GetGoodCaretPointFor(*leafNode, aDirectionAndAmount); + if (NS_WARN_IF(!newSel.IsSet())) { + return EditActionHandled(NS_ERROR_FAILURE); + } + IgnoredErrorResult error; + SelectionRefPtr()->Collapse(newSel, error); + if (NS_WARN_IF(!CanHandleEditAction())) { + return EditActionHandled(NS_ERROR_EDITOR_DESTROYED); + } + NS_WARNING_ASSERTION(!error.Failed(), + "Selection::Collapse() failed, but ignored"); + return EditActionHandled(); + } + + // Else we are joining content to block + EditActionResult result(NS_OK); + EditorDOMPoint pointToPutCaret(startPoint); + { + AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), + &pointToPutCaret); + if (NS_WARN_IF(!leftNode) || NS_WARN_IF(!leftNode->IsContent()) || + NS_WARN_IF(!rightNode) || NS_WARN_IF(!rightNode->IsContent())) { + return EditActionResult(NS_ERROR_FAILURE); + } + result |= MOZ_KnownLive(HTMLEditorRef()) + .TryToJoinBlocksWithTransaction( + MOZ_KnownLive(*leftNode->AsContent()), + MOZ_KnownLive(*rightNode->AsContent())); + if (NS_WARN_IF(result.Failed())) { return result; } + } - // Otherwise, we must have deleted the selection as user expected. - IgnoredErrorResult ignoredError; - SelectionRefPtr()->Collapse(pointToPutCaret, ignoredError); + // If TryToJoinBlocksWithTransaction() didn't handle it and it's not + // canceled, user may want to modify the start leaf node or the last leaf + // node of the block. + if (!result.Handled() && !result.Canceled() && + leafNode != startPoint.GetContainer()) { + int32_t offset = aDirectionAndAmount == nsIEditor::ePrevious + ? static_cast(leafNode->Length()) + : 0; + DebugOnly rv = SelectionRefPtr()->Collapse(leafNode, offset); if (NS_WARN_IF(!CanHandleEditAction())) { return result.SetResult(NS_ERROR_EDITOR_DESTROYED); } - NS_WARNING_ASSERTION(!ignoredError.Failed(), - "Failed to selection at deleted point"); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Selection::Collapse() failed, but ignored"); + EditActionResult result = + WillDeleteSelection(aDirectionAndAmount, aStripWrappers); + NS_WARNING_ASSERTION(result.Succeeded(), + "Nested WillDeleteSelection() failed"); return result; } - if (wsType == WSType::thisBlock) { - // At edge of our block. Look beside it and see if we can join to an - // adjacent block - - // Make sure it's not a table element. If so, cancel the operation - // (translation: users cannot backspace or delete across table cells) - if (HTMLEditUtils::IsTableElement(visibleNode)) { - return EditActionCanceled(); - } - - // First find the relevant nodes - nsCOMPtr leftNode, rightNode; - if (aDirectionAndAmount == nsIEditor::ePrevious) { - leftNode = HTMLEditorRef().GetPreviousEditableHTMLNode(*visibleNode); - rightNode = startPoint.GetContainer(); - } else { - rightNode = HTMLEditorRef().GetNextEditableHTMLNode(*visibleNode); - leftNode = startPoint.GetContainer(); - } - - // Nothing to join - if (!leftNode || !rightNode) { - return EditActionCanceled(); - } - - // Don't cross table boundaries -- cancel it - if (HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) { - return EditActionCanceled(); - } - - EditActionResult result(NS_OK); - EditorDOMPoint pointToPutCaret(startPoint); - { - AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), - &pointToPutCaret); - if (NS_WARN_IF(!leftNode->IsContent()) || - NS_WARN_IF(!rightNode->IsContent())) { - return EditActionResult(NS_ERROR_FAILURE); - } - result |= MOZ_KnownLive(HTMLEditorRef()) - .TryToJoinBlocksWithTransaction( - MOZ_KnownLive(*leftNode->AsContent()), - MOZ_KnownLive(*rightNode->AsContent())); - // This should claim that trying to join the block means that - // this handles the action because the caller shouldn't do anything - // anymore in this case. - result.MarkAsHandled(); - if (NS_WARN_IF(result.Failed())) { - return result; - } - } - IgnoredErrorResult ignoredError; - SelectionRefPtr()->Collapse(pointToPutCaret, ignoredError); - if (NS_WARN_IF(!CanHandleEditAction())) { - return result.SetResult(NS_ERROR_EDITOR_DESTROYED); - } - NS_WARNING_ASSERTION(!ignoredError.Failed(), - "Failed to collapse selection"); - return result; + // Otherwise, we must have deleted the selection as user expected. + IgnoredErrorResult ignoredError; + SelectionRefPtr()->Collapse(pointToPutCaret, ignoredError); + if (NS_WARN_IF(!CanHandleEditAction())) { + return result.SetResult(NS_ERROR_EDITOR_DESTROYED); } + NS_WARNING_ASSERTION(!ignoredError.Failed(), + "Failed to selection at deleted point"); + return result; } + if (wsType == WSType::thisBlock) { + // At edge of our block. Look beside it and see if we can join to an + // adjacent block + + // Make sure it's not a table element. If so, cancel the operation + // (translation: users cannot backspace or delete across table cells) + if (HTMLEditUtils::IsTableElement(visibleNode)) { + return EditActionCanceled(); + } + + // First find the relevant nodes + nsCOMPtr leftNode, rightNode; + if (aDirectionAndAmount == nsIEditor::ePrevious) { + leftNode = HTMLEditorRef().GetPreviousEditableHTMLNode(*visibleNode); + rightNode = startPoint.GetContainer(); + } else { + rightNode = HTMLEditorRef().GetNextEditableHTMLNode(*visibleNode); + leftNode = startPoint.GetContainer(); + } + + // Nothing to join + if (!leftNode || !rightNode) { + return EditActionCanceled(); + } + + // Don't cross table boundaries -- cancel it + if (HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) { + return EditActionCanceled(); + } + + EditActionResult result(NS_OK); + EditorDOMPoint pointToPutCaret(startPoint); + { + AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), + &pointToPutCaret); + if (NS_WARN_IF(!leftNode->IsContent()) || + NS_WARN_IF(!rightNode->IsContent())) { + return EditActionResult(NS_ERROR_FAILURE); + } + result |= MOZ_KnownLive(HTMLEditorRef()) + .TryToJoinBlocksWithTransaction( + MOZ_KnownLive(*leftNode->AsContent()), + MOZ_KnownLive(*rightNode->AsContent())); + // This should claim that trying to join the block means that + // this handles the action because the caller shouldn't do anything + // anymore in this case. + result.MarkAsHandled(); + if (NS_WARN_IF(result.Failed())) { + return result; + } + } + IgnoredErrorResult ignoredError; + SelectionRefPtr()->Collapse(pointToPutCaret, ignoredError); + if (NS_WARN_IF(!CanHandleEditAction())) { + return result.SetResult(NS_ERROR_EDITOR_DESTROYED); + } + NS_WARNING_ASSERTION(!ignoredError.Failed(), + "Failed to collapse selection"); + return result; + } + + MOZ_ASSERT_UNREACHABLE("New WSType value hasn't been handled yet"); + return EditActionIgnored(); +} + +EditActionResult HTMLEditRules::HandleDeleteNonCollapsedSelection( + nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers, + SelectionWasCollapsed aSelectionWasCollapsed) { + MOZ_ASSERT(IsEditorDataAvailable()); + MOZ_ASSERT(HTMLEditorRef().IsTopLevelEditSubActionDataAvailable()); + MOZ_ASSERT(!SelectionRefPtr()->IsCollapsed()); + // Else we have a non-collapsed selection. First adjust the selection. // XXX Why do we extend selection only when there is only one range? if (SelectionRefPtr()->RangeCount() == 1) { @@ -3141,7 +3172,7 @@ EditActionResult HTMLEditRules::WillDeleteSelection( arrayOfNodes.RemoveElementAt(0); // If something visible is deleted, no need to join. Visible means // all nodes except non-visible textnodes and breaks. - if (join && origCollapsed) { + if (join && aSelectionWasCollapsed == SelectionWasCollapsed::Yes) { if (!node->IsContent()) { join = false; continue; @@ -3278,7 +3309,7 @@ EditActionResult HTMLEditRules::WillDeleteSelection( return result.SetResult(rv); } - rv = SelectionRefPtr()->Collapse(startNode, startOffset); + nsresult rv = SelectionRefPtr()->Collapse(startNode, startOffset); if (NS_WARN_IF(!CanHandleEditAction())) { return result.SetResult(NS_ERROR_EDITOR_DESTROYED); } diff --git a/editor/libeditor/HTMLEditRules.h b/editor/libeditor/HTMLEditRules.h index c73a05aa0e60..6dfb5f16b862 100644 --- a/editor/libeditor/HTMLEditRules.h +++ b/editor/libeditor/HTMLEditRules.h @@ -129,6 +129,39 @@ class HTMLEditRules : public TextEditRules { WillDeleteSelection(nsIEditor::EDirection aDirectionAndAmount, nsIEditor::EStripWrappers aStripWrappers); + /** + * HandleDeleteAroundCollapsedSelection() handles deletion with collapsed + * `Selection`. Callers must guarantee that this is called only when + * `Selection` is collapsed. + * + * @param aDirectionAndAmount Direction of the deletion. + * @param aStripWrappers Must be eStrip or eNoStrip. + */ + MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult + HandleDeleteAroundCollapsedSelection( + nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers); + + /** + * HandleDeleteNonCollapsedSelection() handles deletion with non-collapsed + * `Selection`. Callers must guarantee that this is called only when + * `Selection` is NOT collapsed. + * + * @param aDirectionAndAmount Direction of the deletion. + * @param aStripWrappers Must be eStrip or eNoStrip. + * @param aSelectionWasCollpased If the caller extended `Selection` + * from collapsed, set this to `Yes`. + * Otherwise, i.e., `Selection` is not + * collapsed from the beginning, set + * this to `No`. + */ + enum class SelectionWasCollapsed { Yes, No }; + MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult + HandleDeleteNonCollapsedSelection( + nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers, + SelectionWasCollapsed aSelectionWasCollapsed); + /** * Called after deleting selected content. * This method removes unnecessary empty nodes and/or inserts
if