LCOV - code coverage report
Current view: directory - editor/txmgr/src - nsTransactionManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 371 225 60.6 %
Date: 2012-04-21 Functions: 43 30 69.8 %

       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               2 : nsTransactionManager::nsTransactionManager(PRInt32 aMaxTransactionCount)
      50                 :   : mMaxTransactionCount(aMaxTransactionCount)
      51                 :   , mDoStack(nsTransactionStack::FOR_UNDO)
      52                 :   , mUndoStack(nsTransactionStack::FOR_UNDO)
      53               2 :   , mRedoStack(nsTransactionStack::FOR_REDO)
      54                 : {
      55               2 : }
      56                 : 
      57               4 : nsTransactionManager::~nsTransactionManager()
      58                 : {
      59               8 : }
      60                 : 
      61            1396 : 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             328 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransactionManager)
      78             218 :   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             108 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransactionManager)
      84             108 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransactionManager)
      85                 : 
      86                 : NS_IMETHODIMP
      87              26 : nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
      88                 : {
      89                 :   nsresult result;
      90                 : 
      91              26 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
      92                 : 
      93              26 :   bool doInterrupt = false;
      94                 : 
      95              26 :   result = WillDoNotify(aTransaction, &doInterrupt);
      96                 : 
      97              26 :   if (NS_FAILED(result)) {
      98               0 :     return result;
      99                 :   }
     100                 : 
     101              26 :   if (doInterrupt) {
     102               0 :     return NS_OK;
     103                 :   }
     104                 : 
     105              26 :   result = BeginTransaction(aTransaction);
     106                 : 
     107              26 :   if (NS_FAILED(result)) {
     108               0 :     DidDoNotify(aTransaction, result);
     109               0 :     return result;
     110                 :   }
     111                 : 
     112              26 :   result = EndTransaction();
     113                 : 
     114              26 :   nsresult result2 = DidDoNotify(aTransaction, result);
     115                 : 
     116              26 :   if (NS_SUCCEEDED(result))
     117              26 :     result = result2;
     118                 : 
     119              26 :   return result;
     120                 : }
     121                 : 
     122                 : NS_IMETHODIMP
     123               1 : nsTransactionManager::UndoTransaction()
     124                 : {
     125               1 :   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               2 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     132                 : 
     133               1 :   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               1 :   tx = mUndoStack.Peek();
     140                 : 
     141                 :   // Bail if there's nothing on the stack.
     142               1 :   if (!tx) {
     143               0 :     return NS_OK;
     144                 :   }
     145                 : 
     146               2 :   nsCOMPtr<nsITransaction> t;
     147                 : 
     148               1 :   result = tx->GetTransaction(getter_AddRefs(t));
     149                 : 
     150               1 :   if (NS_FAILED(result)) {
     151               0 :     return result;
     152                 :   }
     153                 : 
     154               1 :   bool doInterrupt = false;
     155                 : 
     156               1 :   result = WillUndoNotify(t, &doInterrupt);
     157                 : 
     158               1 :   if (NS_FAILED(result)) {
     159               0 :     return result;
     160                 :   }
     161                 : 
     162               1 :   if (doInterrupt) {
     163               0 :     return NS_OK;
     164                 :   }
     165                 : 
     166               1 :   result = tx->UndoTransaction(this);
     167                 : 
     168               1 :   if (NS_SUCCEEDED(result)) {
     169               1 :     tx = mUndoStack.Pop();
     170               1 :     mRedoStack.Push(tx);
     171                 :   }
     172                 : 
     173               1 :   nsresult result2 = DidUndoNotify(t, result);
     174                 : 
     175               1 :   if (NS_SUCCEEDED(result))
     176               1 :     result = result2;
     177                 : 
     178               1 :   return result;
     179                 : }
     180                 : 
     181                 : NS_IMETHODIMP
     182               0 : nsTransactionManager::RedoTransaction()
     183                 : {
     184               0 :   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               0 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     191                 : 
     192               0 :   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               0 :   tx = mRedoStack.Peek();
     199                 : 
     200                 :   // Bail if there's nothing on the stack.
     201               0 :   if (!tx) {
     202               0 :     return NS_OK;
     203                 :   }
     204                 : 
     205               0 :   nsCOMPtr<nsITransaction> t;
     206                 : 
     207               0 :   result = tx->GetTransaction(getter_AddRefs(t));
     208                 : 
     209               0 :   if (NS_FAILED(result)) {
     210               0 :     return result;
     211                 :   }
     212                 : 
     213               0 :   bool doInterrupt = false;
     214                 : 
     215               0 :   result = WillRedoNotify(t, &doInterrupt);
     216                 : 
     217               0 :   if (NS_FAILED(result)) {
     218               0 :     return result;
     219                 :   }
     220                 : 
     221               0 :   if (doInterrupt) {
     222               0 :     return NS_OK;
     223                 :   }
     224                 : 
     225               0 :   result = tx->RedoTransaction(this);
     226                 : 
     227               0 :   if (NS_SUCCEEDED(result)) {
     228               0 :     tx = mRedoStack.Pop();
     229               0 :     mUndoStack.Push(tx);
     230                 :   }
     231                 : 
     232               0 :   nsresult result2 = DidRedoNotify(t, result);
     233                 : 
     234               0 :   if (NS_SUCCEEDED(result))
     235               0 :     result = result2;
     236                 : 
     237               0 :   return result;
     238                 : }
     239                 : 
     240                 : NS_IMETHODIMP
     241               2 : nsTransactionManager::Clear()
     242                 : {
     243                 :   nsresult result;
     244                 : 
     245               2 :   result = ClearRedoStack();
     246                 : 
     247               2 :   if (NS_FAILED(result)) {
     248               0 :     return result;
     249                 :   }
     250                 : 
     251               2 :   result = ClearUndoStack();
     252                 : 
     253               2 :   return result;
     254                 : }
     255                 : 
     256                 : NS_IMETHODIMP
     257               1 : 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               1 :   bool doInterrupt = false;
     267                 : 
     268               1 :   result = WillBeginBatchNotify(&doInterrupt);
     269                 : 
     270               1 :   if (NS_FAILED(result)) {
     271               0 :     return result;
     272                 :   }
     273                 : 
     274               1 :   if (doInterrupt) {
     275               0 :     return NS_OK;
     276                 :   }
     277                 : 
     278               1 :   result = BeginTransaction(0);
     279                 :   
     280               1 :   nsresult result2 = DidBeginBatchNotify(result);
     281                 : 
     282               1 :   if (NS_SUCCEEDED(result))
     283               1 :     result = result2;
     284                 : 
     285               1 :   return result;
     286                 : }
     287                 : 
     288                 : NS_IMETHODIMP
     289               1 : nsTransactionManager::EndBatch()
     290                 : {
     291               2 :   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               2 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     306                 : 
     307               1 :   if (tx)
     308               1 :     tx->GetTransaction(getter_AddRefs(ti));
     309                 : 
     310               1 :   if (!tx || ti) {
     311               0 :     return NS_ERROR_FAILURE;
     312                 :   }
     313                 : 
     314               1 :   bool doInterrupt = false;
     315                 : 
     316               1 :   result = WillEndBatchNotify(&doInterrupt);
     317                 : 
     318               1 :   if (NS_FAILED(result)) {
     319               0 :     return result;
     320                 :   }
     321                 : 
     322               1 :   if (doInterrupt) {
     323               0 :     return NS_OK;
     324                 :   }
     325                 : 
     326               1 :   result = EndTransaction();
     327                 : 
     328               1 :   nsresult result2 = DidEndBatchNotify(result);
     329                 : 
     330               1 :   if (NS_SUCCEEDED(result))
     331               1 :     result = result2;
     332                 : 
     333               1 :   return result;
     334                 : }
     335                 : 
     336                 : NS_IMETHODIMP
     337               0 : nsTransactionManager::GetNumberOfUndoItems(PRInt32 *aNumItems)
     338                 : {
     339               0 :   *aNumItems = mUndoStack.GetSize();
     340               0 :   return NS_OK;
     341                 : }
     342                 : 
     343                 : NS_IMETHODIMP
     344               0 : nsTransactionManager::GetNumberOfRedoItems(PRInt32 *aNumItems)
     345                 : {
     346               0 :   *aNumItems = mRedoStack.GetSize();
     347               0 :   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               0 : nsTransactionManager::SetMaxTransactionCount(PRInt32 aMaxCount)
     362                 : {
     363               0 :   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               0 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Peek();
     372                 : 
     373               0 :   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               0 :   if (aMaxCount < 0) {
     381               0 :     mMaxTransactionCount = -1;
     382               0 :     return NS_OK;
     383                 :   }
     384                 : 
     385               0 :   numUndoItems = mUndoStack.GetSize();
     386                 : 
     387               0 :   numRedoItems = mRedoStack.GetSize();
     388                 : 
     389               0 :   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               0 :   if (aMaxCount > total ) {
     396               0 :     mMaxTransactionCount = aMaxCount;
     397               0 :     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               0 :   while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
     404               0 :     tx = mUndoStack.PopBottom();
     405                 : 
     406               0 :     if (!tx) {
     407               0 :       return NS_ERROR_FAILURE;
     408                 :     }
     409                 : 
     410               0 :     --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               0 :   while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
     417               0 :     tx = mRedoStack.PopBottom();
     418                 : 
     419               0 :     if (!tx) {
     420               0 :       return NS_ERROR_FAILURE;
     421                 :     }
     422                 : 
     423               0 :     --numRedoItems;
     424                 :   }
     425                 : 
     426               0 :   mMaxTransactionCount = aMaxCount;
     427                 : 
     428               0 :   return NS_OK;
     429                 : }
     430                 : 
     431                 : NS_IMETHODIMP
     432               0 : nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
     433                 : {
     434                 :   nsresult result;
     435                 : 
     436               0 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
     437                 : 
     438               0 :   *aTransaction = 0;
     439                 : 
     440               0 :   nsRefPtr<nsTransactionItem> tx = mUndoStack.Peek();
     441                 : 
     442               0 :   if (!tx) {
     443               0 :     return NS_OK;
     444                 :   }
     445                 : 
     446               0 :   result = tx->GetTransaction(aTransaction);
     447                 : 
     448               0 :   return result;
     449                 : }
     450                 : 
     451                 : NS_IMETHODIMP
     452               0 : nsTransactionManager::PeekRedoStack(nsITransaction **aTransaction)
     453                 : {
     454                 :   nsresult result;
     455                 : 
     456               0 :   NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
     457                 : 
     458               0 :   *aTransaction = 0;
     459                 : 
     460               0 :   nsRefPtr<nsTransactionItem> tx = mRedoStack.Peek();
     461                 : 
     462               0 :   if (!tx) {
     463               0 :     return NS_OK;
     464                 :   }
     465                 : 
     466               0 :   result = tx->GetTransaction(aTransaction);
     467                 : 
     468               0 :   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               2 : nsTransactionManager::AddListener(nsITransactionListener *aListener)
     497                 : {
     498               2 :   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               2 : nsTransactionManager::RemoveListener(nsITransactionListener *aListener)
     505                 : {
     506               2 :   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               2 : nsTransactionManager::ClearUndoStack()
     513                 : {
     514               2 :   mUndoStack.Clear();
     515               2 :   return NS_OK;
     516                 : }
     517                 : 
     518                 : nsresult
     519              25 : nsTransactionManager::ClearRedoStack()
     520                 : {
     521              25 :   mRedoStack.Clear();
     522              25 :   return NS_OK;
     523                 : }
     524                 : 
     525                 : nsresult
     526              26 : nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     527                 : {
     528              26 :   nsresult result = NS_OK;
     529              52 :   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              26 :   return result;
     542                 : }
     543                 : 
     544                 : nsresult
     545              26 : nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
     546                 : {
     547              26 :   nsresult result = NS_OK;
     548              52 :   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              26 :   return result;
     561                 : }
     562                 : 
     563                 : nsresult
     564               2 : nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     565                 : {
     566               2 :   nsresult result = NS_OK;
     567               4 :   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               2 :   return result;
     580                 : }
     581                 : 
     582                 : nsresult
     583               2 : nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
     584                 : {
     585               2 :   nsresult result = NS_OK;
     586               4 :   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               2 :   return result;
     599                 : }
     600                 : 
     601                 : nsresult
     602               0 : nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt)
     603                 : {
     604               0 :   nsresult result = NS_OK;
     605               0 :   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               0 :   return result;
     618                 : }
     619                 : 
     620                 : nsresult
     621               0 : nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
     622                 : {
     623               0 :   nsresult result = NS_OK;
     624               0 :   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               0 :   return result;
     637                 : }
     638                 : 
     639                 : nsresult
     640               1 : nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
     641                 : {
     642               1 :   nsresult result = NS_OK;
     643               2 :   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               1 :   return result;
     656                 : }
     657                 : 
     658                 : nsresult
     659               1 : nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
     660                 : {
     661               1 :   nsresult result = NS_OK;
     662               2 :   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               1 :   return result;
     675                 : }
     676                 : 
     677                 : nsresult
     678               1 : nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
     679                 : {
     680               1 :   nsresult result = NS_OK;
     681               2 :   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               1 :   return result;
     694                 : }
     695                 : 
     696                 : nsresult
     697               1 : nsTransactionManager::DidEndBatchNotify(nsresult aResult)
     698                 : {
     699               1 :   nsresult result = NS_OK;
     700               2 :   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               1 :   return result;
     713                 : }
     714                 : 
     715                 : nsresult
     716              20 : nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, bool *aInterrupt)
     717                 : {
     718              20 :   nsresult result = NS_OK;
     719              40 :   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              20 :   return result;
     732                 : }
     733                 : 
     734                 : nsresult
     735              20 : nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
     736                 :                                      nsITransaction *aTransaction,
     737                 :                                      bool aDidMerge,
     738                 :                                      nsresult aMergeResult)
     739                 : {
     740              20 :   nsresult result = NS_OK;
     741              40 :   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              20 :   return result;
     754                 : }
     755                 : 
     756                 : nsresult
     757              27 : nsTransactionManager::BeginTransaction(nsITransaction *aTransaction)
     758                 : {
     759              27 :   nsresult result = NS_OK;
     760                 : 
     761                 :   // XXX: POSSIBLE OPTIMIZATION
     762                 :   //      We could use a factory that pre-allocates/recycles transaction items.
     763              54 :   nsRefPtr<nsTransactionItem> tx = new nsTransactionItem(aTransaction);
     764                 : 
     765              27 :   if (!tx) {
     766               0 :     return NS_ERROR_OUT_OF_MEMORY;
     767                 :   }
     768                 : 
     769              27 :   mDoStack.Push(tx);
     770                 : 
     771              27 :   result = tx->DoTransaction();
     772                 : 
     773              27 :   if (NS_FAILED(result)) {
     774               0 :     tx = mDoStack.Pop();
     775               0 :     return result;
     776                 :   }
     777                 : 
     778              27 :   return NS_OK;
     779                 : }
     780                 : 
     781                 : nsresult
     782              27 : nsTransactionManager::EndTransaction()
     783                 : {
     784              54 :   nsCOMPtr<nsITransaction> tint;
     785              27 :   nsresult result              = NS_OK;
     786                 : 
     787              54 :   nsRefPtr<nsTransactionItem> tx = mDoStack.Pop();
     788                 : 
     789              27 :   if (!tx)
     790               0 :     return NS_ERROR_FAILURE;
     791                 : 
     792              27 :   result = tx->GetTransaction(getter_AddRefs(tint));
     793                 : 
     794              27 :   if (NS_FAILED(result)) {
     795                 :     // XXX: What do we do with the transaction item at this point?
     796               0 :     return result;
     797                 :   }
     798                 : 
     799              27 :   if (!tint) {
     800               1 :     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               1 :     tx->GetNumberOfChildren(&nc);
     806                 : 
     807               1 :     if (!nc) {
     808               0 :       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              27 :   bool isTransient = false;
     816                 : 
     817              27 :   if (tint)
     818              26 :     result = tint->GetIsTransient(&isTransient);
     819                 : 
     820              27 :   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               0 :     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              54 :   nsRefPtr<nsTransactionItem> top = mDoStack.Peek();
     831              27 :   if (top) {
     832               4 :     result = top->AddChild(tx);
     833                 : 
     834                 :     // XXX: What do we do if this fails?
     835                 : 
     836               4 :     return result;
     837                 :   }
     838                 : 
     839                 :   // The transaction succeeded, so clear the redo stack.
     840                 : 
     841              23 :   result = ClearRedoStack();
     842                 : 
     843              23 :   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              23 :   top = mUndoStack.Peek();
     851                 : 
     852              23 :   if (tint && top) {
     853              20 :     bool didMerge = false;
     854              40 :     nsCOMPtr<nsITransaction> topTransaction;
     855                 : 
     856              20 :     result = top->GetTransaction(getter_AddRefs(topTransaction));
     857                 : 
     858              20 :     if (topTransaction) {
     859                 : 
     860              20 :       bool doInterrupt = false;
     861                 : 
     862              20 :       result = WillMergeNotify(topTransaction, tint, &doInterrupt);
     863                 : 
     864              20 :       NS_ENSURE_SUCCESS(result, result);
     865                 : 
     866              20 :       if (!doInterrupt) {
     867              20 :         result = topTransaction->Merge(tint, &didMerge);
     868                 : 
     869              20 :         nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);
     870                 : 
     871              20 :         if (NS_SUCCEEDED(result))
     872              20 :           result = result2;
     873                 : 
     874              20 :         if (NS_FAILED(result)) {
     875                 :           // XXX: What do we do if this fails?
     876                 :         }
     877                 : 
     878              20 :         if (didMerge) {
     879               0 :           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              23 :   PRInt32 sz = mUndoStack.GetSize();
     889                 : 
     890              23 :   if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
     891               0 :     nsRefPtr<nsTransactionItem> overflow = mUndoStack.PopBottom();
     892                 :   }
     893                 : 
     894                 :   // Push the transaction on the undo stack:
     895                 : 
     896              23 :   mUndoStack.Push(tx);
     897                 : 
     898              23 :   return NS_OK;
     899            4188 : }
     900                 : 

Generated by: LCOV version 1.7