Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "mozilla/mozalloc.h"
7 #include "nsAutoPtr.h"
8 #include "nsCOMPtr.h"
9 #include "nsDebug.h"
10 #include "nsError.h"
11 #include "nsISupportsImpl.h"
12 #include "nsITransaction.h"
13 #include "nsTransactionItem.h"
14 #include "nsTransactionManager.h"
15 #include "nsTransactionStack.h"
17 nsTransactionItem::nsTransactionItem(nsITransaction *aTransaction)
18 : mTransaction(aTransaction), mUndoStack(0), mRedoStack(0)
19 {
20 }
22 nsTransactionItem::~nsTransactionItem()
23 {
24 delete mRedoStack;
26 delete mUndoStack;
27 }
29 void
30 nsTransactionItem::CleanUp()
31 {
32 mData.Clear();
33 mTransaction = nullptr;
34 if (mRedoStack) {
35 mRedoStack->DoUnlink();
36 }
37 if (mUndoStack) {
38 mUndoStack->DoUnlink();
39 }
40 }
42 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(nsTransactionItem)
43 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(nsTransactionItem,
44 CleanUp())
46 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionItem)
48 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionItem)
49 tmp->CleanUp();
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionItem)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
55 if (tmp->mRedoStack) {
56 tmp->mRedoStack->DoTraverse(cb);
57 }
58 if (tmp->mUndoStack) {
59 tmp->mUndoStack->DoTraverse(cb);
60 }
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
63 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTransactionItem, AddRef)
64 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTransactionItem, Release)
66 nsresult
67 nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem)
68 {
69 NS_ENSURE_TRUE(aTransactionItem, NS_ERROR_NULL_POINTER);
71 if (!mUndoStack) {
72 mUndoStack = new nsTransactionStack(nsTransactionStack::FOR_UNDO);
73 }
75 mUndoStack->Push(aTransactionItem);
77 return NS_OK;
78 }
80 already_AddRefed<nsITransaction>
81 nsTransactionItem::GetTransaction()
82 {
83 nsCOMPtr<nsITransaction> txn = mTransaction;
84 return txn.forget();
85 }
87 nsresult
88 nsTransactionItem::GetIsBatch(bool *aIsBatch)
89 {
90 NS_ENSURE_TRUE(aIsBatch, NS_ERROR_NULL_POINTER);
92 *aIsBatch = !mTransaction;
94 return NS_OK;
95 }
97 nsresult
98 nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren)
99 {
100 nsresult result;
102 NS_ENSURE_TRUE(aNumChildren, NS_ERROR_NULL_POINTER);
104 *aNumChildren = 0;
106 int32_t ui = 0;
107 int32_t ri = 0;
109 result = GetNumberOfUndoItems(&ui);
111 NS_ENSURE_SUCCESS(result, result);
113 result = GetNumberOfRedoItems(&ri);
115 NS_ENSURE_SUCCESS(result, result);
117 *aNumChildren = ui + ri;
119 return NS_OK;
120 }
122 nsresult
123 nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
124 {
125 NS_ENSURE_TRUE(aChild, NS_ERROR_NULL_POINTER);
127 *aChild = 0;
129 int32_t numItems = 0;
130 nsresult result = GetNumberOfChildren(&numItems);
132 NS_ENSURE_SUCCESS(result, result);
134 if (aIndex < 0 || aIndex >= numItems)
135 return NS_ERROR_FAILURE;
137 // Children are expected to be in the order they were added,
138 // so the child first added would be at the bottom of the undo
139 // stack, or if there are no items on the undo stack, it would
140 // be at the top of the redo stack.
142 result = GetNumberOfUndoItems(&numItems);
144 NS_ENSURE_SUCCESS(result, result);
146 if (numItems > 0 && aIndex < numItems) {
147 NS_ENSURE_TRUE(mUndoStack, NS_ERROR_FAILURE);
149 nsRefPtr<nsTransactionItem> child = mUndoStack->GetItem(aIndex);
150 child.forget(aChild);
151 return *aChild ? NS_OK : NS_ERROR_FAILURE;
152 }
154 // Adjust the index for the redo stack:
156 aIndex -= numItems;
158 result = GetNumberOfRedoItems(&numItems);
160 NS_ENSURE_SUCCESS(result, result);
162 NS_ENSURE_TRUE(mRedoStack && numItems != 0 && aIndex < numItems, NS_ERROR_FAILURE);
164 nsRefPtr<nsTransactionItem> child = mRedoStack->GetItem(aIndex);
165 child.forget(aChild);
166 return *aChild ? NS_OK : NS_ERROR_FAILURE;
167 }
169 nsresult
170 nsTransactionItem::DoTransaction()
171 {
172 if (mTransaction)
173 return mTransaction->DoTransaction();
174 return NS_OK;
175 }
177 nsresult
178 nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
179 {
180 nsresult result = UndoChildren(aTxMgr);
182 if (NS_FAILED(result)) {
183 RecoverFromUndoError(aTxMgr);
184 return result;
185 }
187 if (!mTransaction)
188 return NS_OK;
190 result = mTransaction->UndoTransaction();
192 if (NS_FAILED(result)) {
193 RecoverFromUndoError(aTxMgr);
194 return result;
195 }
197 return NS_OK;
198 }
200 nsresult
201 nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
202 {
203 nsRefPtr<nsTransactionItem> item;
204 nsresult result = NS_OK;
205 int32_t sz = 0;
207 if (mUndoStack) {
208 if (!mRedoStack && mUndoStack) {
209 mRedoStack = new nsTransactionStack(nsTransactionStack::FOR_REDO);
210 }
212 /* Undo all of the transaction items children! */
213 sz = mUndoStack->GetSize();
215 while (sz-- > 0) {
216 item = mUndoStack->Peek();
218 if (!item) {
219 return NS_ERROR_FAILURE;
220 }
222 nsCOMPtr<nsITransaction> t = item->GetTransaction();
224 bool doInterrupt = false;
226 result = aTxMgr->WillUndoNotify(t, &doInterrupt);
228 if (NS_FAILED(result)) {
229 return result;
230 }
232 if (doInterrupt) {
233 return NS_OK;
234 }
236 result = item->UndoTransaction(aTxMgr);
238 if (NS_SUCCEEDED(result)) {
239 item = mUndoStack->Pop();
240 mRedoStack->Push(item);
241 }
243 nsresult result2 = aTxMgr->DidUndoNotify(t, result);
245 if (NS_SUCCEEDED(result)) {
246 result = result2;
247 }
248 }
249 }
251 return result;
252 }
254 nsresult
255 nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
256 {
257 nsresult result;
259 nsCOMPtr<nsITransaction> kungfuDeathGrip(mTransaction);
260 if (mTransaction) {
261 result = mTransaction->RedoTransaction();
263 NS_ENSURE_SUCCESS(result, result);
264 }
266 result = RedoChildren(aTxMgr);
268 if (NS_FAILED(result)) {
269 RecoverFromRedoError(aTxMgr);
270 return result;
271 }
273 return NS_OK;
274 }
276 nsresult
277 nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
278 {
279 nsRefPtr<nsTransactionItem> item;
280 nsresult result = NS_OK;
282 if (!mRedoStack)
283 return NS_OK;
285 /* Redo all of the transaction items children! */
286 int32_t sz = mRedoStack->GetSize();
288 while (sz-- > 0) {
289 item = mRedoStack->Peek();
291 if (!item) {
292 return NS_ERROR_FAILURE;
293 }
295 nsCOMPtr<nsITransaction> t = item->GetTransaction();
297 bool doInterrupt = false;
299 result = aTxMgr->WillRedoNotify(t, &doInterrupt);
301 if (NS_FAILED(result)) {
302 return result;
303 }
305 if (doInterrupt) {
306 return NS_OK;
307 }
309 result = item->RedoTransaction(aTxMgr);
311 if (NS_SUCCEEDED(result)) {
312 item = mRedoStack->Pop();
313 mUndoStack->Push(item);
314 }
316 nsresult result2 = aTxMgr->DidUndoNotify(t, result);
318 if (NS_SUCCEEDED(result)) {
319 result = result2;
320 }
321 }
323 return result;
324 }
326 nsresult
327 nsTransactionItem::GetNumberOfUndoItems(int32_t *aNumItems)
328 {
329 NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
331 if (!mUndoStack) {
332 *aNumItems = 0;
333 return NS_OK;
334 }
336 *aNumItems = mUndoStack->GetSize();
337 return *aNumItems ? NS_OK : NS_ERROR_FAILURE;
338 }
340 nsresult
341 nsTransactionItem::GetNumberOfRedoItems(int32_t *aNumItems)
342 {
343 NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
345 if (!mRedoStack) {
346 *aNumItems = 0;
347 return NS_OK;
348 }
350 *aNumItems = mRedoStack->GetSize();
351 return *aNumItems ? NS_OK : NS_ERROR_FAILURE;
352 }
354 nsresult
355 nsTransactionItem::RecoverFromUndoError(nsTransactionManager *aTxMgr)
356 {
357 //
358 // If this method gets called, we never got to the point where we
359 // successfully called UndoTransaction() for the transaction item itself.
360 // Just redo any children that successfully called undo!
361 //
362 return RedoChildren(aTxMgr);
363 }
365 nsresult
366 nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
367 {
368 //
369 // If this method gets called, we already successfully called
370 // RedoTransaction() for the transaction item itself. Undo all
371 // the children that successfully called RedoTransaction(),
372 // then undo the transaction item itself.
373 //
375 nsresult result;
377 result = UndoChildren(aTxMgr);
379 if (NS_FAILED(result)) {
380 return result;
381 }
383 if (!mTransaction)
384 return NS_OK;
386 return mTransaction->UndoTransaction();
387 }