Bug 1762115 - part 13: Make EditorBase::InsertNodeWithTransaction stop touching Selection directly r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D144656
This commit is contained in:
Masayuki Nakano 2022-05-03 01:21:51 +00:00
parent 6ce22cc7c4
commit fef1b61ead
8 changed files with 295 additions and 119 deletions

View File

@ -147,6 +147,13 @@ template EditorRawDOMPoint EditorBase::GetFirstIMESelectionStartPoint() const;
template EditorDOMPoint EditorBase::GetLastIMESelectionEndPoint() const;
template EditorRawDOMPoint EditorBase::GetLastIMESelectionEndPoint() const;
template CreateContentResult EditorBase::InsertNodeWithTransaction(
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
template CreateElementResult EditorBase::InsertNodeWithTransaction(
Element& aContentToInsert, const EditorDOMPoint& aPointToInsert);
template CreateTextResult EditorBase::InsertNodeWithTransaction(
Text& aContentToInsert, const EditorDOMPoint& aPointToInsert);
template EditorDOMPoint EditorBase::GetFirstSelectionStartPoint() const;
template EditorRawDOMPoint EditorBase::GetFirstSelectionStartPoint() const;
template EditorDOMPoint EditorBase::GetFirstSelectionEndPoint() const;
@ -2032,21 +2039,37 @@ NS_IMETHODIMP EditorBase::InsertNode(nsINode* aNodeToInsert,
return EditorBase::ToGenericNSResult(rv);
}
uint32_t offset = std::min(aOffset, aContainer->Length());
rv = InsertNodeWithTransaction(*contentToInsert,
EditorDOMPoint(aContainer, offset));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::InsertNodeWithTransaction() failed");
return EditorBase::ToGenericNSResult(rv);
const uint32_t offset = std::min(aOffset, aContainer->Length());
CreateContentResult insertContentResult = InsertNodeWithTransaction(
*contentToInsert, EditorDOMPoint(aContainer, offset));
if (insertContentResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return EditorBase::ToGenericNSResult(insertContentResult.unwrapErr());
}
rv = insertContentResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateContentResult::SuggestCaretPointTo() failed");
return EditorBase::ToGenericNSResult(rv);
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateContentResult::SuggestCaretPointTo() failed, but ignored");
return NS_OK;
}
nsresult EditorBase::InsertNodeWithTransaction(
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert) {
template <typename ContentNodeType>
CreateNodeResultBase<ContentNodeType> EditorBase::InsertNodeWithTransaction(
ContentNodeType& aContentToInsert, const EditorDOMPoint& aPointToInsert) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT_IF(IsTextEditor(), !aContentToInsert.IsText());
using ResultType = CreateNodeResultBase<ContentNodeType>;
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
return NS_ERROR_INVALID_ARG;
return ResultType(NS_ERROR_INVALID_ARG);
}
MOZ_ASSERT(aPointToInsert.IsSetAndValid());
@ -2054,7 +2077,7 @@ nsresult EditorBase::InsertNodeWithTransaction(
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eInsertNode, nsIEditor::eNext, ignoredError);
if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
return ignoredError.StealNSResult();
return ResultType(ignoredError.StealNSResult());
}
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
@ -2066,17 +2089,6 @@ nsresult EditorBase::InsertNodeWithTransaction(
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DoTransactionInternal() failed");
if (NS_SUCCEEDED(rv) && AllowsTransactionsToChangeSelection()) {
const auto pointToPutCaret =
transaction->SuggestPointToPutCaret<EditorRawDOMPoint>();
if (pointToPutCaret.IsSet()) {
DebugOnly<nsresult> rvIgnored = CollapseSelectionTo(pointToPutCaret);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored) || rvIgnored == NS_ERROR_EDITOR_DESTROYED,
"EditorBase::CollapseSelectionTo() failed, but ignored");
}
}
DebugOnly<nsresult> rvIgnored =
RangeUpdaterRef().SelAdjInsertNode(aPointToInsert);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
@ -2086,7 +2098,15 @@ nsresult EditorBase::InsertNodeWithTransaction(
TopLevelEditSubActionDataRef().DidInsertContent(*this, aContentToInsert);
}
return MOZ_UNLIKELY(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : rv;
if (NS_WARN_IF(Destroyed())) {
return ResultType(NS_ERROR_EDITOR_DESTROYED);
}
if (NS_FAILED(rv)) {
return ResultType(rv);
}
return ResultType(&aContentToInsert,
transaction->SuggestPointToPutCaret<EditorDOMPoint>());
}
CreateElementResult
@ -2118,15 +2138,24 @@ EditorBase::InsertPaddingBRElementForEmptyLastLineWithTransaction(
}
newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
nsresult rv = InsertNodeWithTransaction(*newBRElement, pointToInsert);
if (NS_WARN_IF(Destroyed())) {
return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
}
if (NS_FAILED(rv)) {
CreateElementResult insertBRElementResult =
InsertNodeWithTransaction<Element>(*newBRElement, pointToInsert);
if (insertBRElementResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return CreateElementResult(rv);
return CreateElementResult(insertBRElementResult.unwrapErr());
}
nsresult rv = insertBRElementResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateElementResult::SuggestCaretPointTo() failed");
return CreateElementResult(rv);
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateElementResult::SuggestCaretPointTo() failed, but ignored");
return CreateElementResult(std::move(newBRElement));
}
@ -2830,25 +2859,34 @@ Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
CheckedUint32 newOffset;
if (!pointToInsert.IsInTextNode()) {
// create a text node
RefPtr<nsTextNode> newNode = CreateTextNode(u""_ns);
if (MOZ_UNLIKELY(NS_WARN_IF(!newNode))) {
RefPtr<nsTextNode> newTextNode = CreateTextNode(u""_ns);
if (NS_WARN_IF(!newTextNode)) {
return Err(NS_ERROR_FAILURE);
}
// then we insert it into the dom tree
nsresult rv = InsertNodeWithTransaction(*newNode, pointToInsert);
if (MOZ_UNLIKELY(NS_WARN_IF(Destroyed()))) {
return Err(NS_ERROR_EDITOR_DESTROYED);
}
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
CreateTextResult insertTextNodeResult =
InsertNodeWithTransaction<Text>(*newTextNode, pointToInsert);
if (insertTextNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return Err(insertTextNodeResult.unwrapErr());
}
nsresult rv = insertTextNodeResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateTextResult::SuggestCaretPointTo() failed");
return Err(rv);
}
pointToInsert.Set(newNode, 0u);
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateTextResult::SuggestCaretPointTo() failed, but ignored");
pointToInsert.Set(newTextNode, 0u);
newOffset = aStringToInsert.Length();
} else {
newOffset = aStringToInsert.Length();
newOffset += pointToInsert.Offset();
if (MOZ_UNLIKELY(NS_WARN_IF(!newOffset.isValid()))) {
if (NS_WARN_IF(!newOffset.isValid())) {
return Err(NS_ERROR_FAILURE);
}
}
@ -2860,7 +2898,7 @@ Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
"destroying the editor");
return Err(NS_ERROR_EDITOR_DESTROYED);
}
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::InsertTextIntoTextNodeWithTransaction() failed");
return Err(rv);
}
@ -2870,7 +2908,7 @@ Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
if (pointToInsert.IsInTextNode()) {
CheckedUint32 newOffset = aStringToInsert.Length();
newOffset += pointToInsert.Offset();
if (MOZ_UNLIKELY(NS_WARN_IF(!newOffset.isValid()))) {
if (NS_WARN_IF(!newOffset.isValid())) {
return Err(NS_ERROR_FAILURE);
}
// we are inserting text into an existing text node.
@ -2883,7 +2921,7 @@ Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
"destroying the editor");
return Err(NS_ERROR_EDITOR_DESTROYED);
}
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::InsertTextIntoTextNodeWithTransaction() failed");
return Err(rv);
}
@ -2892,22 +2930,30 @@ Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
// we are inserting text into a non-text node. first we have to create a
// textnode (this also populates it with the text)
RefPtr<nsTextNode> newNode = CreateTextNode(aStringToInsert);
if (MOZ_UNLIKELY(NS_WARN_IF(!newNode))) {
RefPtr<nsTextNode> newTextNode = CreateTextNode(aStringToInsert);
if (NS_WARN_IF(!newTextNode)) {
return Err(NS_ERROR_FAILURE);
}
// then we insert it into the dom tree
nsresult rv = InsertNodeWithTransaction(*newNode, pointToInsert);
if (MOZ_UNLIKELY(Destroyed())) {
NS_WARNING(
"EditorBase::InsertNodeWithTransaction() caused destroying the editor");
return Err(NS_ERROR_EDITOR_DESTROYED);
}
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
CreateTextResult insertTextNodeResult =
InsertNodeWithTransaction<Text>(*newTextNode, pointToInsert);
if (insertTextNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return Err(insertTextNodeResult.unwrapErr());
}
nsresult rv = insertTextNodeResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateTextResult::SuggestCaretPointTo() failed");
return Err(rv);
}
return EditorDOMPoint(newNode, aStringToInsert.Length());
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateTextResult::SuggestCaretPointTo() failed, but ignored");
return EditorDOMPoint(insertTextNodeResult.UnwrapNewNode(),
aStringToInsert.Length());
}
static bool TextFragmentBeginsWithStringAtOffset(

View File

@ -73,7 +73,6 @@ class PresShell;
class TextComposition;
class TextInputListener;
class TextServicesDocument;
namespace dom {
class AbstractRange;
class DataTransfer;
@ -1694,9 +1693,13 @@ class EditorBase : public nsIEditor,
* transaction will append the node to the
* container. Otherwise, will insert the node
* before child node referred by this.
* @return If succeeded, returns the new content node and
* point to put caret.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertNodeWithTransaction(
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
template <typename ContentNodeType>
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateNodeResultBase<ContentNodeType>
InsertNodeWithTransaction(ContentNodeType& aContentToInsert,
const EditorDOMPoint& aPointToInsert);
/**
* InsertPaddingBRElementForEmptyLastLineWithTransaction() creates a padding

View File

@ -30,8 +30,6 @@
class nsITransferable;
namespace mozilla {
template <class T>
class OwningNonNull;
/***************************************************************************
* EditActionResult is useful to return multiple results of an editor

View File

@ -16,6 +16,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/ContentIterator.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorForwards.h"
#include "mozilla/EditorUtils.h" // for SuggestCaretOption(s)
#include "mozilla/IntegerRange.h"
#include "mozilla/RangeBoundary.h"
@ -31,8 +32,6 @@
class nsISimpleEnumerator;
namespace mozilla {
template <class T>
class OwningNonNull;
// JoinNodesDirection is also affected to which one is new node at splitting
// a node because a couple of undo/redo.
@ -973,9 +972,6 @@ class MOZ_STACK_CLASS ReplaceRangeDataBase final {
nsString mReplaceString;
};
using ReplaceRangeData = ReplaceRangeDataBase<EditorDOMPoint>;
using ReplaceRangeInTextsData = ReplaceRangeDataBase<EditorDOMPointInText>;
} // namespace mozilla
#endif // #ifndef mozilla_HTMLEditHelpers_h

View File

@ -922,18 +922,17 @@ nsresult HTMLEditor::MaybeCreatePaddingBRElementForEmptyEditor() {
newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
// Put the node in the document.
nsresult rv =
InsertNodeWithTransaction(*newBRElement, EditorDOMPoint(rootElement, 0));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
CreateElementResult insertBRElementResult =
InsertNodeWithTransaction<Element>(*newBRElement,
EditorDOMPoint(rootElement, 0u));
if (insertBRElementResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertBRElementResult.unwrapErr();
}
// Set selection.
rv = CollapseSelectionToStartOf(*rootElement);
insertBRElementResult.IgnoreCaretPointSuggestion();
nsresult rv = CollapseSelectionToStartOf(*rootElement);
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
NS_WARNING(
"EditorBase::CollapseSelectionToStartOf() caused destroying the "
@ -5410,15 +5409,14 @@ nsresult HTMLEditor::CreateStyleForInsertText(
NS_WARNING("EditorBase::CreateTextNode() failed");
return NS_ERROR_FAILURE;
}
nsresult rv = InsertNodeWithTransaction(*newEmptyTextNode, pointToPutCaret);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
CreateTextResult insertNewTextNodeResult =
InsertNodeWithTransaction<Text>(*newEmptyTextNode, pointToPutCaret);
if (insertNewTextNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertNewTextNodeResult.unwrapErr();
}
pointToPutCaret.Set(newEmptyTextNode, 0);
insertNewTextNodeResult.IgnoreCaretPointSuggestion();
pointToPutCaret.Set(newEmptyTextNode, 0u);
putCaret = true;
if (relFontSize) {

View File

@ -1462,12 +1462,15 @@ nsresult HTMLEditor::ReplaceHeadContentsWithSourceWithTransaction(
// Loop over the contents of the fragment and move into the document
while (nsCOMPtr<nsIContent> child = documentFragment->GetFirstChild()) {
nsresult rv = InsertNodeWithTransaction(
CreateContentResult insertChildContentResult = InsertNodeWithTransaction(
*child, EditorDOMPoint(primaryHeadElement, offsetOfNewNode++));
if (NS_FAILED(rv)) {
if (insertChildContentResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertChildContentResult.unwrapErr();
}
// We probably don't need to adjust selection here, although we've done it
// unless AutoTransactionsConserveSelection is created in a caller.
insertChildContentResult.IgnoreCaretPointSuggestion();
}
return NS_OK;
@ -1974,11 +1977,23 @@ EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
// when it's necessary.
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
// Now we can insert the new node.
nsresult rv = InsertNodeWithTransaction(aNode, pointToInsert);
if (NS_FAILED(rv)) {
CreateContentResult insertContentNodeResult =
InsertNodeWithTransaction(aNode, pointToInsert);
if (insertContentNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return EditorDOMPoint();
}
nsresult rv = insertContentNodeResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateContentResult::SuggestCaretPointTo() failed");
return EditorDOMPoint();
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateContentResult::SuggestCaretPointTo() failed, but ignored");
}
return pointToInsert;
}
@ -3773,21 +3788,39 @@ already_AddRefed<Element> HTMLEditor::InsertContainerWithTransactionInternal(
}
{
// TODO: Remove AutoTransactionsConserveSelection here. It's not necessary
// in normal cases. However, it may be required for nested edit
// actions which may be caused by legacy mutation event listeners or
// chrome script.
AutoTransactionsConserveSelection conserveSelection(*this);
rv = InsertNodeWithTransaction(aContent, EditorDOMPoint(newContainer, 0));
if (NS_FAILED(rv)) {
CreateContentResult insertContentNodeResult =
InsertNodeWithTransaction(aContent, EditorDOMPoint(newContainer, 0u));
if (insertContentNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return nullptr;
}
insertContentNodeResult.IgnoreCaretPointSuggestion();
}
// Put the new container where aNode was.
rv = InsertNodeWithTransaction(*newContainer, pointToInsertNewContainer);
if (NS_FAILED(rv)) {
CreateElementResult insertNewContainerElementResult =
InsertNodeWithTransaction<Element>(*newContainer,
pointToInsertNewContainer);
if (insertNewContainerElementResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return nullptr;
}
rv = insertNewContainerElementResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateElementResult::SuggestCaretPointTo() failed");
return nullptr;
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateElementResult::SuggestCaretPointTo() failed, but ignored");
return newContainer.forget();
}
@ -3838,23 +3871,36 @@ already_AddRefed<Element> HTMLEditor::ReplaceContainerWithTransactionInternal(
return nullptr;
}
rv = InsertNodeWithTransaction(
*child, EditorDOMPoint(newContainer, newContainer->Length()));
if (NS_FAILED(rv)) {
CreateContentResult insertChildContentResult = InsertNodeWithTransaction(
*child, EditorDOMPoint::AtEndOf(newContainer));
if (insertChildContentResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return nullptr;
}
insertChildContentResult.IgnoreCaretPointSuggestion();
}
}
// Insert new container into tree.
NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
"The old container might be moved by mutation observer");
nsresult rv = InsertNodeWithTransaction(*newContainer, atOldContainer);
if (NS_FAILED(rv)) {
CreateElementResult insertNewContainerElementResult =
InsertNodeWithTransaction<Element>(*newContainer, atOldContainer);
if (insertNewContainerElementResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return nullptr;
}
nsresult rv = insertNewContainerElementResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateElementResult::SuggestCaretPointTo() failed");
return nullptr;
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateElementResult::SuggestCaretPointTo() failed, but ignored");
// Delete old container.
rv = DeleteNodeWithTransaction(aOldContainer);
@ -3879,6 +3925,7 @@ nsresult HTMLEditor::RemoveContainerWithTransaction(Element& aElement) {
pointToInsertChildren);
// Move all children from aNode to its parent.
EditorDOMPoint pointToPutCaret;
while (aElement.HasChildren()) {
nsCOMPtr<nsIContent> child = aElement.GetLastChild();
if (NS_WARN_IF(!child)) {
@ -3893,13 +3940,29 @@ nsresult HTMLEditor::RemoveContainerWithTransaction(Element& aElement) {
// Insert the last child before the previous last child. So, we need to
// use offset here because previous child might have been moved to
// container.
rv = InsertNodeWithTransaction(
CreateContentResult insertChildContentResult = InsertNodeWithTransaction(
*child, EditorDOMPoint(pointToInsertChildren.GetContainer(),
pointToInsertChildren.Offset()));
if (NS_FAILED(rv)) {
if (insertChildContentResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertChildContentResult.unwrapErr();
}
insertChildContentResult.MoveCaretPointTo(
pointToPutCaret, *this,
{SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
}
if (pointToPutCaret.IsSet()) {
nsresult rv = CollapseSelectionTo(pointToPutCaret);
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
NS_WARNING(
"EditorBase::CollapseSelectionTo() caused destring the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::CollapseSelectionTo() failed, but ignored");
}
nsresult rv = DeleteNodeWithTransaction(aElement);
@ -5089,10 +5152,24 @@ nsresult HTMLEditor::MoveNodeWithTransaction(
if (NS_WARN_IF(!pointToInsert.IsSetAndValid())) {
pointToInsert.SetToEndOf(pointToInsert.GetContainer());
}
rv = InsertNodeWithTransaction(aContent, pointToInsert);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::InsertNodeWithTransaction() failed");
return rv;
CreateContentResult insertContentNodeResult =
InsertNodeWithTransaction(aContent, pointToInsert);
if (insertContentNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return insertContentNodeResult.unwrapErr();
}
rv = insertContentNodeResult.SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt,
SuggestCaret::AndIgnoreTrivialError});
if (NS_FAILED(rv)) {
NS_WARNING("CreateContentResult::SuggestCaretPointTo() failed");
return rv;
}
NS_WARNING_ASSERTION(
rv != NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR,
"CreateContentResult::SuggestCaretPointTo() failed, but ignored");
return NS_OK;
}
Result<RefPtr<Element>, nsresult> HTMLEditor::DeleteSelectionAndCreateElement(

View File

@ -199,13 +199,19 @@ nsresult HTMLEditor::LoadHTML(const nsAString& aInputString) {
// changes the behavior when inserted child is moved by mutation
// observer. We need to investigate what we should do here.
Unused << pointToInsert.Offset();
EditorDOMPoint pointToPutCaret;
for (nsCOMPtr<nsIContent> contentToInsert = documentFragment->GetFirstChild();
contentToInsert; contentToInsert = documentFragment->GetFirstChild()) {
rv = InsertNodeWithTransaction(*contentToInsert, pointToInsert);
if (NS_FAILED(rv)) {
CreateContentResult insertChildContentNodeResult =
InsertNodeWithTransaction(*contentToInsert, pointToInsert);
if (insertChildContentNodeResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertChildContentNodeResult.unwrapErr();
}
insertChildContentNodeResult.MoveCaretPointTo(
pointToPutCaret, *this,
{SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
// XXX If the inserted node has been moved by mutation observer,
// incrementing offset will cause odd result. Next new node
// will be inserted after existing node and the offset will be
@ -218,6 +224,17 @@ nsresult HTMLEditor::LoadHTML(const nsAString& aInputString) {
}
}
if (pointToPutCaret.IsSet()) {
nsresult rv = CollapseSelectionTo(pointToPutCaret);
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
NS_WARNING("EditorBase::CollapseSelectionTo() failed, but ignored");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::CollapseSelectionTo() failed, but ignored");
}
return NS_OK;
}

View File

@ -13,6 +13,7 @@
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorUtils.h"
#include "mozilla/FlushType.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/Element.h"
@ -135,12 +136,21 @@ nsresult HTMLEditor::InsertCell(Element* aCell, int32_t aRowSpan,
"Failed to advance offset to after the old cell");
}
// Don't let Rules System change the selection.
// TODO: Remove AutoTransactionsConserveSelection here. It's not necessary
// in normal cases. However, it may be required for nested edit
// actions which may be caused by legacy mutation event listeners or
// chrome script.
AutoTransactionsConserveSelection dontChangeSelection(*this);
nsresult rv = InsertNodeWithTransaction(*newCell, pointToInsert);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::InsertNodeWithTransaction() failed");
return rv;
CreateElementResult insertNewCellResult =
InsertNodeWithTransaction<Element>(*newCell, pointToInsert);
if (insertNewCellResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return insertNewCellResult.unwrapErr();
}
// Because of dontChangeSelection, we've never allowed to transactions to
// update selection here.
insertNewCellResult.IgnoreCaretPointSuggestion();
return NS_OK;
}
nsresult HTMLEditor::SetColSpan(Element* aCell, int32_t aColSpan) {
@ -254,7 +264,10 @@ nsresult HTMLEditor::InsertTableCellsWithTransaction(
AutoSelectionSetterAfterTableEdit setCaret(
*this, table, cellDataAtSelection.mCurrent.mRow, newCellIndex,
ePreviousColumn, false);
// So, suppress Rules System selection munging.
// TODO: Remove AutoTransactionsConserveSelection here. It's not necessary
// in normal cases. However, it may be required for nested edit
// actions which may be caused by legacy mutation event listeners or
// chrome script.
AutoTransactionsConserveSelection dontChangeSelection(*this);
EditorDOMPoint pointToInsert(cellParent, cellOffset);
@ -266,18 +279,23 @@ nsresult HTMLEditor::InsertTableCellsWithTransaction(
NS_WARNING_ASSERTION(advanced,
"Failed to move insertion point after the cell");
}
for (int32_t i = 0; i < aNumberOfCellsToInsert; i++) {
for ([[maybe_unused]] const auto i :
IntegerRange<uint32_t>(aNumberOfCellsToInsert)) {
RefPtr<Element> newCell = CreateElementWithDefaults(*nsGkAtoms::td);
if (!newCell) {
if (MOZ_UNLIKELY(!newCell)) {
NS_WARNING("HTMLEditor::CreateElementWithDefaults(nsGkAtoms::td) failed");
return NS_ERROR_FAILURE;
}
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
nsresult rv = InsertNodeWithTransaction(*newCell, pointToInsert);
if (NS_FAILED(rv)) {
CreateElementResult insertNewCellResult =
InsertNodeWithTransaction<Element>(*newCell, pointToInsert);
if (insertNewCellResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertNewCellResult.unwrapErr();
}
// Because of dontChangeSelection, we've never allowed to transactions to
// update selection here.
insertNewCellResult.IgnoreCaretPointSuggestion();
}
return NS_OK;
}
@ -762,7 +780,10 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction(
AutoSelectionSetterAfterTableEdit setCaret(
*this, table, startRowIndex, cellDataAtSelection.mCurrent.mColumn,
ePreviousColumn, false);
// Suppress Rules System selection munging.
// TODO: Remove AutoTransactionsConserveSelection here. It's not necessary
// in normal cases. However, it may be required for nested edit
// actions which may be caused by legacy mutation event listeners or
// chrome script.
AutoTransactionsConserveSelection dontChangeSelection(*this);
RefPtr<Element> cellForRowParent;
@ -890,11 +911,15 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction(
}
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
nsresult rv = InsertNodeWithTransaction(*newRow, pointToInsert);
if (NS_FAILED(rv)) {
CreateElementResult insertNewRowResult =
InsertNodeWithTransaction<Element>(*newRow, pointToInsert);
if (insertNewRowResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertNewRowResult.unwrapErr();
}
// Because of dontChangeSelection, we've never allowed to transactions to
// update selection here.
insertNewRowResult.IgnoreCaretPointSuggestion();
}
// SetSelectionAfterTableEdit from AutoSelectionSetterAfterTableEdit will
@ -3110,6 +3135,7 @@ nsresult HTMLEditor::MergeCells(RefPtr<Element> aTargetCell,
}
// Move the contents
EditorDOMPoint pointToPutCaret;
while (aCellToMerge->HasChildren()) {
nsCOMPtr<nsIContent> cellChild = aCellToMerge->GetLastChild();
if (NS_WARN_IF(!cellChild)) {
@ -3120,12 +3146,27 @@ nsresult HTMLEditor::MergeCells(RefPtr<Element> aTargetCell,
NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed");
return rv;
}
rv = InsertNodeWithTransaction(*cellChild,
EditorDOMPoint(aTargetCell, insertIndex));
if (NS_FAILED(rv)) {
CreateContentResult insertChildContentResult = InsertNodeWithTransaction(
*cellChild, EditorDOMPoint(aTargetCell, insertIndex));
if (insertChildContentResult.isErr()) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
return insertChildContentResult.unwrapErr();
}
insertChildContentResult.MoveCaretPointTo(
pointToPutCaret, *this,
{SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
}
if (pointToPutCaret.IsSet()) {
nsresult rv = CollapseSelectionTo(pointToPutCaret);
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
NS_WARNING(
"EditorBase::CollapseSelectionTo() caused destroying the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::CollapseSelectionTo() failed, but ignored");
}
}