LCOV - code coverage report
Current view: top level - editor/txmgr - nsTransactionManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 82 363 22.6 %
Date: 2017-07-14 16:53:18 Functions: 19 45 42.2 %
Legend: Lines: hit not hit

          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/Assertions.h"
       7             : #include "mozilla/mozalloc.h"
       8             : #include "nsCOMPtr.h"
       9             : #include "nsDebug.h"
      10             : #include "nsError.h"
      11             : #include "nsISupportsBase.h"
      12             : #include "nsISupportsUtils.h"
      13             : #include "nsITransaction.h"
      14             : #include "nsITransactionList.h"
      15             : #include "nsITransactionListener.h"
      16             : #include "nsIWeakReference.h"
      17             : #include "nsTransactionItem.h"
      18             : #include "nsTransactionList.h"
      19             : #include "nsTransactionManager.h"
      20             : #include "nsTransactionStack.h"
      21             : 
      22           2 : nsTransactionManager::nsTransactionManager(int32_t aMaxTransactionCount)
      23             :   : mMaxTransactionCount(aMaxTransactionCount)
      24             :   , mDoStack(nsTransactionStack::FOR_UNDO)
      25             :   , mUndoStack(nsTransactionStack::FOR_UNDO)
      26           2 :   , mRedoStack(nsTransactionStack::FOR_REDO)
      27             : {
      28           2 : }
      29             : 
      30           2 : nsTransactionManager::~nsTransactionManager()
      31             : {
      32           3 : }
      33             : 
      34             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionManager)
      35             : 
      36           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionManager)
      37           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
      38           0 :   tmp->mDoStack.DoUnlink();
      39           0 :   tmp->mUndoStack.DoUnlink();
      40           0 :   tmp->mRedoStack.DoUnlink();
      41           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      42             : 
      43           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionManager)
      44           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
      45           0 :   tmp->mDoStack.DoTraverse(cb);
      46           0 :   tmp->mUndoStack.DoTraverse(cb);
      47           0 :   tmp->mRedoStack.DoTraverse(cb);
      48           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      49             : 
      50          10 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransactionManager)
      51           4 :   NS_INTERFACE_MAP_ENTRY(nsITransactionManager)
      52           0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
      53           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITransactionManager)
      54           0 : NS_INTERFACE_MAP_END
      55             : 
      56          11 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransactionManager)
      57          11 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransactionManager)
      58             : 
      59             : NS_IMETHODIMP
      60           3 : nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
      61             : {
      62           3 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
      63             : 
      64           3 :   bool doInterrupt = false;
      65             : 
      66           3 :   nsresult rv = WillDoNotify(aTransaction, &doInterrupt);
      67           3 :   if (NS_FAILED(rv)) {
      68           0 :     return rv;
      69             :   }
      70           3 :   if (doInterrupt) {
      71           0 :     return NS_OK;
      72             :   }
      73             : 
      74           3 :   rv = BeginTransaction(aTransaction, nullptr);
      75           3 :   if (NS_FAILED(rv)) {
      76           0 :     DidDoNotify(aTransaction, rv);
      77           0 :     return rv;
      78             :   }
      79             : 
      80           3 :   rv = EndTransaction(false);
      81             : 
      82           3 :   nsresult rv2 = DidDoNotify(aTransaction, rv);
      83           3 :   if (NS_SUCCEEDED(rv)) {
      84           3 :     rv = rv2;
      85             :   }
      86             : 
      87             :   // XXX The result of EndTransaction() or DidDoNotify() if EndTransaction()
      88             :   //     succeeded.
      89           3 :   return rv;
      90             : }
      91             : 
      92             : NS_IMETHODIMP
      93           0 : nsTransactionManager::UndoTransaction()
      94             : {
      95             :   // It is illegal to call UndoTransaction() while the transaction manager is
      96             :   // executing a  transaction's DoTransaction() method! If this happens,
      97             :   // the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
      98           0 :   if (!mDoStack.IsEmpty()) {
      99           0 :     return NS_ERROR_FAILURE;
     100             :   }
     101             : 
     102             :   // Peek at the top of the undo stack. Don't remove the transaction
     103             :   // until it has successfully completed.
     104           0 :   RefPtr<nsTransactionItem> tx = mUndoStack.Peek();
     105           0 :   if (!tx) {
     106             :     // Bail if there's nothing on the stack.
     107           0 :     return NS_OK;
     108             :   }
     109             : 
     110           0 :   nsCOMPtr<nsITransaction> t = tx->GetTransaction();
     111           0 :   bool doInterrupt = false;
     112           0 :   nsresult rv = WillUndoNotify(t, &doInterrupt);
     113           0 :   if (NS_FAILED(rv)) {
     114           0 :     return rv;
     115             :   }
     116           0 :   if (doInterrupt) {
     117           0 :     return NS_OK;
     118             :   }
     119             : 
     120           0 :   rv = tx->UndoTransaction(this);
     121           0 :   if (NS_SUCCEEDED(rv)) {
     122           0 :     tx = mUndoStack.Pop();
     123           0 :     mRedoStack.Push(tx.forget());
     124             :   }
     125             : 
     126           0 :   nsresult rv2 = DidUndoNotify(t, rv);
     127           0 :   if (NS_SUCCEEDED(rv)) {
     128           0 :     rv = rv2;
     129             :   }
     130             : 
     131             :   // XXX The result of UndoTransaction() or DidUndoNotify() if UndoTransaction()
     132             :   //     succeeded.
     133           0 :   return rv;
     134             : }
     135             : 
     136             : NS_IMETHODIMP
     137           0 : nsTransactionManager::RedoTransaction()
     138             : {
     139             :   // It is illegal to call RedoTransaction() while the transaction manager is
     140             :   // executing a  transaction's DoTransaction() method! If this happens,
     141             :   // the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
     142           0 :   if (!mDoStack.IsEmpty()) {
     143           0 :     return NS_ERROR_FAILURE;
     144             :   }
     145             : 
     146             :   // Peek at the top of the redo stack. Don't remove the transaction
     147             :   // until it has successfully completed.
     148           0 :   RefPtr<nsTransactionItem> tx = mRedoStack.Peek();
     149           0 :   if (!tx) {
     150             :     // Bail if there's nothing on the stack.
     151           0 :     return NS_OK;
     152             :   }
     153             : 
     154           0 :   nsCOMPtr<nsITransaction> t = tx->GetTransaction();
     155           0 :   bool doInterrupt = false;
     156           0 :   nsresult rv = WillRedoNotify(t, &doInterrupt);
     157           0 :   if (NS_FAILED(rv)) {
     158           0 :     return rv;
     159             :   }
     160           0 :   if (doInterrupt) {
     161           0 :     return NS_OK;
     162             :   }
     163             : 
     164           0 :   rv = tx->RedoTransaction(this);
     165           0 :   if (NS_SUCCEEDED(rv)) {
     166           0 :     tx = mRedoStack.Pop();
     167           0 :     mUndoStack.Push(tx.forget());
     168             :   }
     169             : 
     170           0 :   nsresult rv2 = DidRedoNotify(t, rv);
     171           0 :   if (NS_SUCCEEDED(rv)) {
     172           0 :     rv = rv2;
     173             :   }
     174             : 
     175             :   // XXX The result of RedoTransaction() or DidRedoNotify() if RedoTransaction()
     176             :   //     succeeded.
     177           0 :   return rv;
     178             : }
     179             : 
     180             : NS_IMETHODIMP
     181           2 : nsTransactionManager::Clear()
     182             : {
     183           2 :   nsresult rv = ClearRedoStack();
     184           2 :   if (NS_FAILED(rv)) {
     185           0 :     return rv;
     186             :   }
     187           2 :   return ClearUndoStack();
     188             : }
     189             : 
     190             : NS_IMETHODIMP
     191           0 : nsTransactionManager::BeginBatch(nsISupports* aData)
     192             : {
     193             :   // We can batch independent transactions together by simply pushing
     194             :   // a dummy transaction item on the do stack. This dummy transaction item
     195             :   // will be popped off the do stack, and then pushed on the undo stack
     196             :   // in EndBatch().
     197           0 :   bool doInterrupt = false;
     198           0 :   nsresult rv = WillBeginBatchNotify(&doInterrupt);
     199           0 :   if (NS_FAILED(rv)) {
     200           0 :     return rv;
     201             :   }
     202           0 :   if (doInterrupt) {
     203           0 :     return NS_OK;
     204             :   }
     205             : 
     206           0 :   rv = BeginTransaction(0, aData);
     207             : 
     208           0 :   nsresult rv2 = DidBeginBatchNotify(rv);
     209           0 :   if (NS_SUCCEEDED(rv)) {
     210           0 :     rv = rv2;
     211             :   }
     212             : 
     213             :   // XXX The result of BeginTransaction() or DidBeginBatchNotify() if
     214             :   //     BeginTransaction() succeeded.
     215           0 :   return rv;
     216             : }
     217             : 
     218             : NS_IMETHODIMP
     219           0 : nsTransactionManager::EndBatch(bool aAllowEmpty)
     220             : {
     221             :   // XXX: Need to add some mechanism to detect the case where the transaction
     222             :   //      at the top of the do stack isn't the dummy transaction, so we can
     223             :   //      throw an error!! This can happen if someone calls EndBatch() within
     224             :   //      the DoTransaction() method of a transaction.
     225             :   //
     226             :   //      For now, we can detect this case by checking the value of the
     227             :   //      dummy transaction's mTransaction field. If it is our dummy
     228             :   //      transaction, it should be nullptr. This may not be true in the
     229             :   //      future when we allow users to execute a transaction when beginning
     230             :   //      a batch!!!!
     231           0 :   RefPtr<nsTransactionItem> tx = mDoStack.Peek();
     232           0 :   nsCOMPtr<nsITransaction> ti;
     233           0 :   if (tx) {
     234           0 :     ti = tx->GetTransaction();
     235             :   }
     236           0 :   if (!tx || ti) {
     237           0 :     return NS_ERROR_FAILURE;
     238             :   }
     239             : 
     240           0 :   bool doInterrupt = false;
     241           0 :   nsresult rv = WillEndBatchNotify(&doInterrupt);
     242           0 :   if (NS_FAILED(rv)) {
     243           0 :     return rv;
     244             :   }
     245           0 :   if (doInterrupt) {
     246           0 :     return NS_OK;
     247             :   }
     248             : 
     249           0 :   rv = EndTransaction(aAllowEmpty);
     250           0 :   nsresult rv2 = DidEndBatchNotify(rv);
     251           0 :   if (NS_SUCCEEDED(rv)) {
     252           0 :     rv = rv2;
     253             :   }
     254             : 
     255             :   // XXX The result of EndTransaction() or DidEndBatchNotify() if
     256             :   //     EndTransaction() succeeded.
     257           0 :   return rv;
     258             : }
     259             : 
     260             : NS_IMETHODIMP
     261           2 : nsTransactionManager::GetNumberOfUndoItems(int32_t *aNumItems)
     262             : {
     263           2 :   *aNumItems = mUndoStack.GetSize();
     264           2 :   return NS_OK;
     265             : }
     266             : 
     267             : NS_IMETHODIMP
     268           1 : nsTransactionManager::GetNumberOfRedoItems(int32_t *aNumItems)
     269             : {
     270           1 :   *aNumItems = mRedoStack.GetSize();
     271           1 :   return NS_OK;
     272             : }
     273             : 
     274             : NS_IMETHODIMP
     275           0 : nsTransactionManager::GetMaxTransactionCount(int32_t *aMaxCount)
     276             : {
     277           0 :   NS_ENSURE_TRUE(aMaxCount, NS_ERROR_NULL_POINTER);
     278           0 :   *aMaxCount = mMaxTransactionCount;
     279           0 :   return NS_OK;
     280             : }
     281             : 
     282             : NS_IMETHODIMP
     283           6 : nsTransactionManager::SetMaxTransactionCount(int32_t aMaxCount)
     284             : {
     285             :   // It is illegal to call SetMaxTransactionCount() while the transaction
     286             :   // manager is executing a  transaction's DoTransaction() method because
     287             :   // the undo and redo stacks might get pruned! If this happens, the
     288             :   // SetMaxTransactionCount() request is ignored, and we return
     289             :   // NS_ERROR_FAILURE.
     290           6 :   if (!mDoStack.IsEmpty()) {
     291           0 :     return NS_ERROR_FAILURE;
     292             :   }
     293             : 
     294             :   // If aMaxCount is less than zero, the user wants unlimited
     295             :   // levels of undo! No need to prune the undo or redo stacks!
     296           6 :   if (aMaxCount < 0) {
     297           3 :     mMaxTransactionCount = -1;
     298           3 :     return NS_OK;
     299             :   }
     300             : 
     301             :   // If aMaxCount is greater than the number of transactions that currently
     302             :   // exist on the undo and redo stack, there is no need to prune the
     303             :   // undo or redo stacks!
     304           3 :   int32_t numUndoItems = mUndoStack.GetSize();
     305           3 :   int32_t numRedoItems = mRedoStack.GetSize();
     306           3 :   int32_t total = numUndoItems + numRedoItems;
     307           3 :   if (aMaxCount > total) {
     308           2 :     mMaxTransactionCount = aMaxCount;
     309           2 :     return NS_OK;
     310             :   }
     311             : 
     312             :   // Try getting rid of some transactions on the undo stack! Start at
     313             :   // the bottom of the stack and pop towards the top.
     314           1 :   while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
     315           0 :     RefPtr<nsTransactionItem> tx = mUndoStack.PopBottom();
     316           0 :     if (!tx) {
     317           0 :       return NS_ERROR_FAILURE;
     318             :     }
     319           0 :     --numUndoItems;
     320             :   }
     321             : 
     322             :   // If necessary, get rid of some transactions on the redo stack! Start at
     323             :   // the bottom of the stack and pop towards the top.
     324           1 :   while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
     325           0 :     RefPtr<nsTransactionItem> tx = mRedoStack.PopBottom();
     326           0 :     if (!tx) {
     327           0 :       return NS_ERROR_FAILURE;
     328             :     }
     329           0 :     --numRedoItems;
     330             :   }
     331             : 
     332           1 :   mMaxTransactionCount = aMaxCount;
     333           1 :   return NS_OK;
     334             : }
     335             : 
     336             : NS_IMETHODIMP
     337           0 : nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
     338             : {
     339           0 :   MOZ_ASSERT(aTransaction);
     340           0 :   *aTransaction = PeekUndoStack().take();
     341           0 :   return NS_OK;
     342             : }
     343             : 
     344             : already_AddRefed<nsITransaction>
     345           1 : nsTransactionManager::PeekUndoStack()
     346             : {
     347           2 :   RefPtr<nsTransactionItem> tx = mUndoStack.Peek();
     348           1 :   if (!tx) {
     349           1 :     return nullptr;
     350             :   }
     351           0 :   return tx->GetTransaction();
     352             : }
     353             : 
     354             : NS_IMETHODIMP
     355           0 : nsTransactionManager::PeekRedoStack(nsITransaction** aTransaction)
     356             : {
     357           0 :   MOZ_ASSERT(aTransaction);
     358           0 :   *aTransaction = PeekRedoStack().take();
     359           0 :   return NS_OK;
     360             : }
     361             : 
     362             : already_AddRefed<nsITransaction>
     363           0 : nsTransactionManager::PeekRedoStack()
     364             : {
     365           0 :   RefPtr<nsTransactionItem> tx = mRedoStack.Peek();
     366           0 :   if (!tx) {
     367           0 :     return nullptr;
     368             :   }
     369           0 :   return tx->GetTransaction();
     370             : }
     371             : 
     372             : NS_IMETHODIMP
     373           0 : nsTransactionManager::GetUndoList(nsITransactionList **aTransactionList)
     374             : {
     375           0 :   NS_ENSURE_TRUE(aTransactionList, NS_ERROR_NULL_POINTER);
     376             : 
     377           0 :   *aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mUndoStack);
     378           0 :   NS_IF_ADDREF(*aTransactionList);
     379           0 :   return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
     380             : }
     381             : 
     382             : NS_IMETHODIMP
     383           0 : nsTransactionManager::GetRedoList(nsITransactionList **aTransactionList)
     384             : {
     385           0 :   NS_ENSURE_TRUE(aTransactionList, NS_ERROR_NULL_POINTER);
     386             : 
     387           0 :   *aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mRedoStack);
     388           0 :   NS_IF_ADDREF(*aTransactionList);
     389           0 :   return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
     390             : }
     391             : 
     392             : nsresult
     393           0 : nsTransactionManager::BatchTopUndo()
     394             : {
     395           0 :   if (mUndoStack.GetSize() < 2) {
     396             :     // Not enough transactions to merge into one batch.
     397           0 :     return NS_OK;
     398             :   }
     399             : 
     400           0 :   RefPtr<nsTransactionItem> lastUndo;
     401           0 :   RefPtr<nsTransactionItem> previousUndo;
     402             : 
     403           0 :   lastUndo = mUndoStack.Pop();
     404           0 :   MOZ_ASSERT(lastUndo, "There should be at least two transactions.");
     405             : 
     406           0 :   previousUndo = mUndoStack.Peek();
     407           0 :   MOZ_ASSERT(previousUndo, "There should be at least two transactions.");
     408             : 
     409           0 :   nsresult rv = previousUndo->AddChild(lastUndo);
     410             : 
     411             :   // Transfer data from the transactions that is going to be
     412             :   // merged to the transaction that it is being merged with.
     413           0 :   nsCOMArray<nsISupports>& lastData = lastUndo->GetData();
     414           0 :   nsCOMArray<nsISupports>& previousData = previousUndo->GetData();
     415           0 :   NS_ENSURE_TRUE(previousData.AppendObjects(lastData), NS_ERROR_UNEXPECTED);
     416           0 :   lastData.Clear();
     417           0 :   return rv;
     418             : }
     419             : 
     420             : nsresult
     421           0 : nsTransactionManager::RemoveTopUndo()
     422             : {
     423           0 :   if (mUndoStack.IsEmpty()) {
     424           0 :     return NS_OK;
     425             :   }
     426             : 
     427           0 :   RefPtr<nsTransactionItem> lastUndo = mUndoStack.Pop();
     428           0 :   return NS_OK;
     429             : }
     430             : 
     431             : NS_IMETHODIMP
     432           0 : nsTransactionManager::AddListener(nsITransactionListener *aListener)
     433             : {
     434           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
     435           0 :   return mListeners.AppendObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
     436             : }
     437             : 
     438             : NS_IMETHODIMP
     439           0 : nsTransactionManager::RemoveListener(nsITransactionListener *aListener)
     440             : {
     441           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
     442           0 :   return mListeners.RemoveObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
     443             : }
     444             : 
     445             : NS_IMETHODIMP
     446           2 : nsTransactionManager::ClearUndoStack()
     447             : {
     448           2 :   mUndoStack.Clear();
     449           2 :   return NS_OK;
     450             : }
     451             : 
     452             : NS_IMETHODIMP
     453           2 : nsTransactionManager::ClearRedoStack()
     454             : {
     455           2 :   mRedoStack.Clear();
     456           2 :   return NS_OK;
     457             : }
     458             : 
     459             : nsresult
     460           3 : nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     461             : {
     462           3 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     463           0 :     nsITransactionListener* listener = mListeners[i];
     464           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     465             : 
     466           0 :     nsresult rv = listener->WillDo(this, aTransaction, aInterrupt);
     467           0 :     if (NS_FAILED(rv) || *aInterrupt) {
     468           0 :       return rv;
     469             :     }
     470             :   }
     471           3 :   return NS_OK;
     472             : }
     473             : 
     474             : nsresult
     475           3 : nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
     476             : {
     477           3 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     478           0 :     nsITransactionListener* listener = mListeners[i];
     479           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     480             : 
     481           0 :     nsresult rv = listener->DidDo(this, aTransaction, aDoResult);
     482           0 :     if (NS_FAILED(rv)) {
     483           0 :       return rv;
     484             :     }
     485             :   }
     486           3 :   return NS_OK;
     487             : }
     488             : 
     489             : nsresult
     490           0 : nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     491             : {
     492           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     493           0 :     nsITransactionListener* listener = mListeners[i];
     494           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     495             : 
     496           0 :     nsresult rv = listener->WillUndo(this, aTransaction, aInterrupt);
     497           0 :     if (NS_FAILED(rv) || *aInterrupt) {
     498           0 :       return rv;
     499             :     }
     500             :   }
     501           0 :   return NS_OK;
     502             : }
     503             : 
     504             : nsresult
     505           0 : nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
     506             : {
     507           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     508           0 :     nsITransactionListener* listener = mListeners[i];
     509           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     510             : 
     511           0 :     nsresult rv = listener->DidUndo(this, aTransaction, aUndoResult);
     512           0 :     if (NS_FAILED(rv)) {
     513           0 :       return rv;
     514             :     }
     515             :   }
     516           0 :   return NS_OK;
     517             : }
     518             : 
     519             : nsresult
     520           0 : nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     521             : {
     522           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     523           0 :     nsITransactionListener* listener = mListeners[i];
     524           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     525             : 
     526           0 :     nsresult rv = listener->WillRedo(this, aTransaction, aInterrupt);
     527           0 :     if (NS_FAILED(rv) || *aInterrupt) {
     528           0 :       return rv;
     529             :     }
     530             :   }
     531           0 :   return NS_OK;
     532             : }
     533             : 
     534             : nsresult
     535           0 : nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
     536             : {
     537           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     538           0 :     nsITransactionListener* listener = mListeners[i];
     539           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     540             : 
     541           0 :     nsresult rv = listener->DidRedo(this, aTransaction, aRedoResult);
     542           0 :     if (NS_FAILED(rv)) {
     543           0 :       return rv;
     544             :     }
     545             :   }
     546           0 :   return NS_OK;
     547             : }
     548             : 
     549             : nsresult
     550           0 : nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
     551             : {
     552           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     553           0 :     nsITransactionListener* listener = mListeners[i];
     554           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     555             : 
     556           0 :     nsresult rv = listener->WillBeginBatch(this, aInterrupt);
     557           0 :     if (NS_FAILED(rv) || *aInterrupt) {
     558           0 :       return rv;
     559             :     }
     560             :   }
     561           0 :   return NS_OK;
     562             : }
     563             : 
     564             : nsresult
     565           0 : nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
     566             : {
     567           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     568           0 :     nsITransactionListener* listener = mListeners[i];
     569           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     570             : 
     571           0 :     nsresult rv = listener->DidBeginBatch(this, aResult);
     572           0 :     if (NS_FAILED(rv)) {
     573           0 :       return rv;
     574             :     }
     575             :   }
     576           0 :   return NS_OK;
     577             : }
     578             : 
     579             : nsresult
     580           0 : nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
     581             : {
     582           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     583           0 :     nsITransactionListener* listener = mListeners[i];
     584           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     585             : 
     586           0 :     nsresult rv = listener->WillEndBatch(this, aInterrupt);
     587           0 :     if (NS_FAILED(rv) || *aInterrupt) {
     588           0 :       return rv;
     589             :     }
     590             :   }
     591           0 :   return NS_OK;
     592             : }
     593             : 
     594             : nsresult
     595           0 : nsTransactionManager::DidEndBatchNotify(nsresult aResult)
     596             : {
     597           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     598           0 :     nsITransactionListener* listener = mListeners[i];
     599           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     600             : 
     601           0 :     nsresult rv = listener->DidEndBatch(this, aResult);
     602           0 :     if (NS_FAILED(rv)) {
     603           0 :       return rv;
     604             :     }
     605             :   }
     606           0 :   return NS_OK;
     607             : }
     608             : 
     609             : nsresult
     610           0 : nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, bool *aInterrupt)
     611             : {
     612           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     613           0 :     nsITransactionListener* listener = mListeners[i];
     614           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     615             : 
     616           0 :     nsresult rv = listener->WillMerge(this, aTop, aTransaction, aInterrupt);
     617           0 :     if (NS_FAILED(rv) || *aInterrupt) {
     618           0 :       return rv;
     619             :     }
     620             :   }
     621           0 :   return NS_OK;
     622             : }
     623             : 
     624             : nsresult
     625           0 : nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
     626             :                                      nsITransaction *aTransaction,
     627             :                                      bool aDidMerge,
     628             :                                      nsresult aMergeResult)
     629             : {
     630           0 :   for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
     631           0 :     nsITransactionListener* listener = mListeners[i];
     632           0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     633             : 
     634             :     nsresult rv =
     635           0 :       listener->DidMerge(this, aTop, aTransaction, aDidMerge, aMergeResult);
     636           0 :     if (NS_FAILED(rv)) {
     637           0 :       return rv;
     638             :     }
     639             :   }
     640           0 :   return NS_OK;
     641             : }
     642             : 
     643             : nsresult
     644           3 : nsTransactionManager::BeginTransaction(nsITransaction *aTransaction,
     645             :                                        nsISupports *aData)
     646             : {
     647             :   // XXX: POSSIBLE OPTIMIZATION
     648             :   //      We could use a factory that pre-allocates/recycles transaction items.
     649           6 :   RefPtr<nsTransactionItem> tx = new nsTransactionItem(aTransaction);
     650           3 :   if (!tx) {
     651           0 :     return NS_ERROR_OUT_OF_MEMORY;
     652             :   }
     653             : 
     654           3 :   if (aData) {
     655           0 :     nsCOMArray<nsISupports>& data = tx->GetData();
     656           0 :     data.AppendObject(aData);
     657             :   }
     658             : 
     659           3 :   mDoStack.Push(tx);
     660             : 
     661           3 :   nsresult rv = tx->DoTransaction();
     662           3 :   if (NS_FAILED(rv)) {
     663           0 :     tx = mDoStack.Pop();
     664           0 :     return rv;
     665             :   }
     666           3 :   return NS_OK;
     667             : }
     668             : 
     669             : nsresult
     670           3 : nsTransactionManager::EndTransaction(bool aAllowEmpty)
     671             : {
     672           6 :   RefPtr<nsTransactionItem> tx = mDoStack.Pop();
     673           3 :   if (!tx) {
     674           0 :     return NS_ERROR_FAILURE;
     675             :   }
     676             : 
     677           6 :   nsCOMPtr<nsITransaction> tint = tx->GetTransaction();
     678           3 :   if (!tint && !aAllowEmpty) {
     679             :     // If we get here, the transaction must be a dummy batch transaction
     680             :     // created by BeginBatch(). If it contains no children, get rid of it!
     681           0 :     int32_t nc = 0;
     682           0 :     tx->GetNumberOfChildren(&nc);
     683           0 :     if (!nc) {
     684           0 :       return NS_OK;
     685             :     }
     686             :   }
     687             : 
     688             :   // Check if the transaction is transient. If it is, there's nothing
     689             :   // more to do, just return.
     690           3 :   bool isTransient = false;
     691           3 :   nsresult rv = NS_OK;
     692           3 :   if (tint) {
     693           3 :     rv = tint->GetIsTransient(&isTransient);
     694             :   }
     695           3 :   if (NS_FAILED(rv) || isTransient || !mMaxTransactionCount) {
     696             :     // XXX: Should we be clearing the redo stack if the transaction
     697             :     //      is transient and there is nothing on the do stack?
     698           3 :     return rv;
     699             :   }
     700             : 
     701             :   // Check if there is a transaction on the do stack. If there is,
     702             :   // the current transaction is a "sub" transaction, and should
     703             :   // be added to the transaction at the top of the do stack.
     704           0 :   RefPtr<nsTransactionItem> top = mDoStack.Peek();
     705           0 :   if (top) {
     706           0 :     return top->AddChild(tx); // XXX: What do we do if this fails?
     707             :   }
     708             : 
     709             :   // The transaction succeeded, so clear the redo stack.
     710           0 :   rv = ClearRedoStack();
     711           0 :   if (NS_FAILED(rv)) {
     712             :     // XXX: What do we do if this fails?
     713             :   }
     714             : 
     715             :   // Check if we can coalesce this transaction with the one at the top
     716             :   // of the undo stack.
     717           0 :   top = mUndoStack.Peek();
     718           0 :   if (tint && top) {
     719           0 :     bool didMerge = false;
     720           0 :     nsCOMPtr<nsITransaction> topTransaction = top->GetTransaction();
     721           0 :     if (topTransaction) {
     722           0 :       bool doInterrupt = false;
     723           0 :       rv = WillMergeNotify(topTransaction, tint, &doInterrupt);
     724           0 :       NS_ENSURE_SUCCESS(rv, rv);
     725             : 
     726           0 :       if (!doInterrupt) {
     727           0 :         rv = topTransaction->Merge(tint, &didMerge);
     728           0 :         nsresult rv2 = DidMergeNotify(topTransaction, tint, didMerge, rv);
     729           0 :         if (NS_SUCCEEDED(rv)) {
     730           0 :           rv = rv2;
     731             :         }
     732           0 :         if (NS_FAILED(rv)) {
     733             :           // XXX: What do we do if this fails?
     734             :         }
     735           0 :         if (didMerge) {
     736           0 :           return rv;
     737             :         }
     738             :       }
     739             :     }
     740             :   }
     741             : 
     742             :   // Check to see if we've hit the max level of undo. If so,
     743             :   // pop the bottom transaction off the undo stack and release it!
     744           0 :   int32_t sz = mUndoStack.GetSize();
     745           0 :   if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
     746           0 :     RefPtr<nsTransactionItem> overflow = mUndoStack.PopBottom();
     747             :   }
     748             : 
     749             :   // Push the transaction on the undo stack:
     750           0 :   mUndoStack.Push(tx.forget());
     751           0 :   return NS_OK;
     752             : }

Generated by: LCOV version 1.13