nsresult
nsTransactionManager::EndTransaction()
{
  nsCOMPtr<nsITransaction> tint;
  nsRefPtr<nsTransactionItem> tx;
  nsresult result              = NS_OK;

  // No need for LOCK/UNLOCK_TX_MANAGER() calls since the calling routine
  // should have done this already!

  result = mDoStack.Pop(getter_AddRefs(tx));

  if (NS_FAILED(result) || !tx)
    return result;

  result = tx->GetTransaction(getter_AddRefs(tint));

  if (NS_FAILED(result)) {
    // XXX: What do we do with the transaction item at this point?
    return result;
  }

  if (!tint) {
    PRInt32 nc = 0;

    // If we get here, the transaction must be a dummy batch transaction
    // created by BeginBatch(). If it contains no children, get rid of it!

    tx->GetNumberOfChildren(&nc);

    if (!nc) {
      return result;
    }
  }

  // Check if the transaction is transient. If it is, there's nothing
  // more to do, just return.

  PRBool isTransient = PR_FALSE;

  if (tint)
    result = tint->GetIsTransient(&isTransient);

  if (NS_FAILED(result) || isTransient || !mMaxTransactionCount) {
    // XXX: Should we be clearing the redo stack if the transaction
    //      is transient and there is nothing on the do stack?
    return result;
  }

  nsRefPtr<nsTransactionItem> top;

  // Check if there is a transaction on the do stack. If there is,
  // the current transaction is a "sub" transaction, and should
  // be added to the transaction at the top of the do stack.

  result = mDoStack.Peek(getter_AddRefs(top));
  if (top) {
    result = top->AddChild(tx);

    // XXX: What do we do if this fails?

    return result;
  }

  // The transaction succeeded, so clear the redo stack.

  result = ClearRedoStack();

  if (NS_FAILED(result)) {
    // XXX: What do we do if this fails?
  }

  // Check if we can coalesce this transaction with the one at the top
  // of the undo stack.

  top = 0;
  result = mUndoStack.Peek(getter_AddRefs(top));

  if (tint && top) {
    PRBool didMerge = PR_FALSE;
    nsCOMPtr<nsITransaction> topTransaction;

    result = top->GetTransaction(getter_AddRefs(topTransaction));

    if (topTransaction) {

      PRBool doInterrupt = PR_FALSE;

      result = WillMergeNotify(topTransaction, tint, &doInterrupt);

      NS_ENSURE_SUCCESS(result, result);

      if (!doInterrupt) {
        result = topTransaction->Merge(tint, &didMerge);

        nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);

        if (NS_SUCCEEDED(result))
          result = result2;

        if (NS_FAILED(result)) {
          // XXX: What do we do if this fails?
        }

        if (didMerge) {
          return result;
        }
      }
    }
  }

  // Check to see if we've hit the max level of undo. If so,
  // pop the bottom transaction off the undo stack and release it!

  PRInt32 sz = 0;

  result = mUndoStack.GetSize(&sz);

  if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
    nsRefPtr<nsTransactionItem> overflow;

    result = mUndoStack.PopBottom(getter_AddRefs(overflow));

    // XXX: What do we do in the case where this fails?
  }

  // Push the transaction on the undo stack:

  result = mUndoStack.Push(tx);

  if (NS_FAILED(result)) {
    // XXX: What do we do in the case where a clear fails?
    //      Remove the transaction from the stack, and release it?
  }

  return result;
}
nsresult
nsTransactionManager::EndTransaction()
{
  nsresult result              = NS_OK;

  nsRefPtr<nsTransactionItem> tx = mDoStack.Pop();

  if (!tx)
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsITransaction> tint = tx->GetTransaction();

  if (!tint) {
    PRInt32 nc = 0;

    // If we get here, the transaction must be a dummy batch transaction
    // created by BeginBatch(). If it contains no children, get rid of it!

    tx->GetNumberOfChildren(&nc);

    if (!nc) {
      return result;
    }
  }

  // Check if the transaction is transient. If it is, there's nothing
  // more to do, just return.

  bool isTransient = false;

  if (tint)
    result = tint->GetIsTransient(&isTransient);

  if (NS_FAILED(result) || isTransient || !mMaxTransactionCount) {
    // XXX: Should we be clearing the redo stack if the transaction
    //      is transient and there is nothing on the do stack?
    return result;
  }

  // Check if there is a transaction on the do stack. If there is,
  // the current transaction is a "sub" transaction, and should
  // be added to the transaction at the top of the do stack.

  nsRefPtr<nsTransactionItem> top = mDoStack.Peek();
  if (top) {
    result = top->AddChild(tx);

    // XXX: What do we do if this fails?

    return result;
  }

  // The transaction succeeded, so clear the redo stack.

  result = ClearRedoStack();

  if (NS_FAILED(result)) {
    // XXX: What do we do if this fails?
  }

  // Check if we can coalesce this transaction with the one at the top
  // of the undo stack.

  top = mUndoStack.Peek();

  if (tint && top) {
    bool didMerge = false;
    nsCOMPtr<nsITransaction> topTransaction = top->GetTransaction();

    if (topTransaction) {

      bool doInterrupt = false;

      result = WillMergeNotify(topTransaction, tint, &doInterrupt);

      NS_ENSURE_SUCCESS(result, result);

      if (!doInterrupt) {
        result = topTransaction->Merge(tint, &didMerge);

        nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);

        if (NS_SUCCEEDED(result))
          result = result2;

        if (NS_FAILED(result)) {
          // XXX: What do we do if this fails?
        }

        if (didMerge) {
          return result;
        }
      }
    }
  }

  // Check to see if we've hit the max level of undo. If so,
  // pop the bottom transaction off the undo stack and release it!

  PRInt32 sz = mUndoStack.GetSize();

  if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
    nsRefPtr<nsTransactionItem> overflow = mUndoStack.PopBottom();
  }

  // Push the transaction on the undo stack:

  mUndoStack.Push(tx);

  return NS_OK;
}