Source code

Revision control

Other Tools

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/. */
5
6
#include "TransactionItem.h"
7
8
#include "mozilla/mozalloc.h"
9
#include "mozilla/TransactionManager.h"
10
#include "mozilla/TransactionStack.h"
11
#include "nsCOMPtr.h"
12
#include "nsDebug.h"
13
#include "nsError.h"
14
#include "nsISupportsImpl.h"
15
#include "nsITransaction.h"
16
17
namespace mozilla {
18
19
TransactionItem::TransactionItem(nsITransaction* aTransaction)
20
: mTransaction(aTransaction), mUndoStack(0), mRedoStack(0) {}
21
22
TransactionItem::~TransactionItem() {
23
delete mRedoStack;
24
delete mUndoStack;
25
}
26
27
void TransactionItem::CleanUp() {
28
mData.Clear();
29
mTransaction = nullptr;
30
if (mRedoStack) {
31
mRedoStack->DoUnlink();
32
}
33
if (mUndoStack) {
34
mUndoStack->DoUnlink();
35
}
36
}
37
38
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(TransactionItem)
39
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(TransactionItem,
40
CleanUp())
41
42
NS_IMPL_CYCLE_COLLECTION_CLASS(TransactionItem)
43
44
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TransactionItem)
45
tmp->CleanUp();
46
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
47
48
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TransactionItem)
49
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
50
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
51
if (tmp->mRedoStack) {
52
tmp->mRedoStack->DoTraverse(cb);
53
}
54
if (tmp->mUndoStack) {
55
tmp->mUndoStack->DoTraverse(cb);
56
}
57
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
58
59
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(TransactionItem, AddRef)
60
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(TransactionItem, Release)
61
62
nsresult TransactionItem::AddChild(TransactionItem* aTransactionItem) {
63
NS_ENSURE_TRUE(aTransactionItem, NS_ERROR_NULL_POINTER);
64
65
if (!mUndoStack) {
66
mUndoStack = new TransactionStack(TransactionStack::FOR_UNDO);
67
}
68
69
mUndoStack->Push(aTransactionItem);
70
return NS_OK;
71
}
72
73
already_AddRefed<nsITransaction> TransactionItem::GetTransaction() {
74
nsCOMPtr<nsITransaction> txn = mTransaction;
75
return txn.forget();
76
}
77
78
nsresult TransactionItem::GetIsBatch(bool* aIsBatch) {
79
NS_ENSURE_TRUE(aIsBatch, NS_ERROR_NULL_POINTER);
80
*aIsBatch = !mTransaction;
81
return NS_OK;
82
}
83
84
nsresult TransactionItem::GetNumberOfChildren(int32_t* aNumChildren) {
85
NS_ENSURE_TRUE(aNumChildren, NS_ERROR_NULL_POINTER);
86
87
*aNumChildren = 0;
88
89
int32_t ui = 0;
90
nsresult rv = GetNumberOfUndoItems(&ui);
91
NS_ENSURE_SUCCESS(rv, rv);
92
93
int32_t ri = 0;
94
rv = GetNumberOfRedoItems(&ri);
95
NS_ENSURE_SUCCESS(rv, rv);
96
97
*aNumChildren = ui + ri;
98
return NS_OK;
99
}
100
101
nsresult TransactionItem::GetChild(int32_t aIndex, TransactionItem** aChild) {
102
NS_ENSURE_TRUE(aChild, NS_ERROR_NULL_POINTER);
103
104
*aChild = 0;
105
106
int32_t numItems = 0;
107
nsresult rv = GetNumberOfChildren(&numItems);
108
NS_ENSURE_SUCCESS(rv, rv);
109
if (aIndex < 0 || aIndex >= numItems) {
110
return NS_ERROR_FAILURE;
111
}
112
113
// Children are expected to be in the order they were added,
114
// so the child first added would be at the bottom of the undo
115
// stack, or if there are no items on the undo stack, it would
116
// be at the top of the redo stack.
117
rv = GetNumberOfUndoItems(&numItems);
118
NS_ENSURE_SUCCESS(rv, rv);
119
120
if (numItems > 0 && aIndex < numItems) {
121
NS_ENSURE_TRUE(mUndoStack, NS_ERROR_FAILURE);
122
123
RefPtr<TransactionItem> child = mUndoStack->GetItem(aIndex);
124
child.forget(aChild);
125
return *aChild ? NS_OK : NS_ERROR_FAILURE;
126
}
127
128
// Adjust the index for the redo stack:
129
aIndex -= numItems;
130
131
rv = GetNumberOfRedoItems(&numItems);
132
NS_ENSURE_SUCCESS(rv, rv);
133
NS_ENSURE_TRUE(mRedoStack && numItems != 0 && aIndex < numItems,
134
NS_ERROR_FAILURE);
135
136
RefPtr<TransactionItem> child = mRedoStack->GetItem(aIndex);
137
child.forget(aChild);
138
return *aChild ? NS_OK : NS_ERROR_FAILURE;
139
}
140
141
nsresult TransactionItem::DoTransaction() {
142
if (mTransaction) {
143
return mTransaction->DoTransaction();
144
}
145
return NS_OK;
146
}
147
148
nsresult TransactionItem::UndoTransaction(
149
TransactionManager* aTransactionManager) {
150
nsresult rv = UndoChildren(aTransactionManager);
151
if (NS_FAILED(rv)) {
152
RecoverFromUndoError(aTransactionManager);
153
return rv;
154
}
155
156
if (!mTransaction) {
157
return NS_OK;
158
}
159
160
rv = mTransaction->UndoTransaction();
161
if (NS_FAILED(rv)) {
162
RecoverFromUndoError(aTransactionManager);
163
return rv;
164
}
165
166
return NS_OK;
167
}
168
169
nsresult TransactionItem::UndoChildren(
170
TransactionManager* aTransactionManager) {
171
if (mUndoStack) {
172
if (!mRedoStack && mUndoStack) {
173
mRedoStack = new TransactionStack(TransactionStack::FOR_REDO);
174
}
175
176
/* Undo all of the transaction items children! */
177
int32_t sz = mUndoStack->GetSize();
178
179
nsresult rv = NS_OK;
180
while (sz-- > 0) {
181
RefPtr<TransactionItem> transactionItem = mUndoStack->Peek();
182
if (!transactionItem) {
183
return NS_ERROR_FAILURE;
184
}
185
186
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
187
bool doInterrupt = false;
188
rv = aTransactionManager->WillUndoNotify(transaction, &doInterrupt);
189
if (NS_FAILED(rv)) {
190
return rv;
191
}
192
if (doInterrupt) {
193
return NS_OK;
194
}
195
196
rv = transactionItem->UndoTransaction(aTransactionManager);
197
if (NS_SUCCEEDED(rv)) {
198
transactionItem = mUndoStack->Pop();
199
mRedoStack->Push(transactionItem.forget());
200
}
201
202
nsresult rv2 = aTransactionManager->DidUndoNotify(transaction, rv);
203
if (NS_SUCCEEDED(rv)) {
204
rv = rv2;
205
}
206
}
207
// XXX NS_OK if there is no Undo items or all methods work fine, otherwise,
208
// the result of the last item's UndoTransaction() or
209
// DidUndoNotify() if UndoTransaction() succeeded.
210
return rv;
211
}
212
213
return NS_OK;
214
}
215
216
nsresult TransactionItem::RedoTransaction(
217
TransactionManager* aTransactionManager) {
218
nsCOMPtr<nsITransaction> transaction(mTransaction);
219
if (transaction) {
220
nsresult rv = transaction->RedoTransaction();
221
NS_ENSURE_SUCCESS(rv, rv);
222
}
223
224
nsresult rv = RedoChildren(aTransactionManager);
225
if (NS_FAILED(rv)) {
226
RecoverFromRedoError(aTransactionManager);
227
return rv;
228
}
229
230
return NS_OK;
231
}
232
233
nsresult TransactionItem::RedoChildren(
234
TransactionManager* aTransactionManager) {
235
if (!mRedoStack) {
236
return NS_OK;
237
}
238
239
/* Redo all of the transaction items children! */
240
int32_t sz = mRedoStack->GetSize();
241
242
nsresult rv = NS_OK;
243
while (sz-- > 0) {
244
RefPtr<TransactionItem> transactionItem = mRedoStack->Peek();
245
if (!transactionItem) {
246
return NS_ERROR_FAILURE;
247
}
248
249
nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
250
bool doInterrupt = false;
251
rv = aTransactionManager->WillRedoNotify(transaction, &doInterrupt);
252
if (NS_FAILED(rv)) {
253
return rv;
254
}
255
if (doInterrupt) {
256
return NS_OK;
257
}
258
259
rv = transactionItem->RedoTransaction(aTransactionManager);
260
if (NS_SUCCEEDED(rv)) {
261
transactionItem = mRedoStack->Pop();
262
mUndoStack->Push(transactionItem.forget());
263
}
264
265
// XXX Shouldn't this DidRedoNotify()? (bug 1311626)
266
nsresult rv2 = aTransactionManager->DidUndoNotify(transaction, rv);
267
if (NS_SUCCEEDED(rv)) {
268
rv = rv2;
269
}
270
}
271
// XXX NS_OK if there is no Redo items or all methods work fine, otherwise,
272
// the result of the last item's RedoTransaction() or
273
// DidUndoNotify() if UndoTransaction() succeeded.
274
return rv;
275
}
276
277
nsresult TransactionItem::GetNumberOfUndoItems(int32_t* aNumItems) {
278
NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
279
280
if (!mUndoStack) {
281
*aNumItems = 0;
282
return NS_OK;
283
}
284
285
*aNumItems = mUndoStack->GetSize();
286
return *aNumItems ? NS_OK : NS_ERROR_FAILURE;
287
}
288
289
nsresult TransactionItem::GetNumberOfRedoItems(int32_t* aNumItems) {
290
NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
291
292
if (!mRedoStack) {
293
*aNumItems = 0;
294
return NS_OK;
295
}
296
297
*aNumItems = mRedoStack->GetSize();
298
return *aNumItems ? NS_OK : NS_ERROR_FAILURE;
299
}
300
301
nsresult TransactionItem::RecoverFromUndoError(
302
TransactionManager* aTransactionManager) {
303
// If this method gets called, we never got to the point where we
304
// successfully called UndoTransaction() for the transaction item itself.
305
// Just redo any children that successfully called undo!
306
return RedoChildren(aTransactionManager);
307
}
308
309
nsresult TransactionItem::RecoverFromRedoError(
310
TransactionManager* aTransactionManager) {
311
// If this method gets called, we already successfully called
312
// RedoTransaction() for the transaction item itself. Undo all
313
// the children that successfully called RedoTransaction(),
314
// then undo the transaction item itself.
315
nsresult rv = UndoChildren(aTransactionManager);
316
if (NS_FAILED(rv)) {
317
return rv;
318
}
319
320
if (!mTransaction) {
321
return NS_OK;
322
}
323
324
return mTransaction->UndoTransaction();
325
}
326
327
} // namespace mozilla