diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 7982f999fdef..5a19a0326329 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -2748,14 +2748,9 @@ EditorDOMPointType EditorBase::FindBetterInsertionPoint( return aPoint; } -Result EditorBase::InsertTextWithTransaction( +Result EditorBase::InsertTextWithTransaction( Document& aDocument, const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert) { - MOZ_ASSERT( - ShouldHandleIMEComposition() || !AllowsTransactionsToChangeSelection(), - "caller must have already used AutoTransactionsConserveSelection " - "if this is not for updating composition string"); - if (NS_WARN_IF(!aPointToInsert.IsSet())) { return Err(NS_ERROR_INVALID_ARG); } @@ -2763,7 +2758,7 @@ Result EditorBase::InsertTextWithTransaction( MOZ_ASSERT(aPointToInsert.IsSetAndValid()); if (!ShouldHandleIMEComposition() && aStringToInsert.IsEmpty()) { - return aPointToInsert; + return InsertTextResult(); } // In some cases, the node may be the anonymous div element or a padding @@ -2785,7 +2780,6 @@ Result EditorBase::InsertTextWithTransaction( } if (ShouldHandleIMEComposition()) { - CheckedUint32 newOffset; if (!pointToInsert.IsInTextNode()) { // create a text node RefPtr newTextNode = CreateTextNode(u""_ns); @@ -2799,62 +2793,27 @@ Result EditorBase::InsertTextWithTransaction( NS_WARNING("EditorBase::InsertNodeWithTransaction() failed"); return insertTextNodeResult.propagateErr(); } - nsresult rv = insertTextNodeResult.inspect().SuggestCaretPointTo( - *this, {SuggestCaret::OnlyIfHasSuggestion, - SuggestCaret::OnlyIfTransactionsAllowedToDoIt, - SuggestCaret::AndIgnoreTrivialError}); - if (NS_FAILED(rv)) { - NS_WARNING("CreateTextResult::SuggestCaretPointTo() failed"); - return Err(rv); - } - NS_WARNING_ASSERTION( - rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR, - "CreateTextResult::SuggestCaretPointTo() failed, but ignored"); + insertTextNodeResult.unwrap().IgnoreCaretPointSuggestion(); pointToInsert.Set(newTextNode, 0u); - newOffset = aStringToInsert.Length(); - } else { - newOffset = aStringToInsert.Length(); - newOffset += pointToInsert.Offset(); - if (NS_WARN_IF(!newOffset.isValid())) { - return Err(NS_ERROR_FAILURE); - } } - nsresult rv = InsertTextIntoTextNodeWithTransaction( - aStringToInsert, pointToInsert.AsInText()); - if (MOZ_UNLIKELY(Destroyed())) { - NS_WARNING( - "EditorBase::InsertTextIntoTextNodeWithTransaction() caused " - "destroying the editor"); - return Err(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_FAILED(rv)) { - NS_WARNING("EditorBase::InsertTextIntoTextNodeWithTransaction() failed"); - return Err(rv); - } - return EditorDOMPoint(pointToInsert.GetContainer(), newOffset.value()); + Result insertTextResult = + InsertTextIntoTextNodeWithTransaction(aStringToInsert, + pointToInsert.AsInText()); + NS_WARNING_ASSERTION( + insertTextResult.isOk(), + "EditorBase::InsertTextIntoTextNodeWithTransaction() failed"); + return insertTextResult; } if (pointToInsert.IsInTextNode()) { - CheckedUint32 newOffset = aStringToInsert.Length(); - newOffset += pointToInsert.Offset(); - if (NS_WARN_IF(!newOffset.isValid())) { - return Err(NS_ERROR_FAILURE); - } // we are inserting text into an existing text node. - nsresult rv = InsertTextIntoTextNodeWithTransaction( - aStringToInsert, EditorDOMPointInText(pointToInsert.ContainerAs(), - pointToInsert.Offset())); - if (MOZ_UNLIKELY(Destroyed())) { - NS_WARNING( - "EditorBase::InsertTextIntoTextNodeWithTransaction() caused " - "destroying the editor"); - return Err(NS_ERROR_EDITOR_DESTROYED); - } - if (NS_FAILED(rv)) { - NS_WARNING("EditorBase::InsertTextIntoTextNodeWithTransaction() failed"); - return Err(rv); - } - return EditorDOMPoint(pointToInsert.GetContainer(), newOffset.value()); + Result insertTextResult = + InsertTextIntoTextNodeWithTransaction(aStringToInsert, + pointToInsert.AsInText()); + NS_WARNING_ASSERTION( + insertTextResult.isOk(), + "EditorBase::InsertTextIntoTextNodeWithTransaction() failed"); + return insertTextResult; } // we are inserting text into a non-text node. first we have to create a @@ -2870,19 +2829,12 @@ Result EditorBase::InsertTextWithTransaction( NS_WARNING("EditorBase::InsertNodeWithTransaction() failed"); return Err(insertTextNodeResult.unwrapErr()); } - nsresult rv = insertTextNodeResult.inspect().SuggestCaretPointTo( - *this, {SuggestCaret::OnlyIfHasSuggestion, - SuggestCaret::OnlyIfTransactionsAllowedToDoIt, - SuggestCaret::AndIgnoreTrivialError}); - if (NS_FAILED(rv)) { - NS_WARNING("CreateTextResult::SuggestCaretPointTo() failed"); - return Err(rv); + insertTextNodeResult.unwrap().IgnoreCaretPointSuggestion(); + if (NS_WARN_IF(!newTextNode->IsInComposedDoc())) { + return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); } - NS_WARNING_ASSERTION( - rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR, - "CreateTextResult::SuggestCaretPointTo() failed, but ignored"); - return EditorDOMPoint(insertTextNodeResult.inspect().GetNewNode(), - aStringToInsert.Length()); + return InsertTextResult(EditorDOMPointInText::AtEndOf(*newTextNode), + EditorDOMPoint::AtEndOf(*newTextNode)); } static bool TextFragmentBeginsWithStringAtOffset( @@ -2939,18 +2891,16 @@ EditorBase::ComputeInsertedRange(const EditorDOMPointInText& aInsertedPoint, return {EditorDOMPointInText(), EditorDOMPointInText()}; } -nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( +Result +EditorBase::InsertTextIntoTextNodeWithTransaction( const nsAString& aStringToInsert, - const EditorDOMPointInText& aPointToInsert, bool aSuppressIME) { + const EditorDOMPointInText& aPointToInsert) { MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(aPointToInsert.IsSetAndValid()); RefPtr transaction; bool isIMETransaction = false; - // aSuppressIME is used when editor must insert text, yet this text is not - // part of the current IME operation. Example: adjusting white-space around an - // IME insertion. - if (ShouldHandleIMEComposition() && !aSuppressIME) { + if (ShouldHandleIMEComposition()) { transaction = CompositionTransaction::Create(*this, aStringToInsert, aPointToInsert); isIMETransaction = true; @@ -2967,6 +2917,9 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( "EditorBase::DoTransactionInternal() failed"); EndUpdateViewBatch(__FUNCTION__); + // Don't check whether we've been destroyed here because we need to notify + // listeners and observers below even if we've already destroyed. + auto pointToInsert = [&]() -> EditorDOMPointInText { if (!isIMETransaction) { return aPointToInsert; @@ -2980,6 +2933,10 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( mComposition->GetContainerTextNode()->TextDataLength())); }(); + EditorDOMPointInText endOfInsertedText( + pointToInsert.ContainerAs(), + pointToInsert.Offset() + aStringToInsert.Length()); + if (IsHTMLEditor()) { auto [begin, end] = ComputeInsertedRange(pointToInsert, aStringToInsert); if (begin.IsSet() && end.IsSet()) { @@ -2991,6 +2948,7 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( // IME since non-ASCII character may be inserted into it in most cases. pointToInsert.ContainerAs()->MarkAsMaybeModifiedFrequently(); } + // XXX Should we update endOfInsertedText here? } // let listeners know what happened @@ -2998,9 +2956,9 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( for (auto& listener : mActionListeners.Clone()) { // TODO: might need adaptation because of mutation event listeners called // during `DoTransactionInternal`. - DebugOnly rvIgnored = - listener->DidInsertText(pointToInsert.ContainerAs(), - pointToInsert.Offset(), aStringToInsert, rv); + DebugOnly rvIgnored = listener->DidInsertText( + pointToInsert.ContainerAs(), + static_cast(pointToInsert.Offset()), aStringToInsert, rv); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "nsIEditActionListener::DidInsertText() failed, but ignored"); @@ -3020,20 +2978,27 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( if (IsHTMLEditor() && isIMETransaction && mComposition) { RefPtr textNode = mComposition->GetContainerTextNode(); if (textNode && !textNode->Length()) { - nsresult rv = DeleteNodeWithTransaction(*textNode); - if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) { - NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed"); - return rv; + rv = DeleteNodeWithTransaction(*textNode); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "EditorBase::DeleteNodeTransaction() failed"); + if (MOZ_LIKELY(!textNode->IsInComposedDoc())) { + mComposition->OnTextNodeRemoved(); } - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv), - "EditorBase::DeleteNodeWithTransaction() failed, but ignored"); - mComposition->OnTextNodeRemoved(); static_cast(transaction.get())->MarkFixed(); } } - return rv; + if (NS_WARN_IF(Destroyed())) { + return Err(NS_ERROR_EDITOR_DESTROYED); + } + + InsertTextTransaction* const insertTextTransaction = + transaction->GetAsInsertTextTransaction(); + return insertTextTransaction + ? InsertTextResult(std::move(endOfInsertedText), + insertTextTransaction + ->SuggestPointToPutCaret()) + : InsertTextResult(std::move(endOfInsertedText)); } nsresult EditorBase::NotifyDocumentListeners( diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 4a3b88ff7baa..1fc51eede9dc 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -1665,39 +1665,28 @@ class EditorBase : public nsIEditor, const nsAString& aStringToInsert, SelectionHandling aSelectionHandling); /** - * InsertTextWithTransaction() inserts aStringToInsert to aPointToInsert or - * better insertion point around it. If aPointToInsert isn't in a text node, - * this method looks for the nearest point in a text node with - * FindBetterInsertionPoint(). If there is no text node, this creates - * new text node and put aStringToInsert to it. + * Insert aStringToInsert to aPointToInsert or better insertion point around + * it. If aPointToInsert isn't in a text node, this method looks for the + * nearest point in a text node with FindBetterInsertionPoint(). If there is + * no text node, this creates new text node and put aStringToInsert to it. * * @param aDocument The document of this editor. * @param aStringToInsert The string to insert. * @param aPointToInsert The point to insert aStringToInsert. * Must be valid DOM point. - * @return If succeeded, returns the point after inserted - * aStringToInsert. So, when this method actually - * inserts string, returns a point in the text node. - * Otherwise, returns aPointToInsert. */ - [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result + [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result InsertTextWithTransaction(Document& aDocument, const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert); /** - * InsertTextIntoTextNodeWithTransaction() inserts aStringToInsert into - * aOffset of aTextNode with transaction. - * - * @param aStringToInsert String to be inserted. - * @param aPointToInsert The insertion point. - * @param aSuppressIME true if it's not a part of IME composition. - * E.g., adjusting white-spaces during composition. - * false, otherwise. + * Insert aStringToInsert to aPointToInsert. */ - MOZ_CAN_RUN_SCRIPT nsresult InsertTextIntoTextNodeWithTransaction( + [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result + InsertTextIntoTextNodeWithTransaction( const nsAString& aStringToInsert, - const EditorDOMPointInText& aPointToInsert, bool aSuppressIME = false); + const EditorDOMPointInText& aPointToInsert); /** * SetTextNodeWithoutTransaction() is optimized path to set new value to diff --git a/editor/libeditor/EditorForwards.h b/editor/libeditor/EditorForwards.h index cf25450b2b0e..7adc988d0298 100644 --- a/editor/libeditor/EditorForwards.h +++ b/editor/libeditor/EditorForwards.h @@ -75,11 +75,6 @@ using EditorRawDOMPointInText = EditorDOMPointBase; ******************************************************************************/ class AutoPendingStyleCacheArray; // mozilla/PendingStyles.h -class AutoSelectionRangeArray; // EditorUtils.h -class CaretPoint; // EditorUtils.h -class ChangeStyleTransaction; // ChangeStyleTransaction.h -class CSSEditUtils; // CSSEditUtils.h -class EditActionResult; // EditorUtils.h class EditTransactionBase; // mozilla/EditTransactionBase.h class EditorBase; // mozilla/EditorBase.h class HTMLEditor; // mozilla/HTMLEditor.h @@ -92,18 +87,24 @@ class SelectionState; // mozilla/SelectionState.h class TextEditor; // mozilla/TextEditor.h class AutoRangeArray; // AutoRangeArray.h +class AutoSelectionRangeArray; // EditorUtils.h +class CaretPoint; // EditorUtils.h class ChangeAttributeTransaction; // ChangeAttributeTransaction.h +class ChangeStyleTransaction; // ChangeStyleTransaction.h class CompositionTransaction; // CompositionTransaction.h +class CSSEditUtils; // CSSEditUtils.h class DeleteContentTransactionBase; // DeleteContentTransactionBase.h class DeleteMultipleRangesTransaction; // DeleteMultipleRangesTransaction.h class DeleteNodeTransaction; // DeleteNodeTransaction.h class DeleteRangeTransaction; // DeleteRangeTransaction.h class DeleteTextTransaction; // DeleteTextTransaction.h +class EditActionResult; // EditorUtils.h class EditAggregateTransaction; // EditAggregateTransaction.h class EditorEventListener; // EditorEventListener.h class EditResult; // HTMLEditHelpers.h class HTMLEditorEventListener; // HTMLEditorEventListener.h class InsertNodeTransaction; // InsertNodeTransaction.h +class InsertTextResult; // EditorUtils.h class InsertTextTransaction; // InsertTextTransaction.h class InterCiter; // InterCiter.h class JoinNodesResult; // HTMLEditHelpers.h diff --git a/editor/libeditor/EditorUtils.h b/editor/libeditor/EditorUtils.h index 5e96d8f1cf19..bc9713f0466d 100644 --- a/editor/libeditor/EditorUtils.h +++ b/editor/libeditor/EditorUtils.h @@ -254,6 +254,36 @@ class MOZ_STACK_CLASS CreateNodeResultBase final : public CaretPoint { RefPtr mNode; }; +/** + * This is a result of inserting text. If the text inserted as a part of + * composition, this does not return CaretPoint. Otherwise, must return + * CaretPoint which is typically same as end of inserted text. + */ +class MOZ_STACK_CLASS InsertTextResult final : public CaretPoint { + public: + InsertTextResult() : CaretPoint(EditorDOMPoint()) {} + template + explicit InsertTextResult(const EditorDOMPointType& aEndOfInsertedText) + : CaretPoint(EditorDOMPoint()), + mEndOfInsertedText(aEndOfInsertedText.template To()) {} + explicit InsertTextResult(EditorDOMPointInText&& aEndOfInsertedText) + : CaretPoint(EditorDOMPoint()), + mEndOfInsertedText(std::move(aEndOfInsertedText)) {} + template + InsertTextResult(EditorDOMPointInText&& aEndOfInsertedText, + const EditorDOMPointType& aCaretPoint) + : CaretPoint(aCaretPoint.template To()), + mEndOfInsertedText(std::move(aEndOfInsertedText)) {} + + [[nodiscard]] bool Handled() const { return mEndOfInsertedText.IsSet(); } + const EditorDOMPointInText& EndOfInsertedTextRef() const { + return mEndOfInsertedText; + } + + private: + EditorDOMPointInText mEndOfInsertedText; +}; + /*************************************************************************** * stack based helper class for calling EditorBase::EndTransaction() after * EditorBase::BeginTransaction(). This shouldn't be used in editor classes diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp index 67ed7b7f24cd..3f734fc21b08 100644 --- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -1148,13 +1148,24 @@ Result HTMLEditor::HandleInsertText( // Right now the WhiteSpaceVisibilityKeeper code bails on empty strings, // but IME needs the InsertTextWithTransaction() call to still happen // since empty strings are meaningful there. - Result insertTextResult = + Result insertTextResult = InsertTextWithTransaction(*document, aInsertionString, compositionStartPoint); if (MOZ_UNLIKELY(insertTextResult.isErr())) { NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed"); return insertTextResult.propagateErr(); } + nsresult rv = insertTextResult.unwrap().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::SuggestCaretPointTo() failed, but ignored"); return EditActionResult::HandledResult(); } @@ -1273,14 +1284,22 @@ Result HTMLEditor::HandleInsertText( "to different point " "by mutation observer"); } else { - Result insertTextResult = + Result insertTextResult = InsertTextWithTransaction(*document, subStr, currentPoint); if (MOZ_UNLIKELY(insertTextResult.isErr())) { NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed"); return insertTextResult.propagateErr(); } - currentPoint = insertTextResult.inspect(); - pointToInsert = insertTextResult.unwrap(); + // Ignore the caret suggestion because of `dontChangeMySelection` + // above. + insertTextResult.inspect().IgnoreCaretPointSuggestion(); + if (insertTextResult.inspect().Handled()) { + pointToInsert = currentPoint = insertTextResult.unwrap() + .EndOfInsertedTextRef() + .To(); + } else { + pointToInsert = currentPoint; + } } } } else { @@ -2311,13 +2330,19 @@ Result HTMLEditor::HandleInsertLinefeed( { AutoTrackDOMPoint trackingInsertingPosition(RangeUpdaterRef(), &pointToInsert); - Result insertTextResult = + Result insertTextResult = InsertTextWithTransaction(*document, u"\n"_ns, pointToInsert); if (MOZ_UNLIKELY(insertTextResult.isErr())) { NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed"); - return insertTextResult; + return insertTextResult.propagateErr(); } - pointToPutCaret = insertTextResult.unwrap(); + // Ignore the caret suggestion because of `dontChangeMySelection` above. + insertTextResult.inspect().IgnoreCaretPointSuggestion(); + pointToPutCaret = insertTextResult.inspect().Handled() + ? insertTextResult.unwrap() + .EndOfInsertedTextRef() + .To() + : pointToInsert; } // Insert a padding
element at the end of the block element if there is diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 2c68bcdf7169..29ec2cf4c7c3 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -3862,13 +3862,24 @@ nsresult HTMLEditor::ReplaceTextWithTransaction( if (NS_WARN_IF(!document)) { return NS_ERROR_NOT_INITIALIZED; } - Result insertTextResult = + Result insertTextResult = InsertTextWithTransaction(*document, aStringToInsert, EditorDOMPoint(&aTextNode, aOffset)); if (MOZ_UNLIKELY(insertTextResult.isErr())) { NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed"); return insertTextResult.unwrapErr(); } + nsresult rv = insertTextResult.unwrap().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::SuggestCaretPointTo() failed, but ignored"); return NS_OK; } @@ -3960,7 +3971,7 @@ nsresult HTMLEditor::ReplaceTextWithTransaction( return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : rv; } -Result HTMLEditor::InsertTextWithTransaction( +Result HTMLEditor::InsertTextWithTransaction( Document& aDocument, const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert) { if (NS_WARN_IF(!aPointToInsert.IsSet())) { diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index 549e6085d4ce..337eca9d9dc0 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -752,9 +752,10 @@ class HTMLEditor final : public EditorBase, const nsAString& aStringToInsert); /** - * InsertTextWithTransaction() inserts aStringToInsert at aPointToInsert. + * Insert aStringToInsert to aPointToInsert. If the point is not editable, + * this returns error. */ - [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result + [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result InsertTextWithTransaction(Document& aDocument, const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert) final; diff --git a/editor/libeditor/InsertTextTransaction.cpp b/editor/libeditor/InsertTextTransaction.cpp index f946fc44c904..8010fe32f65d 100644 --- a/editor/libeditor/InsertTextTransaction.cpp +++ b/editor/libeditor/InsertTextTransaction.cpp @@ -5,16 +5,18 @@ #include "InsertTextTransaction.h" +#include "ErrorList.h" #include "mozilla/EditorBase.h" // mEditorBase #include "mozilla/Logging.h" #include "mozilla/SelectionState.h" // RangeUpdater #include "mozilla/ToString.h" #include "mozilla/dom/Selection.h" // Selection local var #include "mozilla/dom/Text.h" // mTextNode -#include "nsAString.h" // nsAString parameter -#include "nsDebug.h" // for NS_ASSERTION, etc. -#include "nsError.h" // for NS_OK, etc. -#include "nsQueryObject.h" // for do_QueryObject + +#include "nsAString.h" // nsAString parameter +#include "nsDebug.h" // for NS_ASSERTION, etc. +#include "nsError.h" // for NS_OK, etc. +#include "nsQueryObject.h" // for do_QueryObject namespace mozilla { @@ -77,28 +79,9 @@ NS_IMETHODIMP InsertTextTransaction::DoTransaction() { return error.StealNSResult(); } - // Only set selection to insertion point if editor gives permission - if (editorBase->AllowsTransactionsToChangeSelection()) { - RefPtr selection = editorBase->GetSelection(); - if (NS_WARN_IF(!selection)) { - return NS_ERROR_FAILURE; - } - DebugOnly rvIgnored = editorBase->CollapseSelectionTo( - EditorRawDOMPoint(textNode, mOffset + mStringToInsert.Length())); - NS_ASSERTION(NS_SUCCEEDED(rvIgnored), - "EditorBase::CollapseSelectionTo() failed, but ignored"); - // Keep handling to adjust the ranges in the range updater even if the - // editor is destroyed. - } else { - // Do nothing - DOM Range gravity will adjust selection - } - // XXX Other transactions do not do this but its callers do. - // Why do this transaction do this by itself? editorBase->RangeUpdaterRef().SelAdjInsertText(textNode, mOffset, mStringToInsert.Length()); - - return MOZ_UNLIKELY(editorBase->Destroyed()) ? NS_ERROR_EDITOR_DESTROYED - : NS_OK; + return NS_OK; } NS_IMETHODIMP InsertTextTransaction::UndoTransaction() { @@ -121,7 +104,22 @@ NS_IMETHODIMP InsertTextTransaction::RedoTransaction() { MOZ_LOG(GetLogModule(), LogLevel::Info, ("%p InsertTextTransaction::%s this=%s", this, __FUNCTION__, ToString(*this).c_str())); - return DoTransaction(); + nsresult rv = DoTransaction(); + if (NS_FAILED(rv)) { + NS_WARNING("InsertTextTransaction::DoTransaction() failed"); + return rv; + } + if (RefPtr editorBase = mEditorBase) { + nsresult rv = editorBase->CollapseSelectionTo( + SuggestPointToPutCaret()); + if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { + return NS_ERROR_EDITOR_DESTROYED; + } + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "EditorBase::CollapseSelectionTo() failed, but ignored"); + } + return NS_OK; } NS_IMETHODIMP InsertTextTransaction::Merge(nsITransaction* aOtherTransaction, diff --git a/editor/libeditor/InsertTextTransaction.h b/editor/libeditor/InsertTextTransaction.h index 6b839a777364..6b78d107d707 100644 --- a/editor/libeditor/InsertTextTransaction.h +++ b/editor/libeditor/InsertTextTransaction.h @@ -8,6 +8,7 @@ #include "EditTransactionBase.h" // base class +#include "EditorDOMPoint.h" #include "EditorForwards.h" #include "nsCycleCollectionParticipant.h" // various macros @@ -57,6 +58,14 @@ class InsertTextTransaction final : public EditTransactionBase { */ void GetData(nsString& aResult); + template + EditorDOMPointType SuggestPointToPutCaret() const { + if (NS_WARN_IF(!mTextNode)) { + return EditorDOMPointType(); + } + return EditorDOMPointType(mTextNode, mOffset + mStringToInsert.Length()); + } + friend std::ostream& operator<<(std::ostream& aStream, const InsertTextTransaction& aTransaction); diff --git a/editor/libeditor/TextEditSubActionHandler.cpp b/editor/libeditor/TextEditSubActionHandler.cpp index 131b770849f6..17fae74f2ac9 100644 --- a/editor/libeditor/TextEditSubActionHandler.cpp +++ b/editor/libeditor/TextEditSubActionHandler.cpp @@ -3,6 +3,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 "ErrorList.h" #include "TextEditor.h" #include "AutoRangeArray.h" @@ -218,49 +219,34 @@ TextEditor::InsertLineFeedCharacterAtSelection() { return Err(NS_ERROR_NOT_INITIALIZED); } - // Don't change my selection in sub-transactions. - AutoTransactionsConserveSelection dontChangeMySelection(*this); - // Insert a linefeed character. - Result insertTextResult = + Result insertTextResult = InsertTextWithTransaction(*document, u"\n"_ns, pointToInsert); if (MOZ_UNLIKELY(insertTextResult.isErr())) { NS_WARNING("TextEditor::InsertTextWithTransaction(\"\\n\") failed"); return insertTextResult.propagateErr(); } - if (MOZ_UNLIKELY(!insertTextResult.inspect().IsSet())) { - NS_WARNING( - "EditorBase::InsertTextWithTransaction(\"\\n\") didn't return position " - "of inserted linefeed"); + insertTextResult.inspect().IgnoreCaretPointSuggestion(); + EditorDOMPoint pointToPutCaret = insertTextResult.inspect().Handled() + ? insertTextResult.inspect() + .EndOfInsertedTextRef() + .To() + : pointToInsert; + if (NS_WARN_IF(!pointToPutCaret.IsSetAndValid())) { return Err(NS_ERROR_FAILURE); } - - // set the selection to the correct location - MOZ_ASSERT(insertTextResult.inspect().IsInTextNode(), - "After inserting text into a text node, insertTextResult should " - "return a point in a text node"); - nsresult rv = CollapseSelectionTo(insertTextResult.inspect()); + // XXX I don't think we still need this. This must have been required when + // `