diff --git a/editor/libeditor/EditorForwards.h b/editor/libeditor/EditorForwards.h index 1e849cf2c890..932af273fac4 100644 --- a/editor/libeditor/EditorForwards.h +++ b/editor/libeditor/EditorForwards.h @@ -46,6 +46,7 @@ enum class EditorCommandParamType : uint16_t; // mozilla/EditorCommands.h enum class EditSubAction : int32_t; // mozilla/EditAction.h enum class ParagraphSeparator; // mozilla/HTMLEditor.h enum class SpecifiedStyle : uint8_t; // mozilla/PendingStyles.h +enum class StopTracking : bool; // mozilla/SelectionState.h enum class SuggestCaret; // EditorUtils.h enum class WithTransaction; // HTMLEditHelpers.h diff --git a/editor/libeditor/SelectionState.h b/editor/libeditor/SelectionState.h index 7552e47be2c9..0101fcfa19d3 100644 --- a/editor/libeditor/SelectionState.h +++ b/editor/libeditor/SelectionState.h @@ -284,6 +284,8 @@ class MOZ_STACK_CLASS RangeUpdater final { bool mLocked; }; +enum class StopTracking : bool { No, Yes }; + /** * Helper class for using SelectionState. Stack based class for doing * preservation of dom points across editor actions. @@ -331,11 +333,13 @@ class MOZ_STACK_CLASS AutoTrackDOMPoint final { ~AutoTrackDOMPoint() { FlushAndStopTracking(); } - void FlushAndStopTracking() { + void Flush(StopTracking aStopTracking) { if (!mIsTracking) { return; } - mIsTracking = false; + if (static_cast(aStopTracking)) { + mIsTracking = false; + } if (mPoint.isSome()) { mRangeUpdater.DropRangeItem(mRangeItem); // Setting `mPoint` with invalid DOM point causes hitting `NS_ASSERTION()` @@ -378,6 +382,8 @@ class MOZ_STACK_CLASS AutoTrackDOMPoint final { } } + void FlushAndStopTracking() { Flush(StopTracking::Yes); } + void StopTracking() { mIsTracking = false; } private: diff --git a/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp b/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp index 53124dd47447..656ccbc22181 100644 --- a/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp +++ b/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp @@ -3450,6 +3450,11 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( MOZ_ASSERT( StaticPrefs::editor_white_space_normalization_blink_compatible()); + Maybe trackPointToPutCaret; + if (aCaretPoint.IsSet()) { + trackPointToPutCaret.emplace(aHTMLEditor.RangeUpdaterRef(), + &pointToPutCaret); + } // If we're removing a block, it may be surrounded by invisible // white-spaces. We should remove them to avoid to make them accidentally // visible. @@ -3459,8 +3464,6 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( AutoTrackDOMPoint trackAtContent(aHTMLEditor.RangeUpdaterRef(), &atContent); { - AutoTrackDOMPoint trackPointToPutCaret(aHTMLEditor.RangeUpdaterRef(), - &pointToPutCaret); nsresult rv = WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpacesBefore( aHTMLEditor, EditorDOMPoint(aContentToDelete.AsElement())); @@ -3484,6 +3487,9 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( if (NS_WARN_IF(!aContentToDelete.IsInComposedDoc())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); } + if (trackPointToPutCaret.isSome()) { + trackPointToPutCaret->Flush(StopTracking::No); + } } if (pointToPutCaret.IsInContentNode()) { // Additionally, we may put caret into the preceding block (this is the @@ -3558,7 +3564,7 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( } } } - trackAtContent.FlushAndStopTracking(); + trackAtContent.Flush(StopTracking::Yes); if (NS_WARN_IF(!atContent.IsInContentNodeAndValidInComposedDoc())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); } @@ -3584,7 +3590,7 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( "failed"); return afterLastVisibleThingOrError.propagateErr(); } - trackAtContent.FlushAndStopTracking(); + trackAtContent.Flush(StopTracking::Yes); if (NS_WARN_IF(!atContent.IsInContentNodeAndValidInComposedDoc())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); } @@ -3604,7 +3610,7 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesBefore() failed"); return atFirstVisibleThingOrError.propagateErr(); } - trackAtContent.FlushAndStopTracking(); + trackAtContent.Flush(StopTracking::Yes); if (NS_WARN_IF(!atContent.IsInContentNodeAndValidInComposedDoc())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); } @@ -3616,13 +3622,22 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( aContentToDelete, {WalkTreeOption::IgnoreNonEditableNode}); // Delete the node, and join like nodes if appropriate { - AutoTrackDOMPoint trackPointToPutCaret(aHTMLEditor.RangeUpdaterRef(), - &pointToPutCaret); + Maybe trackPointToPutCaret; + if (pointToPutCaret.IsSet()) { + trackPointToPutCaret.emplace(aHTMLEditor.RangeUpdaterRef(), + &pointToPutCaret); + } nsresult rv = aHTMLEditor.DeleteNodeWithTransaction(aContentToDelete); if (NS_FAILED(rv)) { NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed"); return Err(rv); } + if (trackPointToPutCaret.isSome()) { + trackPointToPutCaret->Flush(StopTracking::Yes); + if (NS_WARN_IF(!pointToPutCaret.IsInContentNode())) { + return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); + } + } } // Are they both text nodes? If so, join them! diff --git a/testing/web-platform/tests/editing/crashtests/delete-in-empty-editable-document-element.html b/testing/web-platform/tests/editing/crashtests/delete-in-empty-editable-document-element.html new file mode 100644 index 000000000000..c1cc20b73064 --- /dev/null +++ b/testing/web-platform/tests/editing/crashtests/delete-in-empty-editable-document-element.html @@ -0,0 +1,17 @@ + + + + + + + + + +