LCOV - code coverage report
Current view: directory - editor/txmgr/src - nsTransactionManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 371 310 83.6 %
Date: 2012-06-02 Functions: 43 38 88.4 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsITransaction.h"
      39                 : #include "nsITransactionListener.h"
      40                 : 
      41                 : #include "nsTransactionItem.h"
      42                 : #include "nsTransactionStack.h"
      43                 : #include "nsVoidArray.h"
      44                 : #include "nsTransactionManager.h"
      45                 : #include "nsTransactionList.h"
      46                 : #include "nsAutoPtr.h"
      47                 : #include "nsCOMPtr.h"
      48                 : 
      49               9 : nsTransactionManager::nsTransactionManager(PRInt32 aMaxTransactionCount)
      50                 :   : mMaxTransactionCount(aMaxTransactionCount)
      51                 :   , mDoStack(nsTransactionStack::FOR_UNDO)
      52                 :   , mUndoStack(nsTransactionStack::FOR_UNDO)
      53               9 :   , mRedoStack(nsTransactionStack::FOR_REDO)
      54                 : {
      55               9 : }
      56                 : 
      57              18 : nsTransactionManager::~nsTransactionManager()
      58                 : {
      59              36 : }
      60                 : 
      61            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionManager)
      62                 : 
      63               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionManager)
      64               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mListeners)
      65               0 :   tmp->mDoStack.DoUnlink();
      66               0 :   tmp->mUndoStack.DoUnlink();
      67               0 :   tmp->mRedoStack.DoUnlink();
      68               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      69                 : 
      70               2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionManager)
      71               2 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mListeners)
      72               2 :   tmp->mDoStack.DoTraverse(cb);
      73               2 :   tmp->mUndoStack.DoTraverse(cb);
      74               2 :   tmp->mRedoStack.DoTraverse(cb);
      75               2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      76                 : 
      77             356 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransactionManager)
      78             232 :   NS_INTERFACE_MAP_ENTRY(nsITransactionManager)
      79             214 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
      80             214 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITransactionManager)
      81             112 : NS_INTERFACE_MAP_END
      82                 : 
      83             129 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransactionManager)
      84             129 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransactionManager)
      85                 : 
      86                 : NS_IMETHODIMP
      87            4289 : nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
      88                 : {
      89                 :   nsresult result;
      90                 : 
      91            4289 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
      92                 : 
      93            4287 :   bool doInterrupt = false;
      94                 : 
      95            4287 :   result = WillDoNotify(aTransaction, &doInterrupt);
      96                 : 
      97            4287 :   if (NS_FAILED(result)) {
      98               0 :     return result;
      99                 :   }
     100                 : 
     101            4287 :   if (doInterrupt) {
     102               0 :     return NS_OK;
     103                 :   }
     104                 : 
     105            4287 :   result = BeginTransaction(aTransaction);
     106                 : 
     107            4287 :   if (NS_FAILED(result)) {
     108               8 :     DidDoNotify(aTransaction, result);
     109               8 :     return result;
     110                 :   }
     111                 : 
     112            4279 :   result = EndTransaction();
     113                 : 
     114            4279 :   nsresult result2 = DidDoNotify(aTransaction, result);
     115                 : 
     116            4279 :   if (NS_SUCCEEDED(result))
     117            4279 :     result = result2;
     118                 : 
     119            4279 :   return result;
     120                 : }
     121                 : 
     122                 : NS_IMETHODIMP
     123             429 : nsTransactionManager::UndoTransaction()
     124                 : {
     125             429 :   nsresult result       = NS_OK;
     126                 : 
     127                 :   // It is illegal to call UndoTransaction() while the transaction manager is
     128                 :   // executing a  transaction's DoTransaction() method! If this happens,
     129                 :   // the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
     130                 : 
     131             858 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     132                 : 
     133             429 :   if (tx) {
     134               0 :     return NS_ERROR_FAILURE;
     135                 :   }
     136                 : 
     137                 :   // Peek at the top of the undo stack. Don't remove the transaction
     138                 :   // until it has successfully completed.
     139             429 :   tx = mUndoStack.Peek();
     140                 : 
     141                 :   // Bail if there's nothing on the stack.
     142             429 :   if (!tx) {
     143               2 :     return NS_OK;
     144                 :   }
     145                 : 
     146             854 :   nsCOMPtr<nsITransaction> t;
     147                 : 
     148             427 :   result = tx->GetTransaction(getter_AddRefs(t));
     149                 : 
     150             427 :   if (NS_FAILED(result)) {
     151               0 :     return result;
     152                 :   }
     153                 : 
     154             427 :   bool doInterrupt = false;
     155                 : 
     156             427 :   result = WillUndoNotify(t, &doInterrupt);
     157                 : 
     158             427 :   if (NS_FAILED(result)) {
     159               0 :     return result;
     160                 :   }
     161                 : 
     162             427 :   if (doInterrupt) {
     163               0 :     return NS_OK;
     164                 :   }
     165                 : 
     166             427 :   result = tx->UndoTransaction(this);
     167                 : 
     168             427 :   if (NS_SUCCEEDED(result)) {
     169             423 :     tx = mUndoStack.Pop();
     170             423 :     mRedoStack.Push(tx);
     171                 :   }
     172                 : 
     173             427 :   nsresult result2 = DidUndoNotify(t, result);
     174                 : 
     175             427 :   if (NS_SUCCEEDED(result))
     176             423 :     result = result2;
     177                 : 
     178             427 :   return result;
     179                 : }
     180                 : 
     181                 : NS_IMETHODIMP
     182             179 : nsTransactionManager::RedoTransaction()
     183                 : {
     184             179 :   nsresult result       = NS_OK;
     185                 : 
     186                 :   // It is illegal to call RedoTransaction() while the transaction manager is
     187                 :   // executing a  transaction's DoTransaction() method! If this happens,
     188                 :   // the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
     189                 : 
     190             358 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     191                 : 
     192             179 :   if (tx) {
     193               0 :     return NS_ERROR_FAILURE;
     194                 :   }
     195                 : 
     196                 :   // Peek at the top of the redo stack. Don't remove the transaction
     197                 :   // until it has successfully completed.
     198             179 :   tx = mRedoStack.Peek();
     199                 : 
     200                 :   // Bail if there's nothing on the stack.
     201             179 :   if (!tx) {
     202               2 :     return NS_OK;
     203                 :   }
     204                 : 
     205             354 :   nsCOMPtr<nsITransaction> t;
     206                 : 
     207             177 :   result = tx->GetTransaction(getter_AddRefs(t));
     208                 : 
     209             177 :   if (NS_FAILED(result)) {
     210               0 :     return result;
     211                 :   }
     212                 : 
     213             177 :   bool doInterrupt = false;
     214                 : 
     215             177 :   result = WillRedoNotify(t, &doInterrupt);
     216                 : 
     217             177 :   if (NS_FAILED(result)) {
     218               0 :     return result;
     219                 :   }
     220                 : 
     221             177 :   if (doInterrupt) {
     222               0 :     return NS_OK;
     223                 :   }
     224                 : 
     225             177 :   result = tx->RedoTransaction(this);
     226                 : 
     227             177 :   if (NS_SUCCEEDED(result)) {
     228             173 :     tx = mRedoStack.Pop();
     229             173 :     mUndoStack.Push(tx);
     230                 :   }
     231                 : 
     232             177 :   nsresult result2 = DidRedoNotify(t, result);
     233                 : 
     234             177 :   if (NS_SUCCEEDED(result))
     235             173 :     result = result2;
     236                 : 
     237             177 :   return result;
     238                 : }
     239                 : 
     240                 : NS_IMETHODIMP
     241              15 : nsTransactionManager::Clear()
     242                 : {
     243                 :   nsresult result;
     244                 : 
     245              15 :   result = ClearRedoStack();
     246                 : 
     247              15 :   if (NS_FAILED(result)) {
     248               0 :     return result;
     249                 :   }
     250                 : 
     251              15 :   result = ClearUndoStack();
     252                 : 
     253              15 :   return result;
     254                 : }
     255                 : 
     256                 : NS_IMETHODIMP
     257             698 : nsTransactionManager::BeginBatch()
     258                 : {
     259                 :   nsresult result;
     260                 : 
     261                 :   // We can batch independent transactions together by simply pushing
     262                 :   // a dummy transaction item on the do stack. This dummy transaction item
     263                 :   // will be popped off the do stack, and then pushed on the undo stack
     264                 :   // in EndBatch().
     265                 : 
     266             698 :   bool doInterrupt = false;
     267                 : 
     268             698 :   result = WillBeginBatchNotify(&doInterrupt);
     269                 : 
     270             698 :   if (NS_FAILED(result)) {
     271               0 :     return result;
     272                 :   }
     273                 : 
     274             698 :   if (doInterrupt) {
     275               0 :     return NS_OK;
     276                 :   }
     277                 : 
     278             698 :   result = BeginTransaction(0);
     279                 :   
     280             698 :   nsresult result2 = DidBeginBatchNotify(result);
     281                 : 
     282             698 :   if (NS_SUCCEEDED(result))
     283             698 :     result = result2;
     284                 : 
     285             698 :   return result;
     286                 : }
     287                 : 
     288                 : NS_IMETHODIMP
     289             702 : nsTransactionManager::EndBatch()
     290                 : {
     291            1404 :   nsCOMPtr<nsITransaction> ti;
     292                 :   nsresult result;
     293                 : 
     294                 :   // XXX: Need to add some mechanism to detect the case where the transaction
     295                 :   //      at the top of the do stack isn't the dummy transaction, so we can
     296                 :   //      throw an error!! This can happen if someone calls EndBatch() within
     297                 :   //      the DoTransaction() method of a transaction.
     298                 :   //
     299                 :   //      For now, we can detect this case by checking the value of the
     300                 :   //      dummy transaction's mTransaction field. If it is our dummy
     301                 :   //      transaction, it should be NULL. This may not be true in the
     302                 :   //      future when we allow users to execute a transaction when beginning
     303                 :   //      a batch!!!!
     304                 : 
     305            1404 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     306                 : 
     307             702 :   if (tx)
     308             698 :     tx->GetTransaction(getter_AddRefs(ti));
     309                 : 
     310             702 :   if (!tx || ti) {
     311               4 :     return NS_ERROR_FAILURE;
     312                 :   }
     313                 : 
     314             698 :   bool doInterrupt = false;
     315                 : 
     316             698 :   result = WillEndBatchNotify(&doInterrupt);
     317                 : 
     318             698 :   if (NS_FAILED(result)) {
     319               0 :     return result;
     320                 :   }
     321                 : 
     322             698 :   if (doInterrupt) {
     323               0 :     return NS_OK;
     324                 :   }
     325                 : 
     326             698 :   result = EndTransaction();
     327                 : 
     328             698 :   nsresult result2 = DidEndBatchNotify(result);
     329                 : 
     330             698 :   if (NS_SUCCEEDED(result))
     331             698 :     result = result2;
     332                 : 
     333             698 :   return result;
     334                 : }
     335                 : 
     336                 : NS_IMETHODIMP
     337             290 : nsTransactionManager::GetNumberOfUndoItems(PRInt32 *aNumItems)
     338                 : {
     339             290 :   *aNumItems = mUndoStack.GetSize();
     340             290 :   return NS_OK;
     341                 : }
     342                 : 
     343                 : NS_IMETHODIMP
     344             268 : nsTransactionManager::GetNumberOfRedoItems(PRInt32 *aNumItems)
     345                 : {
     346             268 :   *aNumItems = mRedoStack.GetSize();
     347             268 :   return NS_OK;
     348                 : }
     349                 : 
     350                 : NS_IMETHODIMP
     351               0 : nsTransactionManager::GetMaxTransactionCount(PRInt32 *aMaxCount)
     352                 : {
     353               0 :   NS_ENSURE_TRUE(aMaxCount, NS_ERROR_NULL_POINTER);
     354                 : 
     355               0 :   *aMaxCount = mMaxTransactionCount;
     356                 : 
     357               0 :   return NS_OK;
     358                 : }
     359                 : 
     360                 : NS_IMETHODIMP
     361              24 : nsTransactionManager::SetMaxTransactionCount(PRInt32 aMaxCount)
     362                 : {
     363              24 :   PRInt32 numUndoItems  = 0, numRedoItems = 0, total = 0;
     364                 : 
     365                 :   // It is illegal to call SetMaxTransactionCount() while the transaction
     366                 :   // manager is executing a  transaction's DoTransaction() method because
     367                 :   // the undo and redo stacks might get pruned! If this happens, the
     368                 :   // SetMaxTransactionCount() request is ignored, and we return
     369                 :   // NS_ERROR_FAILURE.
     370                 : 
     371              48 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     372                 : 
     373              24 :   if (tx) {
     374               0 :     return NS_ERROR_FAILURE;
     375                 :   }
     376                 : 
     377                 :   // If aMaxCount is less than zero, the user wants unlimited
     378                 :   // levels of undo! No need to prune the undo or redo stacks!
     379                 : 
     380              24 :   if (aMaxCount < 0) {
     381               8 :     mMaxTransactionCount = -1;
     382               8 :     return NS_OK;
     383                 :   }
     384                 : 
     385              16 :   numUndoItems = mUndoStack.GetSize();
     386                 : 
     387              16 :   numRedoItems = mRedoStack.GetSize();
     388                 : 
     389              16 :   total = numUndoItems + numRedoItems;
     390                 : 
     391                 :   // If aMaxCount is greater than the number of transactions that currently
     392                 :   // exist on the undo and redo stack, there is no need to prune the
     393                 :   // undo or redo stacks!
     394                 : 
     395              16 :   if (aMaxCount > total ) {
     396               6 :     mMaxTransactionCount = aMaxCount;
     397               6 :     return NS_OK;
     398                 :   }
     399                 : 
     400                 :   // Try getting rid of some transactions on the undo stack! Start at
     401                 :   // the bottom of the stack and pop towards the top.
     402                 : 
     403              56 :   while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
     404              36 :     tx = mUndoStack.PopBottom();
     405                 : 
     406              36 :     if (!tx) {
     407               0 :       return NS_ERROR_FAILURE;
     408                 :     }
     409                 : 
     410              36 :     --numUndoItems;
     411                 :   }
     412                 : 
     413                 :   // If necessary, get rid of some transactions on the redo stack! Start at
     414                 :   // the bottom of the stack and pop towards the top.
     415                 : 
     416              38 :   while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
     417              18 :     tx = mRedoStack.PopBottom();
     418                 : 
     419              18 :     if (!tx) {
     420               0 :       return NS_ERROR_FAILURE;
     421                 :     }
     422                 : 
     423              18 :     --numRedoItems;
     424                 :   }
     425                 : 
     426              10 :   mMaxTransactionCount = aMaxCount;
     427                 : 
     428              10 :   return NS_OK;
     429                 : }
     430                 : 
     431                 : NS_IMETHODIMP
     432              50 : nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
     433                 : {
     434                 :   nsresult result;
     435                 : 
     436              50 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
     437                 : 
     438              50 :   *aTransaction = 0;
     439                 : 
     440             100 :   nsRefPtr<nsTransactionItem> tx = mUndoStack.Peek();
     441                 : 
     442              50 :   if (!tx) {
     443               4 :     return NS_OK;
     444                 :   }
     445                 : 
     446              46 :   result = tx->GetTransaction(aTransaction);
     447                 : 
     448              46 :   return result;
     449                 : }
     450                 : 
     451                 : NS_IMETHODIMP
     452              50 : nsTransactionManager::PeekRedoStack(nsITransaction **aTransaction)
     453                 : {
     454                 :   nsresult result;
     455                 : 
     456              50 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
     457                 : 
     458              50 :   *aTransaction = 0;
     459                 : 
     460             100 :   nsRefPtr<nsTransactionItem> tx = mRedoStack.Peek();
     461                 : 
     462              50 :   if (!tx) {
     463              26 :     return NS_OK;
     464                 :   }
     465                 : 
     466              24 :   result = tx->GetTransaction(aTransaction);
     467                 : 
     468              24 :   return result;
     469                 : }
     470                 : 
     471                 : NS_IMETHODIMP
     472               0 : nsTransactionManager::GetUndoList(nsITransactionList **aTransactionList)
     473                 : {
     474               0 :   NS_ENSURE_TRUE(aTransactionList, NS_ERROR_NULL_POINTER);
     475                 : 
     476               0 :   *aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mUndoStack);
     477                 : 
     478               0 :   NS_IF_ADDREF(*aTransactionList);
     479                 : 
     480               0 :   return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
     481                 : }
     482                 : 
     483                 : NS_IMETHODIMP
     484               0 : nsTransactionManager::GetRedoList(nsITransactionList **aTransactionList)
     485                 : {
     486               0 :   NS_ENSURE_TRUE(aTransactionList, NS_ERROR_NULL_POINTER);
     487                 : 
     488               0 :   *aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mRedoStack);
     489                 : 
     490               0 :   NS_IF_ADDREF(*aTransactionList);
     491                 : 
     492               0 :   return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
     493                 : }
     494                 : 
     495                 : NS_IMETHODIMP
     496               4 : nsTransactionManager::AddListener(nsITransactionListener *aListener)
     497                 : {
     498               4 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
     499                 : 
     500               2 :   return mListeners.AppendObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
     501                 : }
     502                 : 
     503                 : NS_IMETHODIMP
     504               4 : nsTransactionManager::RemoveListener(nsITransactionListener *aListener)
     505                 : {
     506               4 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
     507                 : 
     508               2 :   return mListeners.RemoveObject(aListener) ? NS_OK : NS_ERROR_FAILURE;
     509                 : }
     510                 : 
     511                 : nsresult
     512              15 : nsTransactionManager::ClearUndoStack()
     513                 : {
     514              15 :   mUndoStack.Clear();
     515              15 :   return NS_OK;
     516                 : }
     517                 : 
     518                 : nsresult
     519             435 : nsTransactionManager::ClearRedoStack()
     520                 : {
     521             435 :   mRedoStack.Clear();
     522             435 :   return NS_OK;
     523                 : }
     524                 : 
     525                 : nsresult
     526            4287 : nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     527                 : {
     528            4287 :   nsresult result = NS_OK;
     529            4313 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     530                 :   {
     531              26 :     nsITransactionListener *listener = mListeners[i];
     532                 : 
     533              26 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     534                 : 
     535              26 :     result = listener->WillDo(this, aTransaction, aInterrupt);
     536                 :     
     537              26 :     if (NS_FAILED(result) || *aInterrupt)
     538               0 :       break;
     539                 :   }
     540                 : 
     541            4287 :   return result;
     542                 : }
     543                 : 
     544                 : nsresult
     545            4287 : nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
     546                 : {
     547            4287 :   nsresult result = NS_OK;
     548            4313 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     549                 :   {
     550              26 :     nsITransactionListener *listener = mListeners[i];
     551                 : 
     552              26 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     553                 : 
     554              26 :     result = listener->DidDo(this, aTransaction, aDoResult);
     555                 :     
     556              26 :     if (NS_FAILED(result))
     557               0 :       break;
     558                 :   }
     559                 : 
     560            4287 :   return result;
     561                 : }
     562                 : 
     563                 : nsresult
     564            6228 : nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     565                 : {
     566            6228 :   nsresult result = NS_OK;
     567            6230 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     568                 :   {
     569               2 :     nsITransactionListener *listener = mListeners[i];
     570                 : 
     571               2 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     572                 : 
     573               2 :     result = listener->WillUndo(this, aTransaction, aInterrupt);
     574                 :     
     575               2 :     if (NS_FAILED(result) || *aInterrupt)
     576               0 :       break;
     577                 :   }
     578                 : 
     579            6228 :   return result;
     580                 : }
     581                 : 
     582                 : nsresult
     583            8998 : nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
     584                 : {
     585            8998 :   nsresult result = NS_OK;
     586            9000 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     587                 :   {
     588               2 :     nsITransactionListener *listener = mListeners[i];
     589                 : 
     590               2 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     591                 : 
     592               2 :     result = listener->DidUndo(this, aTransaction, aUndoResult);
     593                 :     
     594               2 :     if (NS_FAILED(result))
     595               0 :       break;
     596                 :   }
     597                 : 
     598            8998 :   return result;
     599                 : }
     600                 : 
     601                 : nsresult
     602            2947 : nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     603                 : {
     604            2947 :   nsresult result = NS_OK;
     605            2947 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     606                 :   {
     607               0 :     nsITransactionListener *listener = mListeners[i];
     608                 : 
     609               0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     610                 : 
     611               0 :     result = listener->WillRedo(this, aTransaction, aInterrupt);
     612                 :     
     613               0 :     if (NS_FAILED(result) || *aInterrupt)
     614               0 :       break;
     615                 :   }
     616                 : 
     617            2947 :   return result;
     618                 : }
     619                 : 
     620                 : nsresult
     621             177 : nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
     622                 : {
     623             177 :   nsresult result = NS_OK;
     624             177 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     625                 :   {
     626               0 :     nsITransactionListener *listener = mListeners[i];
     627                 : 
     628               0 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     629                 : 
     630               0 :     result = listener->DidRedo(this, aTransaction, aRedoResult);
     631                 :     
     632               0 :     if (NS_FAILED(result))
     633               0 :       break;
     634                 :   }
     635                 : 
     636             177 :   return result;
     637                 : }
     638                 : 
     639                 : nsresult
     640             698 : nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
     641                 : {
     642             698 :   nsresult result = NS_OK;
     643             699 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     644                 :   {
     645               1 :     nsITransactionListener *listener = mListeners[i];
     646                 : 
     647               1 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     648                 : 
     649               1 :     result = listener->WillBeginBatch(this, aInterrupt);
     650                 :     
     651               1 :     if (NS_FAILED(result) || *aInterrupt)
     652               0 :       break;
     653                 :   }
     654                 : 
     655             698 :   return result;
     656                 : }
     657                 : 
     658                 : nsresult
     659             698 : nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
     660                 : {
     661             698 :   nsresult result = NS_OK;
     662             699 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     663                 :   {
     664               1 :     nsITransactionListener *listener = mListeners[i];
     665                 : 
     666               1 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     667                 : 
     668               1 :     result = listener->DidBeginBatch(this, aResult);
     669                 :     
     670               1 :     if (NS_FAILED(result))
     671               0 :       break;
     672                 :   }
     673                 : 
     674             698 :   return result;
     675                 : }
     676                 : 
     677                 : nsresult
     678             698 : nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
     679                 : {
     680             698 :   nsresult result = NS_OK;
     681             699 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     682                 :   {
     683               1 :     nsITransactionListener *listener = mListeners[i];
     684                 : 
     685               1 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     686                 : 
     687               1 :     result = listener->WillEndBatch(this, aInterrupt);
     688                 :     
     689               1 :     if (NS_FAILED(result) || *aInterrupt)
     690               0 :       break;
     691                 :   }
     692                 : 
     693             698 :   return result;
     694                 : }
     695                 : 
     696                 : nsresult
     697             698 : nsTransactionManager::DidEndBatchNotify(nsresult aResult)
     698                 : {
     699             698 :   nsresult result = NS_OK;
     700             699 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     701                 :   {
     702               1 :     nsITransactionListener *listener = mListeners[i];
     703                 : 
     704               1 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     705                 : 
     706               1 :     result = listener->DidEndBatch(this, aResult);
     707                 :     
     708               1 :     if (NS_FAILED(result))
     709               0 :       break;
     710                 :   }
     711                 : 
     712             698 :   return result;
     713                 : }
     714                 : 
     715                 : nsresult
     716             325 : nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, bool *aInterrupt)
     717                 : {
     718             325 :   nsresult result = NS_OK;
     719             345 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     720                 :   {
     721              20 :     nsITransactionListener *listener = mListeners[i];
     722                 : 
     723              20 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     724                 : 
     725              20 :     result = listener->WillMerge(this, aTop, aTransaction, aInterrupt);
     726                 :     
     727              20 :     if (NS_FAILED(result) || *aInterrupt)
     728               0 :       break;
     729                 :   }
     730                 : 
     731             325 :   return result;
     732                 : }
     733                 : 
     734                 : nsresult
     735             325 : nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
     736                 :                                      nsITransaction *aTransaction,
     737                 :                                      bool aDidMerge,
     738                 :                                      nsresult aMergeResult)
     739                 : {
     740             325 :   nsresult result = NS_OK;
     741             345 :   for (PRInt32 i = 0, lcount = mListeners.Count(); i < lcount; i++)
     742                 :   {
     743              20 :     nsITransactionListener *listener = mListeners[i];
     744                 : 
     745              20 :     NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
     746                 : 
     747              20 :     result = listener->DidMerge(this, aTop, aTransaction, aDidMerge, aMergeResult);
     748                 :     
     749              20 :     if (NS_FAILED(result))
     750               0 :       break;
     751                 :   }
     752                 : 
     753             325 :   return result;
     754                 : }
     755                 : 
     756                 : nsresult
     757            4985 : nsTransactionManager::BeginTransaction(nsITransaction *aTransaction)
     758                 : {
     759            4985 :   nsresult result = NS_OK;
     760                 : 
     761                 :   // XXX: POSSIBLE OPTIMIZATION
     762                 :   //      We could use a factory that pre-allocates/recycles transaction items.
     763            9970 :   nsRefPtr<nsTransactionItem> tx = new nsTransactionItem(aTransaction);
     764                 : 
     765            4985 :   if (!tx) {
     766               0 :     return NS_ERROR_OUT_OF_MEMORY;
     767                 :   }
     768                 : 
     769            4985 :   mDoStack.Push(tx);
     770                 : 
     771            4985 :   result = tx->DoTransaction();
     772                 : 
     773            4985 :   if (NS_FAILED(result)) {
     774               8 :     tx = mDoStack.Pop();
     775               8 :     return result;
     776                 :   }
     777                 : 
     778            4977 :   return NS_OK;
     779                 : }
     780                 : 
     781                 : nsresult
     782            4977 : nsTransactionManager::EndTransaction()
     783                 : {
     784            9954 :   nsCOMPtr<nsITransaction> tint;
     785            4977 :   nsresult result              = NS_OK;
     786                 : 
     787            9954 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Pop();
     788                 : 
     789            4977 :   if (!tx)
     790               0 :     return NS_ERROR_FAILURE;
     791                 : 
     792            4977 :   result = tx->GetTransaction(getter_AddRefs(tint));
     793                 : 
     794            4977 :   if (NS_FAILED(result)) {
     795                 :     // XXX: What do we do with the transaction item at this point?
     796               0 :     return result;
     797                 :   }
     798                 : 
     799            4977 :   if (!tint) {
     800             698 :     PRInt32 nc = 0;
     801                 : 
     802                 :     // If we get here, the transaction must be a dummy batch transaction
     803                 :     // created by BeginBatch(). If it contains no children, get rid of it!
     804                 : 
     805             698 :     tx->GetNumberOfChildren(&nc);
     806                 : 
     807             698 :     if (!nc) {
     808             110 :       return result;
     809                 :     }
     810                 :   }
     811                 : 
     812                 :   // Check if the transaction is transient. If it is, there's nothing
     813                 :   // more to do, just return.
     814                 : 
     815            4867 :   bool isTransient = false;
     816                 : 
     817            4867 :   if (tint)
     818            4279 :     result = tint->GetIsTransient(&isTransient);
     819                 : 
     820            4867 :   if (NS_FAILED(result) || isTransient || !mMaxTransactionCount) {
     821                 :     // XXX: Should we be clearing the redo stack if the transaction
     822                 :     //      is transient and there is nothing on the do stack?
     823             400 :     return result;
     824                 :   }
     825                 : 
     826                 :   // Check if there is a transaction on the do stack. If there is,
     827                 :   // the current transaction is a "sub" transaction, and should
     828                 :   // be added to the transaction at the top of the do stack.
     829                 : 
     830            8934 :   nsRefPtr<nsTransactionItem> top = mDoStack.Peek();
     831            4467 :   if (top) {
     832            4047 :     result = top->AddChild(tx);
     833                 : 
     834                 :     // XXX: What do we do if this fails?
     835                 : 
     836            4047 :     return result;
     837                 :   }
     838                 : 
     839                 :   // The transaction succeeded, so clear the redo stack.
     840                 : 
     841             420 :   result = ClearRedoStack();
     842                 : 
     843             420 :   if (NS_FAILED(result)) {
     844                 :     // XXX: What do we do if this fails?
     845                 :   }
     846                 : 
     847                 :   // Check if we can coalesce this transaction with the one at the top
     848                 :   // of the undo stack.
     849                 : 
     850             420 :   top = mUndoStack.Peek();
     851                 : 
     852             420 :   if (tint && top) {
     853             327 :     bool didMerge = false;
     854             654 :     nsCOMPtr<nsITransaction> topTransaction;
     855                 : 
     856             327 :     result = top->GetTransaction(getter_AddRefs(topTransaction));
     857                 : 
     858             327 :     if (topTransaction) {
     859                 : 
     860             325 :       bool doInterrupt = false;
     861                 : 
     862             325 :       result = WillMergeNotify(topTransaction, tint, &doInterrupt);
     863                 : 
     864             325 :       NS_ENSURE_SUCCESS(result, result);
     865                 : 
     866             325 :       if (!doInterrupt) {
     867             325 :         result = topTransaction->Merge(tint, &didMerge);
     868                 : 
     869             325 :         nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);
     870                 : 
     871             325 :         if (NS_SUCCEEDED(result))
     872             325 :           result = result2;
     873                 : 
     874             325 :         if (NS_FAILED(result)) {
     875                 :           // XXX: What do we do if this fails?
     876                 :         }
     877                 : 
     878             325 :         if (didMerge) {
     879              40 :           return result;
     880                 :         }
     881                 :       }
     882                 :     }
     883                 :   }
     884                 : 
     885                 :   // Check to see if we've hit the max level of undo. If so,
     886                 :   // pop the bottom transaction off the undo stack and release it!
     887                 : 
     888             380 :   PRInt32 sz = mUndoStack.GetSize();
     889                 : 
     890             380 :   if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
     891              20 :     nsRefPtr<nsTransactionItem> overflow = mUndoStack.PopBottom();
     892                 :   }
     893                 : 
     894                 :   // Push the transaction on the undo stack:
     895                 : 
     896             380 :   mUndoStack.Push(tx);
     897                 : 
     898             380 :   return NS_OK;
     899            4392 : }
     900                 : 

Generated by: LCOV version 1.7