editor/txmgr/src/nsTransactionManager.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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 }

mercurial