Coverage Report

Created: 2018-09-25 14:53

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