Backed out changeset 88fbb17e3c20 (bug 1865637) for causing animation related mochite...
[gecko.git] / editor / txmgr / TransactionItem.cpp
blob26d114dfc98612c057b36ae37bfee3a2a0347359
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 "TransactionItem.h"
8 #include "mozilla/mozalloc.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/IntegerRange.h"
11 #include "mozilla/OwningNonNull.h"
12 #include "mozilla/TransactionManager.h"
13 #include "mozilla/TransactionStack.h"
14 #include "nsCOMPtr.h"
15 #include "nsDebug.h"
16 #include "nsError.h"
17 #include "nsISupportsImpl.h"
18 #include "nsITransaction.h"
20 namespace mozilla {
22 TransactionItem::TransactionItem(nsITransaction* aTransaction)
23 : mTransaction(aTransaction), mUndoStack(0), mRedoStack(0) {}
25 TransactionItem::~TransactionItem() {
26 delete mRedoStack;
27 delete mUndoStack;
30 void TransactionItem::CleanUp() {
31 mData.Clear();
32 mTransaction = nullptr;
33 if (mRedoStack) {
34 mRedoStack->DoUnlink();
36 if (mUndoStack) {
37 mUndoStack->DoUnlink();
41 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(TransactionItem)
42 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(TransactionItem,
43 CleanUp())
45 NS_IMPL_CYCLE_COLLECTION_CLASS(TransactionItem)
47 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TransactionItem)
48 tmp->CleanUp();
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
51 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TransactionItem)
52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
54 if (tmp->mRedoStack) {
55 tmp->mRedoStack->DoTraverse(cb);
57 if (tmp->mUndoStack) {
58 tmp->mUndoStack->DoTraverse(cb);
60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
62 nsresult TransactionItem::AddChild(TransactionItem& aTransactionItem) {
63 if (!mUndoStack) {
64 mUndoStack = new TransactionStack(TransactionStack::FOR_UNDO);
67 mUndoStack->Push(&aTransactionItem);
68 return NS_OK;
71 already_AddRefed<nsITransaction> TransactionItem::GetTransaction() {
72 return do_AddRef(mTransaction);
75 nsresult TransactionItem::DoTransaction() {
76 if (!mTransaction) {
77 return NS_OK;
79 OwningNonNull<nsITransaction> transaction = *mTransaction;
80 nsresult rv = transaction->DoTransaction();
81 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
82 "nsITransaction::DoTransaction() failed");
83 return rv;
86 nsresult TransactionItem::UndoTransaction(
87 TransactionManager* aTransactionManager) {
88 nsresult rv = UndoChildren(aTransactionManager);
89 if (NS_FAILED(rv)) {
90 NS_WARNING("TransactionItem::UndoChildren() failed");
91 DebugOnly<nsresult> rvIgnored = RecoverFromUndoError(aTransactionManager);
92 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
93 "TransactionItem::RecoverFromUndoError() failed");
94 return rv;
97 if (!mTransaction) {
98 return NS_OK;
101 OwningNonNull<nsITransaction> transaction = *mTransaction;
102 rv = transaction->UndoTransaction();
103 if (NS_SUCCEEDED(rv)) {
104 return NS_OK;
107 NS_WARNING("TransactionItem::UndoTransaction() failed");
108 DebugOnly<nsresult> rvIgnored = RecoverFromUndoError(aTransactionManager);
109 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
110 "TransactionItem::RecoverFromUndoError() failed");
111 return rv;
114 nsresult TransactionItem::UndoChildren(
115 TransactionManager* aTransactionManager) {
116 if (!mUndoStack) {
117 return NS_OK;
120 if (!mRedoStack) {
121 mRedoStack = new TransactionStack(TransactionStack::FOR_REDO);
124 const size_t undoStackSize = mUndoStack->GetSize();
126 nsresult rv = NS_OK;
127 for ([[maybe_unused]] const size_t undoneCount :
128 IntegerRange(undoStackSize)) {
129 RefPtr<TransactionItem> transactionItem = mUndoStack->Peek();
130 if (!transactionItem) {
131 return NS_ERROR_FAILURE;
134 nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
135 rv = transactionItem->UndoTransaction(aTransactionManager);
136 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
137 "TransactionItem::UndoTransaction() failed");
138 if (NS_SUCCEEDED(rv)) {
139 transactionItem = mUndoStack->Pop();
140 mRedoStack->Push(transactionItem.forget());
143 if (transaction) {
144 aTransactionManager->DidUndoNotify(*transaction, rv);
147 // NS_OK if there is no Undo items or all methods work fine, otherwise,
148 // the result of the last item's UndoTransaction().
149 return rv;
152 nsresult TransactionItem::RedoTransaction(
153 TransactionManager* aTransactionManager) {
154 nsCOMPtr<nsITransaction> transaction(mTransaction);
155 if (transaction) {
156 nsresult rv = transaction->RedoTransaction();
157 if (NS_FAILED(rv)) {
158 NS_WARNING("nsITransaction::RedoTransaction() failed");
159 return rv;
163 nsresult rv = RedoChildren(aTransactionManager);
164 if (NS_SUCCEEDED(rv)) {
165 return NS_OK;
168 NS_WARNING("TransactionItem::RedoChildren() failed");
169 DebugOnly<nsresult> rvIgnored = RecoverFromRedoError(aTransactionManager);
170 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
171 "TransactionItem::RecoverFromRedoError() failed");
172 return rv;
175 nsresult TransactionItem::RedoChildren(
176 TransactionManager* aTransactionManager) {
177 if (!mRedoStack) {
178 return NS_OK;
181 /* Redo all of the transaction items children! */
182 const size_t redoStackSize = mRedoStack->GetSize();
184 nsresult rv = NS_OK;
185 for ([[maybe_unused]] const size_t redoneCount :
186 IntegerRange(redoStackSize)) {
187 RefPtr<TransactionItem> transactionItem = mRedoStack->Peek();
188 if (NS_WARN_IF(!transactionItem)) {
189 return NS_ERROR_FAILURE;
192 nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
193 rv = transactionItem->RedoTransaction(aTransactionManager);
194 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
195 "TransactionItem::RedoTransaction() failed");
196 if (NS_SUCCEEDED(rv)) {
197 transactionItem = mRedoStack->Pop();
198 mUndoStack->Push(transactionItem.forget());
201 // XXX Shouldn't this DidRedoNotify()? (bug 1311626)
202 if (transaction) {
203 aTransactionManager->DidUndoNotify(*transaction, rv);
206 // NS_OK if there is no Redo items or all methods work fine, otherwise,
207 // the result of the last item's RedoTransaction().
208 return rv;
211 size_t TransactionItem::NumberOfUndoItems() const {
212 NS_WARNING_ASSERTION(!mUndoStack || mUndoStack->GetSize() > 0,
213 "UndoStack cannot have no children");
214 return mUndoStack ? mUndoStack->GetSize() : 0;
217 size_t TransactionItem::NumberOfRedoItems() const {
218 NS_WARNING_ASSERTION(!mRedoStack || mRedoStack->GetSize() > 0,
219 "UndoStack cannot have no children");
220 return mRedoStack ? mRedoStack->GetSize() : 0;
223 nsresult TransactionItem::RecoverFromUndoError(
224 TransactionManager* aTransactionManager) {
225 // If this method gets called, we never got to the point where we
226 // successfully called UndoTransaction() for the transaction item itself.
227 // Just redo any children that successfully called undo!
228 nsresult rv = RedoChildren(aTransactionManager);
229 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
230 "TransactionItem::RedoChildren() failed");
231 return rv;
234 nsresult TransactionItem::RecoverFromRedoError(
235 TransactionManager* aTransactionManager) {
236 // If this method gets called, we already successfully called
237 // RedoTransaction() for the transaction item itself. Undo all
238 // the children that successfully called RedoTransaction(),
239 // then undo the transaction item itself.
240 nsresult rv = UndoChildren(aTransactionManager);
241 if (NS_FAILED(rv)) {
242 NS_WARNING("TransactionItem::UndoChildren() failed");
243 return rv;
246 if (!mTransaction) {
247 return NS_OK;
250 OwningNonNull<nsITransaction> transaction = *mTransaction;
251 rv = transaction->UndoTransaction();
252 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
253 "nsITransaction::UndoTransaction() failed");
254 return rv;
257 } // namespace mozilla