Bug 1776056 - Switch to the tab an animation is running and make sure the animation...
[gecko.git] / editor / libeditor / DeleteNodeTransaction.cpp
blob26bf0168be0f830aac2ac183900b4e239cf1431a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DeleteNodeTransaction.h"
8 #include "HTMLEditUtils.h"
9 #include "mozilla/EditorBase.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/SelectionState.h" // RangeUpdater
12 #include "mozilla/TextEditor.h"
13 #include "mozilla/ToString.h"
14 #include "nsDebug.h"
15 #include "nsError.h"
16 #include "nsAString.h"
18 namespace mozilla {
20 // static
21 already_AddRefed<DeleteNodeTransaction> DeleteNodeTransaction::MaybeCreate(
22 EditorBase& aEditorBase, nsIContent& aContentToDelete) {
23 RefPtr<DeleteNodeTransaction> transaction =
24 new DeleteNodeTransaction(aEditorBase, aContentToDelete);
25 if (NS_WARN_IF(!transaction->CanDoIt())) {
26 return nullptr;
28 return transaction.forget();
31 DeleteNodeTransaction::DeleteNodeTransaction(EditorBase& aEditorBase,
32 nsIContent& aContentToDelete)
33 : mEditorBase(&aEditorBase),
34 mContentToDelete(&aContentToDelete),
35 mParentNode(aContentToDelete.GetParentNode()) {
36 MOZ_DIAGNOSTIC_ASSERT_IF(
37 aEditorBase.IsHTMLEditor(),
38 HTMLEditUtils::IsRemovableNode(aContentToDelete) ||
39 // It's okay to delete text node if it's added by `HTMLEditor` since
40 // remaining it may be noisy for the users.
41 (aContentToDelete.IsText() &&
42 aContentToDelete.HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)));
43 NS_ASSERTION(
44 !aEditorBase.IsHTMLEditor() ||
45 HTMLEditUtils::IsRemovableNode(aContentToDelete),
46 "Deleting non-editable text node, please write a test for this!!");
49 std::ostream& operator<<(std::ostream& aStream,
50 const DeleteNodeTransaction& aTransaction) {
51 aStream << "{ mContentToDelete=" << aTransaction.mContentToDelete.get();
52 if (aTransaction.mContentToDelete) {
53 aStream << " (" << *aTransaction.mContentToDelete << ")";
55 aStream << ", mParentNode=" << aTransaction.mParentNode.get();
56 if (aTransaction.mParentNode) {
57 aStream << " (" << *aTransaction.mParentNode << ")";
59 aStream << ", mRefContent=" << aTransaction.mRefContent.get();
60 if (aTransaction.mRefContent) {
61 aStream << " (" << *aTransaction.mRefContent << ")";
63 aStream << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
64 return aStream;
67 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteNodeTransaction, EditTransactionBase,
68 mEditorBase, mContentToDelete, mParentNode,
69 mRefContent)
71 NS_IMPL_ADDREF_INHERITED(DeleteNodeTransaction, EditTransactionBase)
72 NS_IMPL_RELEASE_INHERITED(DeleteNodeTransaction, EditTransactionBase)
73 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteNodeTransaction)
74 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
76 bool DeleteNodeTransaction::CanDoIt() const {
77 if (NS_WARN_IF(!mContentToDelete) || NS_WARN_IF(!mEditorBase) ||
78 !mParentNode) {
79 return false;
81 return mEditorBase->IsTextEditor() ||
82 HTMLEditUtils::IsSimplyEditableNode(*mParentNode);
85 NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
86 MOZ_LOG(GetLogModule(), LogLevel::Info,
87 ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__,
88 ToString(*this).c_str()));
90 if (NS_WARN_IF(!CanDoIt())) {
91 return NS_OK;
94 MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToDelete->IsText());
96 // Remember which child mContentToDelete was (by remembering which child was
97 // next). Note that mRefContent can be nullptr.
98 mRefContent = mContentToDelete->GetNextSibling();
100 // give range updater a chance. SelAdjDeleteNode() needs to be called
101 // *before* we do the action, unlike some of the other RangeItem update
102 // methods.
103 mEditorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete);
105 OwningNonNull<nsINode> parentNode = *mParentNode;
106 OwningNonNull<nsIContent> contentToDelete = *mContentToDelete;
107 ErrorResult error;
108 parentNode->RemoveChild(contentToDelete, error);
109 NS_WARNING_ASSERTION(!error.Failed(), "nsINode::RemoveChild() failed");
110 return error.StealNSResult();
113 NS_IMETHODIMP DeleteNodeTransaction::UndoTransaction() {
114 MOZ_LOG(GetLogModule(), LogLevel::Info,
115 ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__,
116 ToString(*this).c_str()));
118 if (NS_WARN_IF(!CanDoIt())) {
119 // This is a legal state, the transaction is a no-op.
120 return NS_OK;
122 ErrorResult error;
123 OwningNonNull<EditorBase> editorBase = *mEditorBase;
124 OwningNonNull<nsINode> parentNode = *mParentNode;
125 OwningNonNull<nsIContent> contentToDelete = *mContentToDelete;
126 nsCOMPtr<nsIContent> refContent = mRefContent;
127 // XXX Perhaps, we should check `refContent` is a child of `parentNode`,
128 // and if it's not, we should stop undoing or something.
129 parentNode->InsertBefore(contentToDelete, refContent, error);
130 // InsertBefore() may call MightThrowJSException() even if there is no error.
131 // We don't need the flag here.
132 error.WouldReportJSException();
133 if (error.Failed()) {
134 NS_WARNING("nsINode::InsertBefore() failed");
135 return error.StealNSResult();
137 return NS_OK;
140 NS_IMETHODIMP DeleteNodeTransaction::RedoTransaction() {
141 MOZ_LOG(GetLogModule(), LogLevel::Info,
142 ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__,
143 ToString(*this).c_str()));
145 if (NS_WARN_IF(!CanDoIt())) {
146 // This is a legal state, the transaction is a no-op.
147 return NS_OK;
150 mEditorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete);
152 OwningNonNull<nsINode> parentNode = *mParentNode;
153 OwningNonNull<nsIContent> contentToDelete = *mContentToDelete;
154 ErrorResult error;
155 parentNode->RemoveChild(contentToDelete, error);
156 NS_WARNING_ASSERTION(!error.Failed(), "nsINode::RemoveChild() failed");
157 return error.StealNSResult();
160 } // namespace mozilla