tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TransactionItem.cpp (8035B)


      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 "TransactionItem.h"
      7 
      8 #include "mozilla/mozalloc.h"
      9 #include "mozilla/DebugOnly.h"
     10 #include "mozilla/IntegerRange.h"
     11 #include "mozilla/OwningNonNull.h"
     12 #include "mozilla/TransactionManager.h"
     13 #include "mozilla/TransactionStack.h"
     14 #include "nsCOMPtr.h"
     15 #include "nsDebug.h"
     16 #include "nsError.h"
     17 #include "nsISupportsImpl.h"
     18 #include "nsITransaction.h"
     19 
     20 namespace mozilla {
     21 
     22 TransactionItem::TransactionItem(nsITransaction* aTransaction)
     23    : mTransaction(aTransaction), mUndoStack(0), mRedoStack(0) {}
     24 
     25 TransactionItem::~TransactionItem() {
     26  delete mRedoStack;
     27  delete mUndoStack;
     28 }
     29 
     30 void TransactionItem::CleanUp() {
     31  mData.Clear();
     32  mTransaction = nullptr;
     33  if (mRedoStack) {
     34    mRedoStack->DoUnlink();
     35  }
     36  if (mUndoStack) {
     37    mUndoStack->DoUnlink();
     38  }
     39 }
     40 
     41 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(TransactionItem)
     42 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(TransactionItem,
     43                                                          CleanUp())
     44 
     45 NS_IMPL_CYCLE_COLLECTION_CLASS(TransactionItem)
     46 
     47 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TransactionItem)
     48  tmp->CleanUp();
     49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     50 
     51 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TransactionItem)
     52  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
     53  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
     54  if (tmp->mRedoStack) {
     55    tmp->mRedoStack->DoTraverse(cb);
     56  }
     57  if (tmp->mUndoStack) {
     58    tmp->mUndoStack->DoTraverse(cb);
     59  }
     60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     61 
     62 nsresult TransactionItem::AddChild(TransactionItem& aTransactionItem) {
     63  if (!mUndoStack) {
     64    mUndoStack = new TransactionStack(TransactionStack::FOR_UNDO);
     65  }
     66 
     67  mUndoStack->Push(&aTransactionItem);
     68  return NS_OK;
     69 }
     70 
     71 already_AddRefed<nsITransaction> TransactionItem::GetTransaction() {
     72  return do_AddRef(mTransaction);
     73 }
     74 
     75 nsresult TransactionItem::DoTransaction() {
     76  if (!mTransaction) {
     77    return NS_OK;
     78  }
     79  OwningNonNull<nsITransaction> transaction = *mTransaction;
     80  nsresult rv = transaction->DoTransaction();
     81  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     82                       "nsITransaction::DoTransaction() failed");
     83  return rv;
     84 }
     85 
     86 nsresult TransactionItem::UndoTransaction(
     87    TransactionManager* aTransactionManager) {
     88  nsresult rv = UndoChildren(aTransactionManager);
     89  if (NS_FAILED(rv)) {
     90    NS_WARNING("TransactionItem::UndoChildren() failed");
     91    DebugOnly<nsresult> rvIgnored = RecoverFromUndoError(aTransactionManager);
     92    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
     93                         "TransactionItem::RecoverFromUndoError() failed");
     94    return rv;
     95  }
     96 
     97  if (!mTransaction) {
     98    return NS_OK;
     99  }
    100 
    101  OwningNonNull<nsITransaction> transaction = *mTransaction;
    102  rv = transaction->UndoTransaction();
    103  if (NS_SUCCEEDED(rv)) {
    104    return NS_OK;
    105  }
    106 
    107  NS_WARNING("TransactionItem::UndoTransaction() failed");
    108  DebugOnly<nsresult> rvIgnored = RecoverFromUndoError(aTransactionManager);
    109  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    110                       "TransactionItem::RecoverFromUndoError() failed");
    111  return rv;
    112 }
    113 
    114 nsresult TransactionItem::UndoChildren(
    115    TransactionManager* aTransactionManager) {
    116  if (!mUndoStack) {
    117    return NS_OK;
    118  }
    119 
    120  if (!mRedoStack) {
    121    mRedoStack = new TransactionStack(TransactionStack::FOR_REDO);
    122  }
    123 
    124  const size_t undoStackSize = mUndoStack->GetSize();
    125 
    126  nsresult rv = NS_OK;
    127  for ([[maybe_unused]] const size_t undoneCount :
    128       IntegerRange(undoStackSize)) {
    129    RefPtr<TransactionItem> transactionItem = mUndoStack->Peek();
    130    if (!transactionItem) {
    131      return NS_ERROR_FAILURE;
    132    }
    133 
    134    nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
    135    rv = transactionItem->UndoTransaction(aTransactionManager);
    136    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    137                         "TransactionItem::UndoTransaction() failed");
    138    if (NS_SUCCEEDED(rv)) {
    139      transactionItem = mUndoStack->Pop();
    140      mRedoStack->Push(transactionItem.forget());
    141    }
    142 
    143    if (transaction) {
    144      aTransactionManager->DidUndoNotify(*transaction, rv);
    145    }
    146  }
    147  // NS_OK if there is no Undo items or all methods work fine, otherwise,
    148  // the result of the last item's UndoTransaction().
    149  return rv;
    150 }
    151 
    152 nsresult TransactionItem::RedoTransaction(
    153    TransactionManager* aTransactionManager) {
    154  nsCOMPtr<nsITransaction> transaction(mTransaction);
    155  if (transaction) {
    156    nsresult rv = transaction->RedoTransaction();
    157    if (NS_FAILED(rv)) {
    158      NS_WARNING("nsITransaction::RedoTransaction() failed");
    159      return rv;
    160    }
    161  }
    162 
    163  nsresult rv = RedoChildren(aTransactionManager);
    164  if (NS_SUCCEEDED(rv)) {
    165    return NS_OK;
    166  }
    167 
    168  NS_WARNING("TransactionItem::RedoChildren() failed");
    169  DebugOnly<nsresult> rvIgnored = RecoverFromRedoError(aTransactionManager);
    170  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    171                       "TransactionItem::RecoverFromRedoError() failed");
    172  return rv;
    173 }
    174 
    175 nsresult TransactionItem::RedoChildren(
    176    TransactionManager* aTransactionManager) {
    177  if (!mRedoStack) {
    178    return NS_OK;
    179  }
    180 
    181  /* Redo all of the transaction items children! */
    182  const size_t redoStackSize = mRedoStack->GetSize();
    183 
    184  nsresult rv = NS_OK;
    185  for ([[maybe_unused]] const size_t redoneCount :
    186       IntegerRange(redoStackSize)) {
    187    RefPtr<TransactionItem> transactionItem = mRedoStack->Peek();
    188    if (NS_WARN_IF(!transactionItem)) {
    189      return NS_ERROR_FAILURE;
    190    }
    191 
    192    nsCOMPtr<nsITransaction> transaction = transactionItem->GetTransaction();
    193    rv = transactionItem->RedoTransaction(aTransactionManager);
    194    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    195                         "TransactionItem::RedoTransaction() failed");
    196    if (NS_SUCCEEDED(rv)) {
    197      transactionItem = mRedoStack->Pop();
    198      mUndoStack->Push(transactionItem.forget());
    199    }
    200 
    201    // XXX Shouldn't this DidRedoNotify()? (bug 1311626)
    202    if (transaction) {
    203      aTransactionManager->DidUndoNotify(*transaction, rv);
    204    }
    205  }
    206  // NS_OK if there is no Redo items or all methods work fine, otherwise,
    207  // the result of the last item's RedoTransaction().
    208  return rv;
    209 }
    210 
    211 size_t TransactionItem::NumberOfUndoItems() const {
    212  NS_WARNING_ASSERTION(!mUndoStack || mUndoStack->GetSize() > 0,
    213                       "UndoStack cannot have no children");
    214  return mUndoStack ? mUndoStack->GetSize() : 0;
    215 }
    216 
    217 size_t TransactionItem::NumberOfRedoItems() const {
    218  NS_WARNING_ASSERTION(!mRedoStack || mRedoStack->GetSize() > 0,
    219                       "UndoStack cannot have no children");
    220  return mRedoStack ? mRedoStack->GetSize() : 0;
    221 }
    222 
    223 nsresult TransactionItem::RecoverFromUndoError(
    224    TransactionManager* aTransactionManager) {
    225  // If this method gets called, we never got to the point where we
    226  // successfully called UndoTransaction() for the transaction item itself.
    227  // Just redo any children that successfully called undo!
    228  nsresult rv = RedoChildren(aTransactionManager);
    229  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    230                       "TransactionItem::RedoChildren() failed");
    231  return rv;
    232 }
    233 
    234 nsresult TransactionItem::RecoverFromRedoError(
    235    TransactionManager* aTransactionManager) {
    236  // If this method gets called, we already successfully called
    237  // RedoTransaction() for the transaction item itself. Undo all
    238  // the children that successfully called RedoTransaction(),
    239  // then undo the transaction item itself.
    240  nsresult rv = UndoChildren(aTransactionManager);
    241  if (NS_FAILED(rv)) {
    242    NS_WARNING("TransactionItem::UndoChildren() failed");
    243    return rv;
    244  }
    245 
    246  if (!mTransaction) {
    247    return NS_OK;
    248  }
    249 
    250  OwningNonNull<nsITransaction> transaction = *mTransaction;
    251  rv = transaction->UndoTransaction();
    252  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    253                       "nsITransaction::UndoTransaction() failed");
    254  return rv;
    255 }
    256 
    257 }  // namespace mozilla