diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp
index 0d2b3fd70a55..e12b93260e5f 100644
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -2921,7 +2921,8 @@ class MOZ_STACK_CLASS HTMLEditor::AutoDeleteRangesHandler final {
aInclusiveDescendantOfLeftBlockElement),
mInclusiveDescendantOfRightBlockElement(
aInclusiveDescendantOfRightBlockElement),
- mCanJoinBlocks(false) {}
+ mCanJoinBlocks(false),
+ mFallbackToDeleteLeafContent(false) {}
bool IsSet() const { return mLeftBlockElement && mRightBlockElement; }
bool IsSameBlockElement() const {
@@ -2939,6 +2940,17 @@ class MOZ_STACK_CLASS HTMLEditor::AutoDeleteRangesHandler final {
*/
bool CanJoinBlocks() const { return mCanJoinBlocks; }
+ /**
+ * When this returns true, `Run()` must return "ignored" so that
+ * caller can skip calling `Run()`. This is available only when
+ * `CanJoinBlocks()` returns `true`.
+ * TODO: This should be merged into `CanJoinBlocks()` in the future.
+ */
+ bool ShouldDeleteLeafContentInstead() const {
+ MOZ_ASSERT(CanJoinBlocks());
+ return mFallbackToDeleteLeafContent;
+ }
+
/**
* Join inclusive ancestor block elements which are found by preceding
* Preare() call.
@@ -2954,6 +2966,35 @@ class MOZ_STACK_CLASS HTMLEditor::AutoDeleteRangesHandler final {
Run(HTMLEditor& aHTMLEditor);
private:
+ /**
+ * This method returns true when
+ * `MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement()`,
+ * `MergeFirstLineOfRightBlockElementIntoAncestorLeftBlockElement()` and
+ * `MergeFirstLineOfRightBlockElementIntoLeftBlockElement()` handle it
+ * with the `if` block of their main blocks.
+ */
+ bool CanMergeLeftAndRightBlockElements() const {
+ if (!IsSet()) {
+ return false;
+ }
+ // `MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement()`
+ if (mPointContainingTheOtherBlockElement.GetContainer() ==
+ mRightBlockElement) {
+ return mNewListElementTagNameOfRightListElement.isSome();
+ }
+ // `MergeFirstLineOfRightBlockElementIntoAncestorLeftBlockElement()`
+ if (mPointContainingTheOtherBlockElement.GetContainer() ==
+ mLeftBlockElement) {
+ return mNewListElementTagNameOfRightListElement.isSome() &&
+ !mRightBlockElement->GetChildCount();
+ }
+ MOZ_ASSERT(!mPointContainingTheOtherBlockElement.IsSet());
+ // `MergeFirstLineOfRightBlockElementIntoLeftBlockElement()`
+ return mNewListElementTagNameOfRightListElement.isSome() ||
+ mLeftBlockElement->NodeInfo()->NameAtom() ==
+ mRightBlockElement->NodeInfo()->NameAtom();
+ }
+
OwningNonNull mInclusiveDescendantOfLeftBlockElement;
OwningNonNull mInclusiveDescendantOfRightBlockElement;
RefPtr mLeftBlockElement;
@@ -2962,6 +3003,7 @@ class MOZ_STACK_CLASS HTMLEditor::AutoDeleteRangesHandler final {
EditorDOMPoint mPointContainingTheOtherBlockElement;
RefPtr mPrecedingInvisibleBRElement;
bool mCanJoinBlocks;
+ bool mFallbackToDeleteLeafContent;
}; // HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
// AutoInclusiveAncestorBlockElementsJoiner
@@ -4345,6 +4387,19 @@ EditActionResult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
NS_WARNING("AutoInclusiveAncestorBlockElementsJoiner::Run() failed");
return result;
}
+#ifdef DEBUG
+ if (joiner.ShouldDeleteLeafContentInstead()) {
+ NS_ASSERTION(
+ result.Ignored(),
+ "Assumed `AutoInclusiveAncestorBlockElementsJoiner::Run()` "
+ "retruning ignored, but returned not ignored");
+ } else {
+ NS_ASSERTION(
+ !result.Ignored(),
+ "Assumed `AutoInclusiveAncestorBlockElementsJoiner::Run()` "
+ "retruning handled, but returned ignored");
+ }
+#endif // #ifdef DEBUG
}
// If AutoInclusiveAncestorBlockElementsJoiner didn't handle it and it's not
@@ -4463,6 +4518,17 @@ EditActionResult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
NS_WARNING("AutoInclusiveAncestorBlockElementsJoiner::Run() failed");
return result;
}
+#ifdef DEBUG
+ if (joiner.ShouldDeleteLeafContentInstead()) {
+ NS_ASSERTION(result.Ignored(),
+ "Assumed `AutoInclusiveAncestorBlockElementsJoiner::Run()` "
+ "retruning ignored, but returned not ignored");
+ } else {
+ NS_ASSERTION(!result.Ignored(),
+ "Assumed `AutoInclusiveAncestorBlockElementsJoiner::Run()` "
+ "retruning handled, but returned ignored");
+ }
+#endif // #ifdef DEBUG
}
// This should claim that trying to join the block means that
// this handles the action because the caller shouldn't do anything
@@ -4915,6 +4981,17 @@ EditActionResult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
NS_WARNING("AutoInclusiveAncestorBlockElementsJoiner::Run() failed");
return result;
}
+#ifdef DEBUG
+ if (joiner.ShouldDeleteLeafContentInstead()) {
+ NS_ASSERTION(result.Ignored(),
+ "Assumed `AutoInclusiveAncestorBlockElementsJoiner::Run()` "
+ "retruning ignored, but returned not ignored");
+ } else {
+ NS_ASSERTION(!result.Ignored(),
+ "Assumed `AutoInclusiveAncestorBlockElementsJoiner::Run()` "
+ "retruning handled, but returned ignored");
+ }
+#endif // #ifdef DEBUG
break;
}
@@ -5937,6 +6014,7 @@ Result HTMLEditor::AutoDeleteRangesHandler::
// Bail if both blocks the same
if (IsSameBlockElement()) {
mCanJoinBlocks = true; // XXX Anyway, Run() will ingore this case.
+ mFallbackToDeleteLeafContent = true;
return true;
}
@@ -5986,15 +6064,71 @@ Result HTMLEditor::AutoDeleteRangesHandler::
mPrecedingInvisibleBRElement =
WSRunScanner::GetPrecedingBRElementUnlessVisibleContentFound(
aHTMLEditor, EditorDOMPoint::AtEndOf(mLeftBlockElement));
+ // `WhiteSpaceVisibilityKeeper::
+ // MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement()`
+ // returns ignored when:
+ // - No preceding invisible `
` element and
+ // - mNewListElementTagNameOfRightListElement is nothing and
+ // - There is no content to move from right block element.
+ if (!mPrecedingInvisibleBRElement) {
+ if (CanMergeLeftAndRightBlockElements()) {
+ // Always marked as handled in this case.
+ mFallbackToDeleteLeafContent = false;
+ } else {
+ // Marked as handled only when it actually moves a content node.
+ Result firstLineHasContent =
+ aHTMLEditor.CanMoveOrDeleteSomethingInHardLine(
+ mPointContainingTheOtherBlockElement.NextPoint());
+ mFallbackToDeleteLeafContent =
+ firstLineHasContent.isOk() && !firstLineHasContent.inspect();
+ }
+ } else {
+ // Marked as handled when deleting the invisible `
` element.
+ mFallbackToDeleteLeafContent = false;
+ }
} else if (mPointContainingTheOtherBlockElement.GetContainer() ==
mLeftBlockElement) {
mPrecedingInvisibleBRElement =
WSRunScanner::GetPrecedingBRElementUnlessVisibleContentFound(
aHTMLEditor, mPointContainingTheOtherBlockElement);
+ // `WhiteSpaceVisibilityKeeper::
+ // MergeFirstLineOfRightBlockElementIntoAncestorLeftBlockElement()`
+ // returns ignored when:
+ // - No preceding invisible `
` element and
+ // - mNewListElementTagNameOfRightListElement is some and
+ // - The right block element has no children
+ // or,
+ // - No preceding invisible `
` element and
+ // - mNewListElementTagNameOfRightListElement is nothing and
+ // - There is no content to move from right block element.
+ if (!mPrecedingInvisibleBRElement) {
+ if (CanMergeLeftAndRightBlockElements()) {
+ // Marked as handled only when it actualy moves a content node.
+ Result rightBlockHasContent =
+ aHTMLEditor.CanMoveChildren(*mRightBlockElement,
+ *mLeftBlockElement);
+ mFallbackToDeleteLeafContent =
+ rightBlockHasContent.isOk() && !rightBlockHasContent.inspect();
+ } else {
+ // Marked as handled only when it actually moves a content node.
+ Result firstLineHasContent =
+ aHTMLEditor.CanMoveOrDeleteSomethingInHardLine(
+ EditorRawDOMPoint(mRightBlockElement, 0));
+ mFallbackToDeleteLeafContent =
+ firstLineHasContent.isOk() && !firstLineHasContent.inspect();
+ }
+ } else {
+ // Marked as handled when deleting the invisible `
` element.
+ mFallbackToDeleteLeafContent = false;
+ }
} else {
mPrecedingInvisibleBRElement =
WSRunScanner::GetPrecedingBRElementUnlessVisibleContentFound(
aHTMLEditor, EditorDOMPoint::AtEndOf(mLeftBlockElement));
+ // `WhiteSpaceVisibilityKeeper::
+ // MergeFirstLineOfRightBlockElementIntoLeftBlockElement()` always
+ // return "handled".
+ mFallbackToDeleteLeafContent = false;
}
mCanJoinBlocks = true;
diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp
index a66bc9066e9b..2ea1fa696d2c 100644
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -160,6 +160,8 @@ EditActionResult WhiteSpaceVisibilityKeeper::
aPrecedingInvisibleBRElement == invisibleBRElementAtEndOfLeftBlockElement,
"The preceding invisible BR element computation was different");
EditActionResult ret(NS_OK);
+ // NOTE: Keep syncing with CanMergeLeftAndRightBlockElements() of
+ // AutoInclusiveAncestorBlockElementsJoiner.
if (NS_WARN_IF(aListElementTagName.isSome())) {
// Since 2002, here was the following comment:
// > The idea here is to take all children in rightListElement that are
@@ -301,6 +303,8 @@ EditActionResult WhiteSpaceVisibilityKeeper::
aPrecedingInvisibleBRElement == invisibleBRElementBeforeLeftBlockElement,
"The preceding invisible BR element computation was different");
EditActionResult ret(NS_OK);
+ // NOTE: Keep syncing with CanMergeLeftAndRightBlockElements() of
+ // AutoInclusiveAncestorBlockElementsJoiner.
if (aListElementTagName.isSome()) {
// XXX Why do we ignore the error from MoveChildrenWithTransaction()?
MOZ_ASSERT(originalLeftBlockElement == atLeftBlockChild.GetContainer(),