tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 689e5a7f77a435da2762d39415b64ae31a78306b
parent c7c08652632ab2b3f56fda1dbb99f6f644103e8d
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date:   Fri,  3 Oct 2025 00:57:53 +0000

Bug 1990586 - Make `WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt` track given caret point r=m_kato

It's used as the result in some cases.  However, it's not tracked in
some cases when touching the DOM.

This modifies the API of `AutoTrackDOMPoint` to flush the tracking point
but can keep tracking.  We should use the new API in every place later.

Differential Revision: https://phabricator.services.mozilla.com/D266752

Diffstat:
Meditor/libeditor/EditorForwards.h | 1+
Meditor/libeditor/SelectionState.h | 10++++++++--
Meditor/libeditor/WhiteSpaceVisibilityKeeper.cpp | 33+++++++++++++++++++++------------
Atesting/web-platform/tests/editing/crashtests/delete-in-empty-editable-document-element.html | 17+++++++++++++++++
4 files changed, 47 insertions(+), 14 deletions(-)

diff --git 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 @@ -322,6 +322,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. @@ -369,11 +371,13 @@ class MOZ_STACK_CLASS AutoTrackDOMPoint final { ~AutoTrackDOMPoint() { FlushAndStopTracking(); } - void FlushAndStopTracking() { + void Flush(StopTracking aStopTracking) { if (!mIsTracking) { return; } - mIsTracking = false; + if (static_cast<bool>(aStopTracking)) { + mIsTracking = false; + } if (mPoint.isSome()) { mRangeUpdater.DropRangeItem(mRangeItem); // Setting `mPoint` with invalid DOM point causes hitting `NS_ASSERTION()` @@ -416,6 +420,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 @@ -2347,6 +2347,11 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( return Err(NS_ERROR_FAILURE); } EditorDOMPoint pointToPutCaret(aCaretPoint); + Maybe<AutoTrackDOMPoint> 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. @@ -2354,8 +2359,6 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( aContentToDelete, BlockInlineCheck::UseComputedDisplayOutsideStyle)) { AutoTrackDOMPoint trackAtContent(aHTMLEditor.RangeUpdaterRef(), &atContent); { - AutoTrackDOMPoint trackPointToPutCaret(aHTMLEditor.RangeUpdaterRef(), - &pointToPutCaret); nsresult rv = WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpacesBefore( aHTMLEditor, EditorDOMPoint(aContentToDelete.AsElement())); @@ -2379,6 +2382,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 @@ -2452,7 +2458,7 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( } } } - trackAtContent.FlushAndStopTracking(); + trackAtContent.Flush(StopTracking::Yes); if (NS_WARN_IF(!atContent.IsInContentNodeAndValidInComposedDoc())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); } @@ -2478,7 +2484,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); } @@ -2497,7 +2503,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); } @@ -2507,13 +2513,16 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt( HTMLEditUtils::GetPreviousSibling( aContentToDelete, {WalkTreeOption::IgnoreNonEditableNode}); // Delete the node, and join like nodes if appropriate - { - AutoTrackDOMPoint trackPointToPutCaret(aHTMLEditor.RangeUpdaterRef(), - &pointToPutCaret); - nsresult rv = aHTMLEditor.DeleteNodeWithTransaction(aContentToDelete); - if (NS_FAILED(rv)) { - NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed"); - return Err(rv); + 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); } } 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 @@ -0,0 +1,17 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script> +"use strict"; + +addEventListener("DOMContentLoaded", () => { + document.documentElement.contentEditable = "plaintext-only"; + document.execCommand("delete"); +}, {once: true}); +</script> +</head> +<body> +<track></track> +</body> +</html>