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"
17 #include "nsISupportsImpl.h"
18 #include "nsITransaction.h"
22 TransactionItem::TransactionItem(nsITransaction
* aTransaction
)
23 : mTransaction(aTransaction
), mUndoStack(0), mRedoStack(0) {}
25 TransactionItem::~TransactionItem() {
30 void TransactionItem::CleanUp() {
32 mTransaction
= nullptr;
34 mRedoStack
->DoUnlink();
37 mUndoStack
->DoUnlink();
41 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(TransactionItem
)
42 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(TransactionItem
,
45 NS_IMPL_CYCLE_COLLECTION_CLASS(TransactionItem
)
47 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TransactionItem
)
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
) {
64 mUndoStack
= new TransactionStack(TransactionStack::FOR_UNDO
);
67 mUndoStack
->Push(&aTransactionItem
);
71 already_AddRefed
<nsITransaction
> TransactionItem::GetTransaction() {
72 return do_AddRef(mTransaction
);
75 nsresult
TransactionItem::DoTransaction() {
79 OwningNonNull
<nsITransaction
> transaction
= *mTransaction
;
80 nsresult rv
= transaction
->DoTransaction();
81 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
82 "nsITransaction::DoTransaction() failed");
86 nsresult
TransactionItem::UndoTransaction(
87 TransactionManager
* aTransactionManager
) {
88 nsresult rv
= UndoChildren(aTransactionManager
);
90 NS_WARNING("TransactionItem::UndoChildren() failed");
91 DebugOnly
<nsresult
> rvIgnored
= RecoverFromUndoError(aTransactionManager
);
92 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
93 "TransactionItem::RecoverFromUndoError() failed");
101 OwningNonNull
<nsITransaction
> transaction
= *mTransaction
;
102 rv
= transaction
->UndoTransaction();
103 if (NS_SUCCEEDED(rv
)) {
107 NS_WARNING("TransactionItem::UndoTransaction() failed");
108 DebugOnly
<nsresult
> rvIgnored
= RecoverFromUndoError(aTransactionManager
);
109 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
110 "TransactionItem::RecoverFromUndoError() failed");
114 nsresult
TransactionItem::UndoChildren(
115 TransactionManager
* aTransactionManager
) {
121 mRedoStack
= new TransactionStack(TransactionStack::FOR_REDO
);
124 const size_t undoStackSize
= mUndoStack
->GetSize();
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());
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().
152 nsresult
TransactionItem::RedoTransaction(
153 TransactionManager
* aTransactionManager
) {
154 nsCOMPtr
<nsITransaction
> transaction(mTransaction
);
156 nsresult rv
= transaction
->RedoTransaction();
158 NS_WARNING("nsITransaction::RedoTransaction() failed");
163 nsresult rv
= RedoChildren(aTransactionManager
);
164 if (NS_SUCCEEDED(rv
)) {
168 NS_WARNING("TransactionItem::RedoChildren() failed");
169 DebugOnly
<nsresult
> rvIgnored
= RecoverFromRedoError(aTransactionManager
);
170 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
171 "TransactionItem::RecoverFromRedoError() failed");
175 nsresult
TransactionItem::RedoChildren(
176 TransactionManager
* aTransactionManager
) {
181 /* Redo all of the transaction items children! */
182 const size_t redoStackSize
= mRedoStack
->GetSize();
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)
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().
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");
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
);
242 NS_WARNING("TransactionItem::UndoChildren() failed");
250 OwningNonNull
<nsITransaction
> transaction
= *mTransaction
;
251 rv
= transaction
->UndoTransaction();
252 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
253 "nsITransaction::UndoTransaction() failed");
257 } // namespace mozilla