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