diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp index f9662665065f..150095519350 100644 --- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -2678,8 +2678,7 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedRanges( } if (aScanFromCaretPointResult.ReachedSpecialContent() || - aScanFromCaretPointResult.ReachedBRElement() || - aScanFromCaretPointResult.ReachedHRElement()) { + aScanFromCaretPointResult.ReachedBRElement()) { if (aScanFromCaretPointResult.GetContent() == aWSRunScannerAtCaret.GetEditingHost()) { return EditActionHandled(); @@ -2694,6 +2693,21 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedRanges( return result; } + if (aScanFromCaretPointResult.ReachedHRElement()) { + if (aScanFromCaretPointResult.GetContent() == + aWSRunScannerAtCaret.GetEditingHost()) { + return EditActionHandled(); + } + EditActionResult result = HandleDeleteCollapsedSelectionAtHRElement( + aDirectionAndAmount, aStripWrappers, + MOZ_KnownLive(*aScanFromCaretPointResult.ElementPtr()), + aWSRunScannerAtCaret.ScanStartRef(), aWSRunScannerAtCaret); + NS_WARNING_ASSERTION( + result.Succeeded(), + "HTMLEditor::HandleDeleteCollapsedSelectionAtHRElement() failed"); + return result; + } + if (aScanFromCaretPointResult.ReachedOtherBlockElement()) { if (NS_WARN_IF(!aScanFromCaretPointResult.GetContent()->IsElement())) { return EditActionResult(NS_ERROR_FAILURE); @@ -2978,6 +2992,107 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtVisibleChar( return EditActionHandled(); } +EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtHRElement( + nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers, Element& aHRElement, + const EditorDOMPoint& aCaretPoint, + const WSRunScanner& aWSRunScannerAtCaret) { + MOZ_ASSERT(IsEditActionDataAvailable()); + MOZ_ASSERT(aHRElement.IsHTMLElement(nsGkAtoms::hr)); + MOZ_ASSERT(&aHRElement != aWSRunScannerAtCaret.GetEditingHost()); + + if (aDirectionAndAmount != nsIEditor::ePrevious) { + EditActionResult result = HandleDeleteCollapsedSelectionAtAtomicContent( + aDirectionAndAmount, aStripWrappers, aHRElement, aCaretPoint, + aWSRunScannerAtCaret); + NS_WARNING_ASSERTION( + result.Succeeded(), + "HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent() failed"); + return result; + } + + // 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 aCaretPoint) is the position directly + // after the
, on the same line as the
. + // + // To detect this case we check: + // aCaretPoint's container == parent of `
` element + // and + // aCaretPoint's offset -1 == `
` element offset + // and + // interline position is false (left) + // + // In any other case we set the position to aCaretPoint's container -1 + // and interlineposition to false, only moving the caret to the + // end-of-hr-line position. + EditorRawDOMPoint atHRElement(&aHRElement); + + ErrorResult error; + bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(error); + if (error.Failed()) { + NS_WARNING("Selection::GetInterlinePosition() failed"); + return EditActionResult(error.StealNSResult()); + } + + if (aCaretPoint.GetContainer() == atHRElement.GetContainer() && + aCaretPoint.Offset() - 1 == atHRElement.Offset() && !interLineIsRight) { + EditActionResult result = HandleDeleteCollapsedSelectionAtAtomicContent( + aDirectionAndAmount, aStripWrappers, aHRElement, aCaretPoint, + aWSRunScannerAtCaret); + NS_WARNING_ASSERTION( + result.Succeeded(), + "HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent() failed"); + return result; + } + + // Go to the position after the
, but to the end of the
line + // by setting the interline position to left. + EditorDOMPoint atNextOfHRElement(EditorDOMPoint::After(aHRElement)); + NS_WARNING_ASSERTION(atNextOfHRElement.IsSet(), + "Failed to set after
element"); + + { + AutoEditorDOMPointChildInvalidator lockOffset(atNextOfHRElement); + + nsresult rv = CollapseSelectionTo(atNextOfHRElement); + if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { + return EditActionResult(NS_ERROR_EDITOR_DESTROYED); + } + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "HTMLEditor::CollapseSelectionTo() failed, but ignored"); + } + + IgnoredErrorResult ignoredError; + SelectionRefPtr()->SetInterlinePosition(false, ignoredError); + NS_WARNING_ASSERTION( + !ignoredError.Failed(), + "Selection::SetInterlinePosition(false) failed, but ignored"); + TopLevelEditSubActionDataRef().mDidExplicitlySetInterLine = true; + + // There is one exception to the move only case. If the
is + // followed by a
we want to delete the
. + + WSScanResult forwardScanFromCaretResult = + aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(aCaretPoint); + if (!forwardScanFromCaretResult.ReachedBRElement()) { + return EditActionHandled(); + } + + // Delete the
+ nsresult rv = + WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( + *this, MOZ_KnownLive(*forwardScanFromCaretResult.BRElementPtr()), + aCaretPoint); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "WhiteSpaceVisibilityKeeper::" + "DeleteContentNodeAndJoinTextNodesAroundIt() failed"); + return EditActionHandled(rv); +} + EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent( nsIEditor::EDirection aDirectionAndAmount, nsIEditor::EStripWrappers aStripWrappers, nsIContent& aAtomicContent, @@ -2988,92 +3103,6 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent( IsVisibleBRElement(&aAtomicContent)); MOZ_ASSERT(&aAtomicContent != aWSRunScannerAtCaret.GetEditingHost()); - // Special handling for backspace when positioned after
- if (aDirectionAndAmount == nsIEditor::ePrevious && - aAtomicContent.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 aCaretPoint) is the position directly - // after the
, on the same line as the
. - // - // To detect this case we check: - // aCaretPoint's container == parent of `
` element - // and - // aCaretPoint's offset -1 == `
` element offset - // and - // interline position is false (left) - // - // In any other case we set the position to aCaretPoint's container -1 - // and interlineposition to false, only moving the caret to the - // end-of-hr-line position. - bool moveOnly = true; - - EditorRawDOMPoint atHRElement(&aAtomicContent); - - ErrorResult error; - bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(error); - if (error.Failed()) { - NS_WARNING("Selection::GetInterlinePosition() failed"); - return EditActionResult(error.StealNSResult()); - } - - if (aCaretPoint.GetContainer() == atHRElement.GetContainer() && - aCaretPoint.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(EditorDOMPoint::After(aAtomicContent)); - NS_WARNING_ASSERTION(atNextOfHRElement.IsSet(), - "Failed to set after
element"); - - { - AutoEditorDOMPointChildInvalidator lockOffset(atNextOfHRElement); - - nsresult rv = CollapseSelectionTo(atNextOfHRElement); - if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { - return EditActionResult(NS_ERROR_EDITOR_DESTROYED); - } - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv), - "HTMLEditor::CollapseSelectionTo() failed, but ignored"); - } - - IgnoredErrorResult ignoredError; - SelectionRefPtr()->SetInterlinePosition(false, ignoredError); - NS_WARNING_ASSERTION( - !ignoredError.Failed(), - "Selection::SetInterlinePosition(false) failed, but ignored"); - TopLevelEditSubActionDataRef().mDidExplicitlySetInterLine = true; - - // There is one exception to the move only case. If the
is - // followed by a
we want to delete the
. - - WSScanResult forwardScanFromCaretResult = - aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom( - aCaretPoint); - if (!forwardScanFromCaretResult.ReachedBRElement()) { - return EditActionHandled(); - } - - // Delete the
- nsresult rv = - WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( - *this, MOZ_KnownLive(*forwardScanFromCaretResult.BRElementPtr()), - aCaretPoint); - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv), - "WhiteSpaceVisibilityKeeper::" - "DeleteContentNodeAndJoinTextNodesAroundIt() failed"); - return EditActionHandled(rv); - } - // Else continue with normal delete code - } - nsresult rv = WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( *this, aAtomicContent, aCaretPoint); diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index f47481f03fd8..7e44a8697051 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -2603,8 +2603,9 @@ class HTMLEditor final : public TextEditor, * HandleDeleteCollapsedSelectionAtAtomicContent() handles deletion of * atomic elements like `
`, `
`, ``, ``, etc and * data nodes except text node (e.g., comment node). - * If aAtomicContent is a invisible `
` element, this will call - * `HandleDeleteSelectionInternal()` recursively after deleting it. + * Note that don't call this directly with `
` element. Instead, call + * `HandleDeleteCollapsedSelectionAtHRElement()`. + * Note that don't call this for invisible `
` element. * * @param aDirectionAndAmount Direction of the deletion. * @param aStripWrappers Must be eStrip or eNoStrip. @@ -2621,6 +2622,31 @@ class HTMLEditor final : public TextEditor, const EditorDOMPoint& aCaretPoint, const WSRunScanner& aWSRunScannerAtCaret); + /** + * HandleDeleteCollapsedSelectionAtHRElement() handles deletion around + * `
` element. If aDirectionAndAmount is nsIEditor::ePrevious, + * aHTElement is removed only when caret is at next sibling of the `
` + * element and inter line position is "left". Otherwise, caret is moved + * and does not remove the `
` elemnent. + * XXX Perhaps, we can get rid of this special handling because the other + * browsers don't do this, and our `
` element handling is really + * odd. + * + * @param aDirectionAndAmount Direction of the deletion. + * @param aStripWrappers Must be eStrip or eNoStrip. + * @param aHRElement The `
` element to be removed. + * @param aCaretPoint The caret point (i.e., selection start or + * end). + * @param aWSRunScannerAtCaret WSRunScanner instance which was initialized + * with the caret point. + */ + [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult + HandleDeleteCollapsedSelectionAtHRElement( + nsIEditor::EDirection aDirectionAndAmount, + nsIEditor::EStripWrappers aStripWrappers, Element& aHRElement, + const EditorDOMPoint& aCaretPoint, + const WSRunScanner& aWSRunScannerAtCaret); + class MOZ_STACK_CLASS AutoEmptyBlockAncestorDeleter final { public: /**