зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1815383 - part 1: Make `InsertTextTransaction::DoTransaction` stop updating `Selection` directly r=m_kato
And also this patch makes its only user, `EditorBase::InsertTextIntoTextNodeWithTransaction`, and its only caller, `EditorBase::InsertTextWithTransaction`, return `InsertTextResult` for returning both end of inserted text and caret point suggestion. Note that if it's for IME composition, `CompositionTransaction` needs to update `Selection` directly. Therefore, caret point may be unset under composition to updating `Selection` to wrong point (it seems that `TextEditor::HandleInsertText` can be simplified later because of this change). Depends on D169044 Differential Revision: https://phabricator.services.mozilla.com/D169744
This commit is contained in:
Родитель
2aa6aa8213
Коммит
b1b8ae7fb7
|
@ -2748,14 +2748,9 @@ EditorDOMPointType EditorBase::FindBetterInsertionPoint(
|
|||
return aPoint;
|
||||
}
|
||||
|
||||
Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
|
||||
Result<InsertTextResult, nsresult> 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<EditorDOMPoint, nsresult> 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<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
|
|||
}
|
||||
|
||||
if (ShouldHandleIMEComposition()) {
|
||||
CheckedUint32 newOffset;
|
||||
if (!pointToInsert.IsInTextNode()) {
|
||||
// create a text node
|
||||
RefPtr<nsTextNode> newTextNode = CreateTextNode(u""_ns);
|
||||
|
@ -2799,62 +2793,27 @@ Result<EditorDOMPoint, nsresult> 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, nsresult> 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<Text>(),
|
||||
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, nsresult> 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<EditorDOMPoint, nsresult> 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<InsertTextResult, nsresult>
|
||||
EditorBase::InsertTextIntoTextNodeWithTransaction(
|
||||
const nsAString& aStringToInsert,
|
||||
const EditorDOMPointInText& aPointToInsert, bool aSuppressIME) {
|
||||
const EditorDOMPointInText& aPointToInsert) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
MOZ_ASSERT(aPointToInsert.IsSetAndValid());
|
||||
|
||||
RefPtr<EditTransactionBase> 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<Text>(),
|
||||
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<Text>()->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<nsresult> rvIgnored =
|
||||
listener->DidInsertText(pointToInsert.ContainerAs<Text>(),
|
||||
pointToInsert.Offset(), aStringToInsert, rv);
|
||||
DebugOnly<nsresult> rvIgnored = listener->DidInsertText(
|
||||
pointToInsert.ContainerAs<Text>(),
|
||||
static_cast<int32_t>(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<Text> 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<CompositionTransaction*>(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<EditorDOMPoint>())
|
||||
: InsertTextResult(std::move(endOfInsertedText));
|
||||
}
|
||||
|
||||
nsresult EditorBase::NotifyDocumentListeners(
|
||||
|
|
|
@ -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<EditorDOMPoint, nsresult>
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result<InsertTextResult, nsresult>
|
||||
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<InsertTextResult, nsresult>
|
||||
InsertTextIntoTextNodeWithTransaction(
|
||||
const nsAString& aStringToInsert,
|
||||
const EditorDOMPointInText& aPointToInsert, bool aSuppressIME = false);
|
||||
const EditorDOMPointInText& aPointToInsert);
|
||||
|
||||
/**
|
||||
* SetTextNodeWithoutTransaction() is optimized path to set new value to
|
||||
|
|
|
@ -75,11 +75,6 @@ using EditorRawDOMPointInText = EditorDOMPointBase<dom::Text*, nsIContent*>;
|
|||
******************************************************************************/
|
||||
|
||||
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
|
||||
|
|
|
@ -254,6 +254,36 @@ class MOZ_STACK_CLASS CreateNodeResultBase final : public CaretPoint {
|
|||
RefPtr<NodeType> 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 <typename EditorDOMPointType>
|
||||
explicit InsertTextResult(const EditorDOMPointType& aEndOfInsertedText)
|
||||
: CaretPoint(EditorDOMPoint()),
|
||||
mEndOfInsertedText(aEndOfInsertedText.template To<EditorDOMPoint>()) {}
|
||||
explicit InsertTextResult(EditorDOMPointInText&& aEndOfInsertedText)
|
||||
: CaretPoint(EditorDOMPoint()),
|
||||
mEndOfInsertedText(std::move(aEndOfInsertedText)) {}
|
||||
template <typename EditorDOMPointType>
|
||||
InsertTextResult(EditorDOMPointInText&& aEndOfInsertedText,
|
||||
const EditorDOMPointType& aCaretPoint)
|
||||
: CaretPoint(aCaretPoint.template To<EditorDOMPoint>()),
|
||||
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
|
||||
|
|
|
@ -1148,13 +1148,24 @@ Result<EditActionResult, nsresult> 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<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> 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<EditActionResult, nsresult> HTMLEditor::HandleInsertText(
|
|||
"to different point "
|
||||
"by mutation observer");
|
||||
} else {
|
||||
Result<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> 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<EditorDOMPoint>();
|
||||
} else {
|
||||
pointToInsert = currentPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2311,13 +2330,19 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::HandleInsertLinefeed(
|
|||
{
|
||||
AutoTrackDOMPoint trackingInsertingPosition(RangeUpdaterRef(),
|
||||
&pointToInsert);
|
||||
Result<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> 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<EditorDOMPoint>()
|
||||
: pointToInsert;
|
||||
}
|
||||
|
||||
// Insert a padding <br> element at the end of the block element if there is
|
||||
|
|
|
@ -3862,13 +3862,24 @@ nsresult HTMLEditor::ReplaceTextWithTransaction(
|
|||
if (NS_WARN_IF(!document)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
Result<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> 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<EditorDOMPoint, nsresult> HTMLEditor::InsertTextWithTransaction(
|
||||
Result<InsertTextResult, nsresult> HTMLEditor::InsertTextWithTransaction(
|
||||
Document& aDocument, const nsAString& aStringToInsert,
|
||||
const EditorDOMPoint& aPointToInsert) {
|
||||
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
||||
|
|
|
@ -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<EditorDOMPoint, nsresult>
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult>
|
||||
InsertTextWithTransaction(Document& aDocument,
|
||||
const nsAString& aStringToInsert,
|
||||
const EditorDOMPoint& aPointToInsert) final;
|
||||
|
|
|
@ -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> selection = editorBase->GetSelection();
|
||||
if (NS_WARN_IF(!selection)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
DebugOnly<nsresult> 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> editorBase = mEditorBase) {
|
||||
nsresult rv = editorBase->CollapseSelectionTo(
|
||||
SuggestPointToPutCaret<EditorRawDOMPoint>());
|
||||
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,
|
||||
|
|
|
@ -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 <typename EditorDOMPointType>
|
||||
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);
|
||||
|
||||
|
|
|
@ -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<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> 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<EditorDOMPoint>()
|
||||
: 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
|
||||
// `<textarea>` was implemented with text nodes and `<br>` elements.
|
||||
// We want the caret to stick to the content on the "right". We want the
|
||||
// caret to stick to whatever is past the break. This is because the break is
|
||||
// on the same line we were on, but the next content will be on the following
|
||||
// line.
|
||||
pointToPutCaret.SetInterlinePosition(InterlinePosition::StartOfNextLine);
|
||||
nsresult rv = CollapseSelectionTo(pointToPutCaret);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::CollapseSelectionTo() failed");
|
||||
return Err(rv);
|
||||
}
|
||||
|
||||
// XXX I don't think we still need this. This must have been required when
|
||||
// `<textarea>` was implemented with text nodes and `<br>` elements.
|
||||
// see if we're at the end of the editor range
|
||||
const auto endPoint = GetFirstSelectionEndPoint<EditorRawDOMPoint>();
|
||||
if (endPoint == insertTextResult.inspect()) {
|
||||
// SetInterlinePosition(true) means we want the caret to stick to the
|
||||
// content on the "right". We want the caret to stick to whatever is
|
||||
// past the break. This is because the break is on the same line we
|
||||
// were on, but the next content will be on the following line.
|
||||
DebugOnly<nsresult> rvIgnored =
|
||||
SelectionRef().SetInterlinePosition(InterlinePosition::StartOfNextLine);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
|
||||
"Selection::SetInterlinePosition(InterlinePosition::"
|
||||
"StartOfNextLine) failed, but ignored");
|
||||
}
|
||||
|
||||
return EditActionResult::HandledResult();
|
||||
}
|
||||
|
||||
|
@ -475,33 +461,45 @@ Result<EditActionResult, nsresult> TextEditor::HandleInsertText(
|
|||
compositionStartPoint.IsSet(),
|
||||
"EditorBase::FindBetterInsertionPoint() failed, but ignored");
|
||||
}
|
||||
Result<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> insertTextResult =
|
||||
InsertTextWithTransaction(*document, insertionString,
|
||||
compositionStartPoint);
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr())) {
|
||||
NS_WARNING("EditorBase::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");
|
||||
} else {
|
||||
MOZ_ASSERT(aEditSubAction == EditSubAction::eInsertText);
|
||||
|
||||
// don't change my selection in subtransactions
|
||||
AutoTransactionsConserveSelection dontChangeMySelection(*this);
|
||||
|
||||
Result<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> insertTextResult =
|
||||
InsertTextWithTransaction(*document, insertionString,
|
||||
atStartOfSelection);
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr())) {
|
||||
NS_WARNING("EditorBase::InsertTextWithTransaction() failed");
|
||||
return insertTextResult.propagateErr();
|
||||
}
|
||||
|
||||
if (insertTextResult.inspect().IsSet()) {
|
||||
// Ignore caret suggestion because there was
|
||||
// AutoTransactionsConserveSelection.
|
||||
insertTextResult.inspect().IgnoreCaretPointSuggestion();
|
||||
if (insertTextResult.inspect().Handled()) {
|
||||
// Make the caret attach to the inserted text, unless this text ends with
|
||||
// a LF, in which case make the caret attach to the next line.
|
||||
const bool endsWithLF =
|
||||
!insertionString.IsEmpty() && insertionString.Last() == nsCRT::LF;
|
||||
EditorDOMPoint pointToPutCaret = insertTextResult.unwrap();
|
||||
EditorDOMPoint pointToPutCaret = insertTextResult.inspect()
|
||||
.EndOfInsertedTextRef()
|
||||
.To<EditorDOMPoint>();
|
||||
pointToPutCaret.SetInterlinePosition(
|
||||
endsWithLF ? InterlinePosition::StartOfNextLine
|
||||
: InterlinePosition::EndOfLine);
|
||||
|
|
|
@ -1315,22 +1315,23 @@ Result<EditorDOMPoint, nsresult> WhiteSpaceVisibilityKeeper::ReplaceText(
|
|||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
OwningNonNull<Document> document = *aHTMLEditor.GetDocument();
|
||||
Result<EditorDOMPoint, nsresult> insertTextResult =
|
||||
Result<InsertTextResult, nsresult> insertTextResult =
|
||||
aHTMLEditor.InsertTextWithTransaction(document, theString, pointToInsert);
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr() && insertTextResult.inspectErr() ==
|
||||
NS_ERROR_EDITOR_DESTROYED)) {
|
||||
NS_WARNING(
|
||||
"HTMLEditor::InsertTextWithTransaction() caused destroying the editor");
|
||||
return Err(NS_ERROR_EDITOR_DESTROYED);
|
||||
if (MOZ_UNLIKELY(insertTextResult.isErr())) {
|
||||
if (MOZ_UNLIKELY(insertTextResult.inspectErr() ==
|
||||
NS_ERROR_EDITOR_DESTROYED)) {
|
||||
NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed");
|
||||
return Err(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed, but ignored");
|
||||
// XXX Temporarily, set new insertion point to the original point.
|
||||
return pointToInsert;
|
||||
}
|
||||
if (insertTextResult.isOk()) {
|
||||
return insertTextResult.unwrap();
|
||||
}
|
||||
|
||||
NS_WARNING("HTMLEditor::InsertTextWithTransaction() failed, but ignored");
|
||||
|
||||
// XXX Temporarily, set new insertion point to the original point.
|
||||
return pointToInsert;
|
||||
insertTextResult.inspect().IgnoreCaretPointSuggestion();
|
||||
return insertTextResult.inspect().Handled() ? insertTextResult.inspect()
|
||||
.EndOfInsertedTextRef()
|
||||
.To<EditorDOMPoint>()
|
||||
: pointToInsert;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
Загрузка…
Ссылка в новой задаче