editor/txmgr/src/nsTransactionManager.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial