Bug 1968843 - Make HTMLEditor::HandleInsertText() manage Selection if committing composition a=dmeehan DONTBUILD

When committing composition string,
`WhiteSpaceVisibilityKeeper::InsertOrUpdateCompositionString()` may
replace commit string with normalized white-spaces.  Therefore, if
the last character is a white-space, it may be replaced with the other
white-space (ASCII white-space vs. NBSP).  Then, `Selection` collapsed
after the last character will be moved to start of the replaced
white-spaces.  So, when committing composition,
`HTMLEditor::HandleInsertText()` may need to change `Selection` by
itself.

Original Revision: https://phabricator.services.mozilla.com/D252278

Differential Revision: https://phabricator.services.mozilla.com/D253280
This commit is contained in:
Masayuki Nakano
2025-06-11 17:03:31 +00:00
committed by dmeehan@mozilla.com
parent e4865898e0
commit f3c6a960d9
4 changed files with 117 additions and 39 deletions

View File

@@ -4,6 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EditorBase.h"
#include "HTMLEditor.h"
#include "HTMLEditorInlines.h"
#include "HTMLEditorNestedClasses.h"
@@ -1271,45 +1272,86 @@ Result<EditActionResult, nsresult> HTMLEditor::HandleInsertText(
return EditActionResult::HandledResult();
}
const auto compositionEndPoint =
GetLastIMESelectionEndPoint<EditorDOMPoint>();
Result<InsertTextResult, nsresult> replaceTextResult =
WhiteSpaceVisibilityKeeper::InsertOrUpdateCompositionString(
*this, aInsertionString,
compositionEndPoint.IsSet()
? EditorDOMRange(pointToInsert, compositionEndPoint)
: EditorDOMRange(pointToInsert),
aPurpose);
if (MOZ_UNLIKELY(replaceTextResult.isErr())) {
NS_WARNING("WhiteSpaceVisibilityKeeper::ReplaceText() failed");
return replaceTextResult.propagateErr();
EditorDOMPoint endOfInsertedText;
{
AutoTrackDOMPoint trackPointToInsert(RangeUpdaterRef(), &pointToInsert);
const auto compositionEndPoint =
GetLastIMESelectionEndPoint<EditorDOMPoint>();
Result<InsertTextResult, nsresult> replaceTextResult =
WhiteSpaceVisibilityKeeper::InsertOrUpdateCompositionString(
*this, aInsertionString,
compositionEndPoint.IsSet()
? EditorDOMRange(pointToInsert, compositionEndPoint)
: EditorDOMRange(pointToInsert),
aPurpose);
if (MOZ_UNLIKELY(replaceTextResult.isErr())) {
NS_WARNING("WhiteSpaceVisibilityKeeper::ReplaceText() failed");
return replaceTextResult.propagateErr();
}
InsertTextResult unwrappedReplaceTextResult = replaceTextResult.unwrap();
nsresult rv = EnsureNoFollowingUnnecessaryLineBreak(
unwrappedReplaceTextResult.EndOfInsertedTextRef());
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::EnsureNoFollowingUnnecessaryLineBreak() failed");
return Err(rv);
}
endOfInsertedText = unwrappedReplaceTextResult.EndOfInsertedTextRef();
if (InsertingTextForCommittingComposition(aPurpose)) {
// If we're committing the composition,
// WhiteSpaceVisibilityKeeper::InsertOrUpdateCompositionString() may
// replace the last character of the composition string when it's a
// white-space. Then, Selection will be moved before the last
// character. So, we need to adjust Selection here.
nsresult rv = unwrappedReplaceTextResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CaretPoint::SuggestCaretPointTo() failed");
return Err(rv);
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CaretPoint::SuggestCaretPoint() failed, but ignored");
} else {
// CompositionTransaction should've set selection so that we should
// ignore caret suggestion.
unwrappedReplaceTextResult.IgnoreCaretPointSuggestion();
}
}
InsertTextResult unwrappedReplacedTextResult = replaceTextResult.unwrap();
nsresult rv = EnsureNoFollowingUnnecessaryLineBreak(
unwrappedReplacedTextResult.EndOfInsertedTextRef());
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::EnsureNoFollowingUnnecessaryLineBreak() failed");
return Err(rv);
}
// CompositionTransaction should've set selection so that we should ignore
// caret suggestion.
unwrappedReplacedTextResult.IgnoreCaretPointSuggestion();
const auto newCompositionStartPoint =
GetFirstIMESelectionStartPoint<EditorDOMPoint>();
const auto newCompositionEndPoint =
GetLastIMESelectionEndPoint<EditorDOMPoint>();
if (NS_WARN_IF(!newCompositionStartPoint.IsSet()) ||
NS_WARN_IF(!newCompositionEndPoint.IsSet())) {
// Mutation event listener has changed the DOM tree...
return EditActionResult::HandledResult();
}
rv = TopLevelEditSubActionDataRef().mChangedRange->SetStartAndEnd(
newCompositionStartPoint.ToRawRangeBoundary(),
newCompositionEndPoint.ToRawRangeBoundary());
if (NS_FAILED(rv)) {
NS_WARNING("nsRange::SetStartAndEnd() failed");
return Err(rv);
if (!InsertingTextForCommittingComposition(aPurpose)) {
const auto newCompositionStartPoint =
GetFirstIMESelectionStartPoint<EditorDOMPoint>();
const auto newCompositionEndPoint =
GetLastIMESelectionEndPoint<EditorDOMPoint>();
if (NS_WARN_IF(!newCompositionStartPoint.IsSet()) ||
NS_WARN_IF(!newCompositionEndPoint.IsSet())) {
// Mutation event listener has changed the DOM tree...
return EditActionResult::HandledResult();
}
nsresult rv =
TopLevelEditSubActionDataRef().mChangedRange->SetStartAndEnd(
newCompositionStartPoint.ToRawRangeBoundary(),
newCompositionEndPoint.ToRawRangeBoundary());
if (NS_FAILED(rv)) {
NS_WARNING("nsRange::SetStartAndEnd() failed");
return Err(rv);
}
} else {
if (NS_WARN_IF(!endOfInsertedText.IsSetAndValidInComposedDoc()) ||
NS_WARN_IF(!pointToInsert.IsSetAndValidInComposedDoc())) {
return EditActionResult::HandledResult();
}
nsresult rv =
TopLevelEditSubActionDataRef().mChangedRange->SetStartAndEnd(
pointToInsert.ToRawRangeBoundary(),
endOfInsertedText.ToRawRangeBoundary());
if (NS_FAILED(rv)) {
NS_WARNING("nsRange::SetStartAndEnd() failed");
return Err(rv);
}
}
return EditActionResult::HandledResult();
}