1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/txmgr/src/nsTransactionItem.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,388 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/mozalloc.h" 1.10 +#include "nsAutoPtr.h" 1.11 +#include "nsCOMPtr.h" 1.12 +#include "nsDebug.h" 1.13 +#include "nsError.h" 1.14 +#include "nsISupportsImpl.h" 1.15 +#include "nsITransaction.h" 1.16 +#include "nsTransactionItem.h" 1.17 +#include "nsTransactionManager.h" 1.18 +#include "nsTransactionStack.h" 1.19 + 1.20 +nsTransactionItem::nsTransactionItem(nsITransaction *aTransaction) 1.21 + : mTransaction(aTransaction), mUndoStack(0), mRedoStack(0) 1.22 +{ 1.23 +} 1.24 + 1.25 +nsTransactionItem::~nsTransactionItem() 1.26 +{ 1.27 + delete mRedoStack; 1.28 + 1.29 + delete mUndoStack; 1.30 +} 1.31 + 1.32 +void 1.33 +nsTransactionItem::CleanUp() 1.34 +{ 1.35 + mData.Clear(); 1.36 + mTransaction = nullptr; 1.37 + if (mRedoStack) { 1.38 + mRedoStack->DoUnlink(); 1.39 + } 1.40 + if (mUndoStack) { 1.41 + mUndoStack->DoUnlink(); 1.42 + } 1.43 +} 1.44 + 1.45 +NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(nsTransactionItem) 1.46 +NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(nsTransactionItem, 1.47 + CleanUp()) 1.48 + 1.49 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsTransactionItem) 1.50 + 1.51 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionItem) 1.52 + tmp->CleanUp(); 1.53 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.54 + 1.55 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionItem) 1.56 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData) 1.57 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) 1.58 + if (tmp->mRedoStack) { 1.59 + tmp->mRedoStack->DoTraverse(cb); 1.60 + } 1.61 + if (tmp->mUndoStack) { 1.62 + tmp->mUndoStack->DoTraverse(cb); 1.63 + } 1.64 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.65 + 1.66 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTransactionItem, AddRef) 1.67 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTransactionItem, Release) 1.68 + 1.69 +nsresult 1.70 +nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem) 1.71 +{ 1.72 + NS_ENSURE_TRUE(aTransactionItem, NS_ERROR_NULL_POINTER); 1.73 + 1.74 + if (!mUndoStack) { 1.75 + mUndoStack = new nsTransactionStack(nsTransactionStack::FOR_UNDO); 1.76 + } 1.77 + 1.78 + mUndoStack->Push(aTransactionItem); 1.79 + 1.80 + return NS_OK; 1.81 +} 1.82 + 1.83 +already_AddRefed<nsITransaction> 1.84 +nsTransactionItem::GetTransaction() 1.85 +{ 1.86 + nsCOMPtr<nsITransaction> txn = mTransaction; 1.87 + return txn.forget(); 1.88 +} 1.89 + 1.90 +nsresult 1.91 +nsTransactionItem::GetIsBatch(bool *aIsBatch) 1.92 +{ 1.93 + NS_ENSURE_TRUE(aIsBatch, NS_ERROR_NULL_POINTER); 1.94 + 1.95 + *aIsBatch = !mTransaction; 1.96 + 1.97 + return NS_OK; 1.98 +} 1.99 + 1.100 +nsresult 1.101 +nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren) 1.102 +{ 1.103 + nsresult result; 1.104 + 1.105 + NS_ENSURE_TRUE(aNumChildren, NS_ERROR_NULL_POINTER); 1.106 + 1.107 + *aNumChildren = 0; 1.108 + 1.109 + int32_t ui = 0; 1.110 + int32_t ri = 0; 1.111 + 1.112 + result = GetNumberOfUndoItems(&ui); 1.113 + 1.114 + NS_ENSURE_SUCCESS(result, result); 1.115 + 1.116 + result = GetNumberOfRedoItems(&ri); 1.117 + 1.118 + NS_ENSURE_SUCCESS(result, result); 1.119 + 1.120 + *aNumChildren = ui + ri; 1.121 + 1.122 + return NS_OK; 1.123 +} 1.124 + 1.125 +nsresult 1.126 +nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild) 1.127 +{ 1.128 + NS_ENSURE_TRUE(aChild, NS_ERROR_NULL_POINTER); 1.129 + 1.130 + *aChild = 0; 1.131 + 1.132 + int32_t numItems = 0; 1.133 + nsresult result = GetNumberOfChildren(&numItems); 1.134 + 1.135 + NS_ENSURE_SUCCESS(result, result); 1.136 + 1.137 + if (aIndex < 0 || aIndex >= numItems) 1.138 + return NS_ERROR_FAILURE; 1.139 + 1.140 + // Children are expected to be in the order they were added, 1.141 + // so the child first added would be at the bottom of the undo 1.142 + // stack, or if there are no items on the undo stack, it would 1.143 + // be at the top of the redo stack. 1.144 + 1.145 + result = GetNumberOfUndoItems(&numItems); 1.146 + 1.147 + NS_ENSURE_SUCCESS(result, result); 1.148 + 1.149 + if (numItems > 0 && aIndex < numItems) { 1.150 + NS_ENSURE_TRUE(mUndoStack, NS_ERROR_FAILURE); 1.151 + 1.152 + nsRefPtr<nsTransactionItem> child = mUndoStack->GetItem(aIndex); 1.153 + child.forget(aChild); 1.154 + return *aChild ? NS_OK : NS_ERROR_FAILURE; 1.155 + } 1.156 + 1.157 + // Adjust the index for the redo stack: 1.158 + 1.159 + aIndex -= numItems; 1.160 + 1.161 + result = GetNumberOfRedoItems(&numItems); 1.162 + 1.163 + NS_ENSURE_SUCCESS(result, result); 1.164 + 1.165 + NS_ENSURE_TRUE(mRedoStack && numItems != 0 && aIndex < numItems, NS_ERROR_FAILURE); 1.166 + 1.167 + nsRefPtr<nsTransactionItem> child = mRedoStack->GetItem(aIndex); 1.168 + child.forget(aChild); 1.169 + return *aChild ? NS_OK : NS_ERROR_FAILURE; 1.170 +} 1.171 + 1.172 +nsresult 1.173 +nsTransactionItem::DoTransaction() 1.174 +{ 1.175 + if (mTransaction) 1.176 + return mTransaction->DoTransaction(); 1.177 + return NS_OK; 1.178 +} 1.179 + 1.180 +nsresult 1.181 +nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr) 1.182 +{ 1.183 + nsresult result = UndoChildren(aTxMgr); 1.184 + 1.185 + if (NS_FAILED(result)) { 1.186 + RecoverFromUndoError(aTxMgr); 1.187 + return result; 1.188 + } 1.189 + 1.190 + if (!mTransaction) 1.191 + return NS_OK; 1.192 + 1.193 + result = mTransaction->UndoTransaction(); 1.194 + 1.195 + if (NS_FAILED(result)) { 1.196 + RecoverFromUndoError(aTxMgr); 1.197 + return result; 1.198 + } 1.199 + 1.200 + return NS_OK; 1.201 +} 1.202 + 1.203 +nsresult 1.204 +nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr) 1.205 +{ 1.206 + nsRefPtr<nsTransactionItem> item; 1.207 + nsresult result = NS_OK; 1.208 + int32_t sz = 0; 1.209 + 1.210 + if (mUndoStack) { 1.211 + if (!mRedoStack && mUndoStack) { 1.212 + mRedoStack = new nsTransactionStack(nsTransactionStack::FOR_REDO); 1.213 + } 1.214 + 1.215 + /* Undo all of the transaction items children! */ 1.216 + sz = mUndoStack->GetSize(); 1.217 + 1.218 + while (sz-- > 0) { 1.219 + item = mUndoStack->Peek(); 1.220 + 1.221 + if (!item) { 1.222 + return NS_ERROR_FAILURE; 1.223 + } 1.224 + 1.225 + nsCOMPtr<nsITransaction> t = item->GetTransaction(); 1.226 + 1.227 + bool doInterrupt = false; 1.228 + 1.229 + result = aTxMgr->WillUndoNotify(t, &doInterrupt); 1.230 + 1.231 + if (NS_FAILED(result)) { 1.232 + return result; 1.233 + } 1.234 + 1.235 + if (doInterrupt) { 1.236 + return NS_OK; 1.237 + } 1.238 + 1.239 + result = item->UndoTransaction(aTxMgr); 1.240 + 1.241 + if (NS_SUCCEEDED(result)) { 1.242 + item = mUndoStack->Pop(); 1.243 + mRedoStack->Push(item); 1.244 + } 1.245 + 1.246 + nsresult result2 = aTxMgr->DidUndoNotify(t, result); 1.247 + 1.248 + if (NS_SUCCEEDED(result)) { 1.249 + result = result2; 1.250 + } 1.251 + } 1.252 + } 1.253 + 1.254 + return result; 1.255 +} 1.256 + 1.257 +nsresult 1.258 +nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr) 1.259 +{ 1.260 + nsresult result; 1.261 + 1.262 + nsCOMPtr<nsITransaction> kungfuDeathGrip(mTransaction); 1.263 + if (mTransaction) { 1.264 + result = mTransaction->RedoTransaction(); 1.265 + 1.266 + NS_ENSURE_SUCCESS(result, result); 1.267 + } 1.268 + 1.269 + result = RedoChildren(aTxMgr); 1.270 + 1.271 + if (NS_FAILED(result)) { 1.272 + RecoverFromRedoError(aTxMgr); 1.273 + return result; 1.274 + } 1.275 + 1.276 + return NS_OK; 1.277 +} 1.278 + 1.279 +nsresult 1.280 +nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr) 1.281 +{ 1.282 + nsRefPtr<nsTransactionItem> item; 1.283 + nsresult result = NS_OK; 1.284 + 1.285 + if (!mRedoStack) 1.286 + return NS_OK; 1.287 + 1.288 + /* Redo all of the transaction items children! */ 1.289 + int32_t sz = mRedoStack->GetSize(); 1.290 + 1.291 + while (sz-- > 0) { 1.292 + item = mRedoStack->Peek(); 1.293 + 1.294 + if (!item) { 1.295 + return NS_ERROR_FAILURE; 1.296 + } 1.297 + 1.298 + nsCOMPtr<nsITransaction> t = item->GetTransaction(); 1.299 + 1.300 + bool doInterrupt = false; 1.301 + 1.302 + result = aTxMgr->WillRedoNotify(t, &doInterrupt); 1.303 + 1.304 + if (NS_FAILED(result)) { 1.305 + return result; 1.306 + } 1.307 + 1.308 + if (doInterrupt) { 1.309 + return NS_OK; 1.310 + } 1.311 + 1.312 + result = item->RedoTransaction(aTxMgr); 1.313 + 1.314 + if (NS_SUCCEEDED(result)) { 1.315 + item = mRedoStack->Pop(); 1.316 + mUndoStack->Push(item); 1.317 + } 1.318 + 1.319 + nsresult result2 = aTxMgr->DidUndoNotify(t, result); 1.320 + 1.321 + if (NS_SUCCEEDED(result)) { 1.322 + result = result2; 1.323 + } 1.324 + } 1.325 + 1.326 + return result; 1.327 +} 1.328 + 1.329 +nsresult 1.330 +nsTransactionItem::GetNumberOfUndoItems(int32_t *aNumItems) 1.331 +{ 1.332 + NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER); 1.333 + 1.334 + if (!mUndoStack) { 1.335 + *aNumItems = 0; 1.336 + return NS_OK; 1.337 + } 1.338 + 1.339 + *aNumItems = mUndoStack->GetSize(); 1.340 + return *aNumItems ? NS_OK : NS_ERROR_FAILURE; 1.341 +} 1.342 + 1.343 +nsresult 1.344 +nsTransactionItem::GetNumberOfRedoItems(int32_t *aNumItems) 1.345 +{ 1.346 + NS_ENSURE_TRUE(aNumItems, NS_ERROR_NULL_POINTER); 1.347 + 1.348 + if (!mRedoStack) { 1.349 + *aNumItems = 0; 1.350 + return NS_OK; 1.351 + } 1.352 + 1.353 + *aNumItems = mRedoStack->GetSize(); 1.354 + return *aNumItems ? NS_OK : NS_ERROR_FAILURE; 1.355 +} 1.356 + 1.357 +nsresult 1.358 +nsTransactionItem::RecoverFromUndoError(nsTransactionManager *aTxMgr) 1.359 +{ 1.360 + // 1.361 + // If this method gets called, we never got to the point where we 1.362 + // successfully called UndoTransaction() for the transaction item itself. 1.363 + // Just redo any children that successfully called undo! 1.364 + // 1.365 + return RedoChildren(aTxMgr); 1.366 +} 1.367 + 1.368 +nsresult 1.369 +nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr) 1.370 +{ 1.371 + // 1.372 + // If this method gets called, we already successfully called 1.373 + // RedoTransaction() for the transaction item itself. Undo all 1.374 + // the children that successfully called RedoTransaction(), 1.375 + // then undo the transaction item itself. 1.376 + // 1.377 + 1.378 + nsresult result; 1.379 + 1.380 + result = UndoChildren(aTxMgr); 1.381 + 1.382 + if (NS_FAILED(result)) { 1.383 + return result; 1.384 + } 1.385 + 1.386 + if (!mTransaction) 1.387 + return NS_OK; 1.388 + 1.389 + return mTransaction->UndoTransaction(); 1.390 +} 1.391 +