editor/txmgr/src/nsTransactionItem.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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/mozalloc.h"
michael@0 7 #include "nsAutoPtr.h"
michael@0 8 #include "nsCOMPtr.h"
michael@0 9 #include "nsDebug.h"
michael@0 10 #include "nsError.h"
michael@0 11 #include "nsISupportsImpl.h"
michael@0 12 #include "nsITransaction.h"
michael@0 13 #include "nsTransactionItem.h"
michael@0 14 #include "nsTransactionManager.h"
michael@0 15 #include "nsTransactionStack.h"
michael@0 16
michael@0 17 nsTransactionItem::nsTransactionItem(nsITransaction *aTransaction)
michael@0 18 : mTransaction(aTransaction), mUndoStack(0), mRedoStack(0)
michael@0 19 {
michael@0 20 }
michael@0 21
michael@0 22 nsTransactionItem::~nsTransactionItem()
michael@0 23 {
michael@0 24 delete mRedoStack;
michael@0 25
michael@0 26 delete mUndoStack;
michael@0 27 }
michael@0 28
michael@0 29 void
michael@0 30 nsTransactionItem::CleanUp()
michael@0 31 {
michael@0 32 mData.Clear();
michael@0 33 mTransaction = nullptr;
michael@0 34 if (mRedoStack) {
michael@0 35 mRedoStack->DoUnlink();
michael@0 36 }
michael@0 37 if (mUndoStack) {
michael@0 38 mUndoStack->DoUnlink();
michael@0 39 }
michael@0 40 }
michael@0 41
michael@0 42 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(nsTransactionItem)
michael@0 43 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(nsTransactionItem,
michael@0 44 CleanUp())
michael@0 45
michael@0 46 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionItem)
michael@0 47
michael@0 48 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionItem)
michael@0 49 tmp->CleanUp();
michael@0 50 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 51
michael@0 52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionItem)
michael@0 53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
michael@0 54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
michael@0 55 if (tmp->mRedoStack) {
michael@0 56 tmp->mRedoStack->DoTraverse(cb);
michael@0 57 }
michael@0 58 if (tmp->mUndoStack) {
michael@0 59 tmp->mUndoStack->DoTraverse(cb);
michael@0 60 }
michael@0 61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 62
michael@0 63 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTransactionItem, AddRef)
michael@0 64 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTransactionItem, Release)
michael@0 65
michael@0 66 nsresult
michael@0 67 nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem)
michael@0 68 {
michael@0 69 NS_ENSURE_TRUE(aTransactionItem, NS_ERROR_NULL_POINTER);
michael@0 70
michael@0 71 if (!mUndoStack) {
michael@0 72 mUndoStack = new nsTransactionStack(nsTransactionStack::FOR_UNDO);
michael@0 73 }
michael@0 74
michael@0 75 mUndoStack->Push(aTransactionItem);
michael@0 76
michael@0 77 return NS_OK;
michael@0 78 }
michael@0 79
michael@0 80 already_AddRefed<nsITransaction>
michael@0 81 nsTransactionItem::GetTransaction()
michael@0 82 {
michael@0 83 nsCOMPtr<nsITransaction> txn = mTransaction;
michael@0 84 return txn.forget();
michael@0 85 }
michael@0 86
michael@0 87 nsresult
michael@0 88 nsTransactionItem::GetIsBatch(bool *aIsBatch)
michael@0 89 {
michael@0 90 NS_ENSURE_TRUE(aIsBatch, NS_ERROR_NULL_POINTER);
michael@0 91
michael@0 92 *aIsBatch = !mTransaction;
michael@0 93
michael@0 94 return NS_OK;
michael@0 95 }
michael@0 96
michael@0 97 nsresult
michael@0 98 nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren)
michael@0 99 {
michael@0 100 nsresult result;
michael@0 101
michael@0 102 NS_ENSURE_TRUE(aNumChildren, NS_ERROR_NULL_POINTER);
michael@0 103
michael@0 104 *aNumChildren = 0;
michael@0 105
michael@0 106 int32_t ui = 0;
michael@0 107 int32_t ri = 0;
michael@0 108
michael@0 109 result = GetNumberOfUndoItems(&ui);
michael@0 110
michael@0 111 NS_ENSURE_SUCCESS(result, result);
michael@0 112
michael@0 113 result = GetNumberOfRedoItems(&ri);
michael@0 114
michael@0 115 NS_ENSURE_SUCCESS(result, result);
michael@0 116
michael@0 117 *aNumChildren = ui + ri;
michael@0 118
michael@0 119 return NS_OK;
michael@0 120 }
michael@0 121
michael@0 122 nsresult
michael@0 123 nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
michael@0 124 {
michael@0 125 NS_ENSURE_TRUE(aChild, NS_ERROR_NULL_POINTER);
michael@0 126
michael@0 127 *aChild = 0;
michael@0 128
michael@0 129 int32_t numItems = 0;
michael@0 130 nsresult result = GetNumberOfChildren(&numItems);
michael@0 131
michael@0 132 NS_ENSURE_SUCCESS(result, result);
michael@0 133
michael@0 134 if (aIndex < 0 || aIndex >= numItems)
michael@0 135 return NS_ERROR_FAILURE;
michael@0 136
michael@0 137 // Children are expected to be in the order they were added,
michael@0 138 // so the child first added would be at the bottom of the undo
michael@0 139 // stack, or if there are no items on the undo stack, it would
michael@0 140 // be at the top of the redo stack.
michael@0 141
michael@0 142 result = GetNumberOfUndoItems(&numItems);
michael@0 143
michael@0 144 NS_ENSURE_SUCCESS(result, result);
michael@0 145
michael@0 146 if (numItems > 0 && aIndex < numItems) {
michael@0 147 NS_ENSURE_TRUE(mUndoStack, NS_ERROR_FAILURE);
michael@0 148
michael@0 149 nsRefPtr<nsTransactionItem> child = mUndoStack->GetItem(aIndex);
michael@0 150 child.forget(aChild);
michael@0 151 return *aChild ? NS_OK : NS_ERROR_FAILURE;
michael@0 152 }
michael@0 153
michael@0 154 // Adjust the index for the redo stack:
michael@0 155
michael@0 156 aIndex -= numItems;
michael@0 157
michael@0 158 result = GetNumberOfRedoItems(&numItems);
michael@0 159
michael@0 160 NS_ENSURE_SUCCESS(result, result);
michael@0 161
michael@0 162 NS_ENSURE_TRUE(mRedoStack && numItems != 0 && aIndex < numItems, NS_ERROR_FAILURE);
michael@0 163
michael@0 164 nsRefPtr<nsTransactionItem> child = mRedoStack->GetItem(aIndex);
michael@0 165 child.forget(aChild);
michael@0 166 return *aChild ? NS_OK : NS_ERROR_FAILURE;
michael@0 167 }
michael@0 168
michael@0 169 nsresult
michael@0 170 nsTransactionItem::DoTransaction()
michael@0 171 {
michael@0 172 if (mTransaction)
michael@0 173 return mTransaction->DoTransaction();
michael@0 174 return NS_OK;
michael@0 175 }
michael@0 176
michael@0 177 nsresult
michael@0 178 nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
michael@0 179 {
michael@0 180 nsresult result = UndoChildren(aTxMgr);
michael@0 181
michael@0 182 if (NS_FAILED(result)) {
michael@0 183 RecoverFromUndoError(aTxMgr);
michael@0 184 return result;
michael@0 185 }
michael@0 186
michael@0 187 if (!mTransaction)
michael@0 188 return NS_OK;
michael@0 189
michael@0 190 result = mTransaction->UndoTransaction();
michael@0 191
michael@0 192 if (NS_FAILED(result)) {
michael@0 193 RecoverFromUndoError(aTxMgr);
michael@0 194 return result;
michael@0 195 }
michael@0 196
michael@0 197 return NS_OK;
michael@0 198 }
michael@0 199
michael@0 200 nsresult
michael@0 201 nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
michael@0 202 {
michael@0 203 nsRefPtr<nsTransactionItem> item;
michael@0 204 nsresult result = NS_OK;
michael@0 205 int32_t sz = 0;
michael@0 206
michael@0 207 if (mUndoStack) {
michael@0 208 if (!mRedoStack && mUndoStack) {
michael@0 209 mRedoStack = new nsTransactionStack(nsTransactionStack::FOR_REDO);
michael@0 210 }
michael@0 211
michael@0 212 /* Undo all of the transaction items children! */
michael@0 213 sz = mUndoStack->GetSize();
michael@0 214
michael@0 215 while (sz-- > 0) {
michael@0 216 item = mUndoStack->Peek();
michael@0 217
michael@0 218 if (!item) {
michael@0 219 return NS_ERROR_FAILURE;
michael@0 220 }
michael@0 221
michael@0 222 nsCOMPtr<nsITransaction> t = item->GetTransaction();
michael@0 223
michael@0 224 bool doInterrupt = false;
michael@0 225
michael@0 226 result = aTxMgr->WillUndoNotify(t, &doInterrupt);
michael@0 227
michael@0 228 if (NS_FAILED(result)) {
michael@0 229 return result;
michael@0 230 }
michael@0 231
michael@0 232 if (doInterrupt) {
michael@0 233 return NS_OK;
michael@0 234 }
michael@0 235
michael@0 236 result = item->UndoTransaction(aTxMgr);
michael@0 237
michael@0 238 if (NS_SUCCEEDED(result)) {
michael@0 239 item = mUndoStack->Pop();
michael@0 240 mRedoStack->Push(item);
michael@0 241 }
michael@0 242
michael@0 243 nsresult result2 = aTxMgr->DidUndoNotify(t, result);
michael@0 244
michael@0 245 if (NS_SUCCEEDED(result)) {
michael@0 246 result = result2;
michael@0 247 }
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 return result;
michael@0 252 }
michael@0 253
michael@0 254 nsresult
michael@0 255 nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
michael@0 256 {
michael@0 257 nsresult result;
michael@0 258
michael@0 259 nsCOMPtr<nsITransaction> kungfuDeathGrip(mTransaction);
michael@0 260 if (mTransaction) {
michael@0 261 result = mTransaction->RedoTransaction();
michael@0 262
michael@0 263 NS_ENSURE_SUCCESS(result, result);
michael@0 264 }
michael@0 265
michael@0 266 result = RedoChildren(aTxMgr);
michael@0 267
michael@0 268 if (NS_FAILED(result)) {
michael@0 269 RecoverFromRedoError(aTxMgr);
michael@0 270 return result;
michael@0 271 }
michael@0 272
michael@0 273 return NS_OK;
michael@0 274 }
michael@0 275
michael@0 276 nsresult
michael@0 277 nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
michael@0 278 {
michael@0 279 nsRefPtr<nsTransactionItem> item;
michael@0 280 nsresult result = NS_OK;
michael@0 281
michael@0 282 if (!mRedoStack)
michael@0 283 return NS_OK;
michael@0 284
michael@0 285 /* Redo all of the transaction items children! */
michael@0 286 int32_t sz = mRedoStack->GetSize();
michael@0 287
michael@0 288 while (sz-- > 0) {
michael@0 289 item = mRedoStack->Peek();
michael@0 290
michael@0 291 if (!item) {
michael@0 292 return NS_ERROR_FAILURE;
michael@0 293 }
michael@0 294
michael@0 295 nsCOMPtr<nsITransaction> t = item->GetTransaction();
michael@0 296
michael@0 297 bool doInterrupt = false;
michael@0 298
michael@0 299 result = aTxMgr->WillRedoNotify(t, &doInterrupt);
michael@0 300
michael@0 301 if (NS_FAILED(result)) {
michael@0 302 return result;
michael@0 303 }
michael@0 304
michael@0 305 if (doInterrupt) {
michael@0 306 return NS_OK;
michael@0 307 }
michael@0 308
michael@0 309 result = item->RedoTransaction(aTxMgr);
michael@0 310
michael@0 311 if (NS_SUCCEEDED(result)) {
michael@0 312 item = mRedoStack->Pop();
michael@0 313 mUndoStack->Push(item);
michael@0 314 }
michael@0 315
michael@0 316 nsresult result2 = aTxMgr->DidUndoNotify(t, result);
michael@0 317
michael@0 318 if (NS_SUCCEEDED(result)) {
michael@0 319 result = result2;
michael@0 320 }
michael@0 321 }
michael@0 322
michael@0 323 return result;
michael@0 324 }
michael@0 325
michael@0 326 nsresult
michael@0 327 nsTransactionItem::GetNumberOfUndoItems(int32_t *aNumItems)
michael@0 328 {
michael@0 329 NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
michael@0 330
michael@0 331 if (!mUndoStack) {
michael@0 332 *aNumItems = 0;
michael@0 333 return NS_OK;
michael@0 334 }
michael@0 335
michael@0 336 *aNumItems = mUndoStack->GetSize();
michael@0 337 return *aNumItems ? NS_OK : NS_ERROR_FAILURE;
michael@0 338 }
michael@0 339
michael@0 340 nsresult
michael@0 341 nsTransactionItem::GetNumberOfRedoItems(int32_t *aNumItems)
michael@0 342 {
michael@0 343 NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER);
michael@0 344
michael@0 345 if (!mRedoStack) {
michael@0 346 *aNumItems = 0;
michael@0 347 return NS_OK;
michael@0 348 }
michael@0 349
michael@0 350 *aNumItems = mRedoStack->GetSize();
michael@0 351 return *aNumItems ? NS_OK : NS_ERROR_FAILURE;
michael@0 352 }
michael@0 353
michael@0 354 nsresult
michael@0 355 nsTransactionItem::RecoverFromUndoError(nsTransactionManager *aTxMgr)
michael@0 356 {
michael@0 357 //
michael@0 358 // If this method gets called, we never got to the point where we
michael@0 359 // successfully called UndoTransaction() for the transaction item itself.
michael@0 360 // Just redo any children that successfully called undo!
michael@0 361 //
michael@0 362 return RedoChildren(aTxMgr);
michael@0 363 }
michael@0 364
michael@0 365 nsresult
michael@0 366 nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
michael@0 367 {
michael@0 368 //
michael@0 369 // If this method gets called, we already successfully called
michael@0 370 // RedoTransaction() for the transaction item itself. Undo all
michael@0 371 // the children that successfully called RedoTransaction(),
michael@0 372 // then undo the transaction item itself.
michael@0 373 //
michael@0 374
michael@0 375 nsresult result;
michael@0 376
michael@0 377 result = UndoChildren(aTxMgr);
michael@0 378
michael@0 379 if (NS_FAILED(result)) {
michael@0 380 return result;
michael@0 381 }
michael@0 382
michael@0 383 if (!mTransaction)
michael@0 384 return NS_OK;
michael@0 385
michael@0 386 return mTransaction->UndoTransaction();
michael@0 387 }
michael@0 388

mercurial