Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/Assertions.h"
7 #include "mozilla/mozalloc.h"
8 #include "nsAutoPtr.h"
9 #include "nsCOMPtr.h"
10 #include "nsDebug.h"
11 #include "nsError.h"
12 #include "nsISupportsBase.h"
13 #include "nsISupportsUtils.h"
14 #include "nsITransaction.h"
15 #include "nsITransactionList.h"
16 #include "nsITransactionListener.h"
17 #include "nsIWeakReference.h"
18 #include "nsTransactionItem.h"
19 #include "nsTransactionList.h"
20 #include "nsTransactionManager.h"
21 #include "nsTransactionStack.h"
23 nsTransactionManager::nsTransactionManager(int32_t aMaxTransactionCount)
24 : mMaxTransactionCount(aMaxTransactionCount)
25 , mDoStack(nsTransactionStack::FOR_UNDO)
26 , mUndoStack(nsTransactionStack::FOR_UNDO)
27 , mRedoStack(nsTransactionStack::FOR_REDO)
28 {
29 }
31 nsTransactionManager::~nsTransactionManager()
32 {
33 }
35 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionManager)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionManager)
38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
39 tmp->mDoStack.DoUnlink();
40 tmp->mUndoStack.DoUnlink();
41 tmp->mRedoStack.DoUnlink();
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionManager)
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
46 tmp->mDoStack.DoTraverse(cb);
47 tmp->mUndoStack.DoTraverse(cb);
48 tmp->mRedoStack.DoTraverse(cb);
49 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
51 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransactionManager)
52 NS_INTERFACE_MAP_ENTRY(nsITransactionManager)
53 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
54 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITransactionManager)
55 NS_INTERFACE_MAP_END
57 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransactionManager)
58 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransactionManager)
60 NS_IMETHODIMP
61 nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
62 {
63 nsresult result;
65 NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
67 bool doInterrupt = false;
69 result = WillDoNotify(aTransaction, &doInterrupt);
71 if (NS_FAILED(result)) {
72 return result;
73 }
75 if (doInterrupt) {
76 return NS_OK;
77 }
79 result = BeginTransaction(aTransaction, nullptr);
81 if (NS_FAILED(result)) {
82 DidDoNotify(aTransaction, result);
83 return result;
84 }
86 result = EndTransaction(false);
88 nsresult result2 = DidDoNotify(aTransaction, result);
90 if (NS_SUCCEEDED(result))
91 result = result2;
93 return result;
94 }
96 NS_IMETHODIMP
97 nsTransactionManager::UndoTransaction()
98 {
99 nsresult result = NS_OK;
101 // It is illegal to call UndoTransaction() while the transaction manager is
102 // executing a transaction's DoTransaction() method! If this happens,
103 // the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
105 nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
107 if (tx) {
108 return NS_ERROR_FAILURE;
109 }
111 // Peek at the top of the undo stack. Don't remove the transaction
112 // until it has successfully completed.
113 tx = mUndoStack.Peek();
115 // Bail if there's nothing on the stack.
116 if (!tx) {
117 return NS_OK;
118 }
120 nsCOMPtr<nsITransaction> t = tx->GetTransaction();
122 bool doInterrupt = false;
124 result = WillUndoNotify(t, &doInterrupt);
126 if (NS_FAILED(result)) {
127 return result;
128 }
130 if (doInterrupt) {
131 return NS_OK;
132 }
134 result = tx->UndoTransaction(this);
136 if (NS_SUCCEEDED(result)) {
137 tx = mUndoStack.Pop();
138 mRedoStack.Push(tx);
139 }
141 nsresult result2 = DidUndoNotify(t, result);
143 if (NS_SUCCEEDED(result))
144 result = result2;
146 return result;
147 }
149 NS_IMETHODIMP
150 nsTransactionManager::RedoTransaction()
151 {
152 nsresult result = NS_OK;
154 // It is illegal to call RedoTransaction() while the transaction manager is
155 // executing a transaction's DoTransaction() method! If this happens,
156 // the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
158 nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
160 if (tx) {
161 return NS_ERROR_FAILURE;
162 }
164 // Peek at the top of the redo stack. Don't remove the transaction
165 // until it has successfully completed.
166 tx = mRedoStack.Peek();
168 // Bail if there's nothing on the stack.
169 if (!tx) {
170 return NS_OK;
171 }
173 nsCOMPtr<nsITransaction> t = tx->GetTransaction();
175 bool doInterrupt = false;
177 result = WillRedoNotify(t, &doInterrupt);
179 if (NS_FAILED(result)) {
180 return result;
181 }
183 if (doInterrupt) {
184 return NS_OK;
185 }
187 result = tx->RedoTransaction(this);
189 if (NS_SUCCEEDED(result)) {
190 tx = mRedoStack.Pop();
191 mUndoStack.Push(tx);
192 }
194 nsresult result2 = DidRedoNotify(t, result);
196 if (NS_SUCCEEDED(result))
197 result = result2;
199 return result;
200 }
202 NS_IMETHODIMP
203 nsTransactionManager::Clear()
204 {
205 nsresult result;
207 result = ClearRedoStack();
209 if (NS_FAILED(result)) {
210 return result;
211 }
213 result = ClearUndoStack();
215 return result;
216 }
218 NS_IMETHODIMP
219 nsTransactionManager::BeginBatch(nsISupports* aData)
220 {
221 nsresult result;
223 // We can batch independent transactions together by simply pushing
224 // a dummy transaction item on the do stack. This dummy transaction item
225 // will be popped off the do stack, and then pushed on the undo stack
226 // in EndBatch().
228 bool doInterrupt = false;
230 result = WillBeginBatchNotify(&doInterrupt);
232 if (NS_FAILED(result)) {
233 return result;
234 }
236 if (doInterrupt) {
237 return NS_OK;
238 }
240 result = BeginTransaction(0, aData);
242 nsresult result2 = DidBeginBatchNotify(result);
244 if (NS_SUCCEEDED(result))
245 result = result2;
247 return result;
248 }
250 NS_IMETHODIMP
251 nsTransactionManager::EndBatch(bool aAllowEmpty)
252 {
253 nsCOMPtr<nsITransaction> ti;
254 nsresult result;
256 // XXX: Need to add some mechanism to detect the case where the transaction
257 // at the top of the do stack isn't the dummy transaction, so we can
258 // throw an error!! This can happen if someone calls EndBatch() within
259 // the DoTransaction() method of a transaction.
260 //
261 // For now, we can detect this case by checking the value of the
262 // dummy transaction's mTransaction field. If it is our dummy
263 // transaction, it should be nullptr. This may not be true in the
264 // future when we allow users to execute a transaction when beginning
265 // a batch!!!!
267 nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
269 if (tx) {
270 ti = tx->GetTransaction();
271 }
273 if (!tx || ti) {
274 return NS_ERROR_FAILURE;
275 }
277 bool doInterrupt = false;
279 result = WillEndBatchNotify(&doInterrupt);
281 if (NS_FAILED(result)) {
282 return result;
283 }
285 if (doInterrupt) {
286 return NS_OK;
287 }
289 result = EndTransaction(aAllowEmpty);
291 nsresult result2 = DidEndBatchNotify(result);
293 if (NS_SUCCEEDED(result))
294 result = result2;
296 return result;
297 }
299 NS_IMETHODIMP
300 nsTransactionManager::GetNumberOfUndoItems(int32_t *aNumItems)
301 {
302 *aNumItems = mUndoStack.GetSize();
303 return NS_OK;
304 }
306 NS_IMETHODIMP
307 nsTransactionManager::GetNumberOfRedoItems(int32_t *aNumItems)
308 {
309 *aNumItems = mRedoStack.GetSize();
310 return NS_OK;
311 }
313 NS_IMETHODIMP
314 nsTransactionManager::GetMaxTransactionCount(int32_t *aMaxCount)
315 {
316 NS_ENSURE_TRUE(aMaxCount, NS_ERROR_NULL_POINTER);
318 *aMaxCount = mMaxTransactionCount;
320 return NS_OK;
321 }
323 NS_IMETHODIMP
324 nsTransactionManager::SetMaxTransactionCount(int32_t aMaxCount)
325 {
326 int32_t numUndoItems = 0, numRedoItems = 0, total = 0;
328 // It is illegal to call SetMaxTransactionCount() while the transaction
329 // manager is executing a transaction's DoTransaction() method because
330 // the undo and redo stacks might get pruned! If this happens, the
331 // SetMaxTransactionCount() request is ignored, and we return
332 // NS_ERROR_FAILURE.
334 nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
336 if (tx) {
337 return NS_ERROR_FAILURE;
338 }
340 // If aMaxCount is less than zero, the user wants unlimited
341 // levels of undo! No need to prune the undo or redo stacks!
343 if (aMaxCount < 0) {
344 mMaxTransactionCount = -1;
345 return NS_OK;
346 }
348 numUndoItems = mUndoStack.GetSize();
350 numRedoItems = mRedoStack.GetSize();
352 total = numUndoItems + numRedoItems;
354 // If aMaxCount is greater than the number of transactions that currently
355 // exist on the undo and redo stack, there is no need to prune the
356 // undo or redo stacks!
358 if (aMaxCount > total ) {
359 mMaxTransactionCount = aMaxCount;
360 return NS_OK;
361 }
363 // Try getting rid of some transactions on the undo stack! Start at
364 // the bottom of the stack and pop towards the top.
366 while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
367 tx = mUndoStack.PopBottom();
369 if (!tx) {
370 return NS_ERROR_FAILURE;
371 }
373 --numUndoItems;
374 }
376 // If necessary, get rid of some transactions on the redo stack! Start at
377 // the bottom of the stack and pop towards the top.
379 while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
380 tx = mRedoStack.PopBottom();
382 if (!tx) {
383 return NS_ERROR_FAILURE;
384 }
386 --numRedoItems;
387 }
389 mMaxTransactionCount = aMaxCount;
391 return NS_OK;
392 }
394 NS_IMETHODIMP
395 nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
396 {
397 MOZ_ASSERT(aTransaction);
398 *aTransaction = PeekUndoStack().take();
399 return NS_OK;
400 }
402 already_AddRefed<nsITransaction>
403 nsTransactionManager::PeekUndoStack()
404 {
405 nsRefPtr<nsTransactionItem> tx = mUndoStack.Peek();
407 if (!tx) {
408 return nullptr;
409 }
411 return tx->GetTransaction();
412 }
414 NS_IMETHODIMP
415 nsTransactionManager::PeekRedoStack(nsITransaction** aTransaction)
416 {
417 MOZ_ASSERT(aTransaction);
418 *aTransaction = PeekRedoStack().take();
419 return NS_OK;
420 }
422 already_AddRefed<nsITransaction>
423 nsTransactionManager::PeekRedoStack()
424 {
425 nsRefPtr<nsTransactionItem> tx = mRedoStack.Peek();
427 if (!tx) {
428 return nullptr;
429 }
431 return tx->GetTransaction();
432 }
434 NS_IMETHODIMP
435 nsTransactionManager::GetUndoList(nsITransactionList **aTransactionList)
436 {
437 NS_ENSURE_TRUE(aTransactionList, NS_ERROR_NULL_POINTER);
439 *aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mUndoStack);
441 NS_IF_ADDREF(*aTransactionList);
443 return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
444 }
446 NS_IMETHODIMP
447 nsTransactionManager::GetRedoList(nsITransactionList **aTransactionList)
448 {
449 NS_ENSURE_TRUE(aTransactionList, NS_ERROR_NULL_POINTER);
451 *aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mRedoStack);
453 NS_IF_ADDREF(*aTransactionList);
455 return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
456 }
458 nsresult
459 nsTransactionManager::BatchTopUndo()
460 {
461 if (mUndoStack.GetSize() < 2) {
462 // Not enough transactions to merge into one batch.
463 return NS_OK;
464 }
466 nsRefPtr<nsTransactionItem> lastUndo;
467 nsRefPtr<nsTransactionItem> previousUndo;
469 lastUndo = mUndoStack.Pop();
470 MOZ_ASSERT(lastUndo, "There should be at least two transactions.");
472 previousUndo = mUndoStack.Peek();
473 MOZ_ASSERT(previousUndo, "There should be at least two transactions.");
475 nsresult result = previousUndo->AddChild(lastUndo);
477 // Transfer data from the transactions that is going to be
478 // merged to the transaction that it is being merged with.
479 nsCOMArray<nsISupports>& lastData = lastUndo->GetData();
480 nsCOMArray<nsISupports>& previousData = previousUndo->GetData();
481 NS_ENSURE_TRUE(previousData.AppendObjects(lastData), NS_ERROR_UNEXPECTED);
482 lastData.Clear();
484 return result;
485 }
487 nsresult
488 nsTransactionManager::RemoveTopUndo()
489 {
490 nsRefPtr<nsTransactionItem> lastUndo;
492 lastUndo = mUndoStack.Peek();
493 if (!lastUndo) {
494 return NS_OK;
495 }
497 lastUndo = mUndoStack.Pop();
499 return NS_OK;
500 }
502 NS_IMETHODIMP
503 nsTransactionManager::AddListener(nsITransactionListener *aListener)
504 {
505 NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
507 return mListeners.AppendObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
508 }
510 NS_IMETHODIMP
511 nsTransactionManager::RemoveListener(nsITransactionListener *aListener)
512 {
513 NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
515 return mListeners.RemoveObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
516 }
518 NS_IMETHODIMP
519 nsTransactionManager::ClearUndoStack()
520 {
521 mUndoStack.Clear();
522 return NS_OK;
523 }
525 NS_IMETHODIMP
526 nsTransactionManager::ClearRedoStack()
527 {
528 mRedoStack.Clear();
529 return NS_OK;
530 }
532 nsresult
533 nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt)
534 {
535 nsresult result = NS_OK;
536 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
537 {
538 nsITransactionListener *listener = mListeners[i];
540 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
542 result = listener->WillDo(this, aTransaction, aInterrupt);
544 if (NS_FAILED(result) || *aInterrupt)
545 break;
546 }
548 return result;
549 }
551 nsresult
552 nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
553 {
554 nsresult result = NS_OK;
555 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
556 {
557 nsITransactionListener *listener = mListeners[i];
559 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
561 result = listener->DidDo(this, aTransaction, aDoResult);
563 if (NS_FAILED(result))
564 break;
565 }
567 return result;
568 }
570 nsresult
571 nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt)
572 {
573 nsresult result = NS_OK;
574 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
575 {
576 nsITransactionListener *listener = mListeners[i];
578 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
580 result = listener->WillUndo(this, aTransaction, aInterrupt);
582 if (NS_FAILED(result) || *aInterrupt)
583 break;
584 }
586 return result;
587 }
589 nsresult
590 nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
591 {
592 nsresult result = NS_OK;
593 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
594 {
595 nsITransactionListener *listener = mListeners[i];
597 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
599 result = listener->DidUndo(this, aTransaction, aUndoResult);
601 if (NS_FAILED(result))
602 break;
603 }
605 return result;
606 }
608 nsresult
609 nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt)
610 {
611 nsresult result = NS_OK;
612 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
613 {
614 nsITransactionListener *listener = mListeners[i];
616 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
618 result = listener->WillRedo(this, aTransaction, aInterrupt);
620 if (NS_FAILED(result) || *aInterrupt)
621 break;
622 }
624 return result;
625 }
627 nsresult
628 nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
629 {
630 nsresult result = NS_OK;
631 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
632 {
633 nsITransactionListener *listener = mListeners[i];
635 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
637 result = listener->DidRedo(this, aTransaction, aRedoResult);
639 if (NS_FAILED(result))
640 break;
641 }
643 return result;
644 }
646 nsresult
647 nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
648 {
649 nsresult result = NS_OK;
650 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
651 {
652 nsITransactionListener *listener = mListeners[i];
654 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
656 result = listener->WillBeginBatch(this, aInterrupt);
658 if (NS_FAILED(result) || *aInterrupt)
659 break;
660 }
662 return result;
663 }
665 nsresult
666 nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
667 {
668 nsresult result = NS_OK;
669 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
670 {
671 nsITransactionListener *listener = mListeners[i];
673 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
675 result = listener->DidBeginBatch(this, aResult);
677 if (NS_FAILED(result))
678 break;
679 }
681 return result;
682 }
684 nsresult
685 nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
686 {
687 nsresult result = NS_OK;
688 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
689 {
690 nsITransactionListener *listener = mListeners[i];
692 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
694 result = listener->WillEndBatch(this, aInterrupt);
696 if (NS_FAILED(result) || *aInterrupt)
697 break;
698 }
700 return result;
701 }
703 nsresult
704 nsTransactionManager::DidEndBatchNotify(nsresult aResult)
705 {
706 nsresult result = NS_OK;
707 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
708 {
709 nsITransactionListener *listener = mListeners[i];
711 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
713 result = listener->DidEndBatch(this, aResult);
715 if (NS_FAILED(result))
716 break;
717 }
719 return result;
720 }
722 nsresult
723 nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, bool *aInterrupt)
724 {
725 nsresult result = NS_OK;
726 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
727 {
728 nsITransactionListener *listener = mListeners[i];
730 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
732 result = listener->WillMerge(this, aTop, aTransaction, aInterrupt);
734 if (NS_FAILED(result) || *aInterrupt)
735 break;
736 }
738 return result;
739 }
741 nsresult
742 nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
743 nsITransaction *aTransaction,
744 bool aDidMerge,
745 nsresult aMergeResult)
746 {
747 nsresult result = NS_OK;
748 for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++)
749 {
750 nsITransactionListener *listener = mListeners[i];
752 NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
754 result = listener->DidMerge(this, aTop, aTransaction, aDidMerge, aMergeResult);
756 if (NS_FAILED(result))
757 break;
758 }
760 return result;
761 }
763 nsresult
764 nsTransactionManager::BeginTransaction(nsITransaction *aTransaction,
765 nsISupports *aData)
766 {
767 nsresult result = NS_OK;
769 // XXX: POSSIBLE OPTIMIZATION
770 // We could use a factory that pre-allocates/recycles transaction items.
771 nsRefPtr<nsTransactionItem> tx = new nsTransactionItem(aTransaction);
773 if (aData) {
774 nsCOMArray<nsISupports>& data = tx->GetData();
775 data.AppendObject(aData);
776 }
778 if (!tx) {
779 return NS_ERROR_OUT_OF_MEMORY;
780 }
782 mDoStack.Push(tx);
784 result = tx->DoTransaction();
786 if (NS_FAILED(result)) {
787 tx = mDoStack.Pop();
788 return result;
789 }
791 return NS_OK;
792 }
794 nsresult
795 nsTransactionManager::EndTransaction(bool aAllowEmpty)
796 {
797 nsresult result = NS_OK;
799 nsRefPtr<nsTransactionItem> tx = mDoStack.Pop();
801 if (!tx)
802 return NS_ERROR_FAILURE;
804 nsCOMPtr<nsITransaction> tint = tx->GetTransaction();
806 if (!tint && !aAllowEmpty) {
807 int32_t nc = 0;
809 // If we get here, the transaction must be a dummy batch transaction
810 // created by BeginBatch(). If it contains no children, get rid of it!
812 tx->GetNumberOfChildren(&nc);
814 if (!nc) {
815 return result;
816 }
817 }
819 // Check if the transaction is transient. If it is, there's nothing
820 // more to do, just return.
822 bool isTransient = false;
824 if (tint)
825 result = tint->GetIsTransient(&isTransient);
827 if (NS_FAILED(result) || isTransient || !mMaxTransactionCount) {
828 // XXX: Should we be clearing the redo stack if the transaction
829 // is transient and there is nothing on the do stack?
830 return result;
831 }
833 // Check if there is a transaction on the do stack. If there is,
834 // the current transaction is a "sub" transaction, and should
835 // be added to the transaction at the top of the do stack.
837 nsRefPtr<nsTransactionItem> top = mDoStack.Peek();
838 if (top) {
839 result = top->AddChild(tx);
841 // XXX: What do we do if this fails?
843 return result;
844 }
846 // The transaction succeeded, so clear the redo stack.
848 result = ClearRedoStack();
850 if (NS_FAILED(result)) {
851 // XXX: What do we do if this fails?
852 }
854 // Check if we can coalesce this transaction with the one at the top
855 // of the undo stack.
857 top = mUndoStack.Peek();
859 if (tint && top) {
860 bool didMerge = false;
861 nsCOMPtr<nsITransaction> topTransaction = top->GetTransaction();
863 if (topTransaction) {
865 bool doInterrupt = false;
867 result = WillMergeNotify(topTransaction, tint, &doInterrupt);
869 NS_ENSURE_SUCCESS(result, result);
871 if (!doInterrupt) {
872 result = topTransaction->Merge(tint, &didMerge);
874 nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);
876 if (NS_SUCCEEDED(result))
877 result = result2;
879 if (NS_FAILED(result)) {
880 // XXX: What do we do if this fails?
881 }
883 if (didMerge) {
884 return result;
885 }
886 }
887 }
888 }
890 // Check to see if we've hit the max level of undo. If so,
891 // pop the bottom transaction off the undo stack and release it!
893 int32_t sz = mUndoStack.GetSize();
895 if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
896 nsRefPtr<nsTransactionItem> overflow = mUndoStack.PopBottom();
897 }
899 // Push the transaction on the undo stack:
901 mUndoStack.Push(tx);
903 return NS_OK;
904 }