1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/IDBTransaction.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1275 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "base/basictypes.h" 1.11 + 1.12 +#include "IDBTransaction.h" 1.13 + 1.14 +#include "nsIAppShell.h" 1.15 +#include "nsIScriptContext.h" 1.16 + 1.17 +#include "mozilla/dom/quota/QuotaManager.h" 1.18 +#include "mozilla/storage.h" 1.19 +#include "nsDOMClassInfoID.h" 1.20 +#include "mozilla/dom/DOMStringList.h" 1.21 +#include "mozilla/EventDispatcher.h" 1.22 +#include "nsPIDOMWindow.h" 1.23 +#include "nsProxyRelease.h" 1.24 +#include "nsThreadUtils.h" 1.25 +#include "nsWidgetsCID.h" 1.26 + 1.27 +#include "AsyncConnectionHelper.h" 1.28 +#include "DatabaseInfo.h" 1.29 +#include "IDBCursor.h" 1.30 +#include "IDBEvents.h" 1.31 +#include "IDBFactory.h" 1.32 +#include "IDBObjectStore.h" 1.33 +#include "IndexedDatabaseManager.h" 1.34 +#include "ProfilerHelpers.h" 1.35 +#include "ReportInternalError.h" 1.36 +#include "TransactionThreadPool.h" 1.37 + 1.38 +#include "ipc/IndexedDBChild.h" 1.39 + 1.40 +#define SAVEPOINT_NAME "savepoint" 1.41 + 1.42 +using namespace mozilla; 1.43 +using namespace mozilla::dom; 1.44 +USING_INDEXEDDB_NAMESPACE 1.45 +using mozilla::dom::quota::QuotaManager; 1.46 +using mozilla::ErrorResult; 1.47 + 1.48 +namespace { 1.49 + 1.50 +NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); 1.51 + 1.52 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.53 +uint64_t gNextTransactionSerialNumber = 1; 1.54 +#endif 1.55 + 1.56 +PLDHashOperator 1.57 +DoomCachedStatements(const nsACString& aQuery, 1.58 + nsCOMPtr<mozIStorageStatement>& aStatement, 1.59 + void* aUserArg) 1.60 +{ 1.61 + CommitHelper* helper = static_cast<CommitHelper*>(aUserArg); 1.62 + helper->AddDoomedObject(aStatement); 1.63 + return PL_DHASH_REMOVE; 1.64 +} 1.65 + 1.66 +// This runnable doesn't actually do anything beyond "prime the pump" and get 1.67 +// transactions in the right order on the transaction thread pool. 1.68 +class StartTransactionRunnable : public nsIRunnable 1.69 +{ 1.70 +public: 1.71 + NS_DECL_ISUPPORTS 1.72 + 1.73 + NS_IMETHOD Run() 1.74 + { 1.75 + // NOP 1.76 + return NS_OK; 1.77 + } 1.78 +}; 1.79 + 1.80 +// Could really use those NS_REFCOUNTING_HAHA_YEAH_RIGHT macros here. 1.81 +NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::AddRef() 1.82 +{ 1.83 + return 2; 1.84 +} 1.85 + 1.86 +NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::Release() 1.87 +{ 1.88 + return 1; 1.89 +} 1.90 + 1.91 +NS_IMPL_QUERY_INTERFACE(StartTransactionRunnable, nsIRunnable) 1.92 + 1.93 +} // anonymous namespace 1.94 + 1.95 + 1.96 +// static 1.97 +already_AddRefed<IDBTransaction> 1.98 +IDBTransaction::CreateInternal(IDBDatabase* aDatabase, 1.99 + const Sequence<nsString>& aObjectStoreNames, 1.100 + Mode aMode, 1.101 + bool aDispatchDelayed, 1.102 + bool aIsVersionChangeTransactionChild) 1.103 +{ 1.104 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.105 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess() || !aDispatchDelayed, 1.106 + "No support for delayed-dispatch transactions in child " 1.107 + "process!"); 1.108 + NS_ASSERTION(!aIsVersionChangeTransactionChild || 1.109 + (!IndexedDatabaseManager::IsMainProcess() && 1.110 + aMode == IDBTransaction::VERSION_CHANGE), 1.111 + "Busted logic!"); 1.112 + 1.113 + nsRefPtr<IDBTransaction> transaction = new IDBTransaction(aDatabase); 1.114 + 1.115 + transaction->SetScriptOwner(aDatabase->GetScriptOwner()); 1.116 + transaction->mDatabase = aDatabase; 1.117 + transaction->mMode = aMode; 1.118 + transaction->mDatabaseInfo = aDatabase->Info(); 1.119 + transaction->mObjectStoreNames.AppendElements(aObjectStoreNames); 1.120 + transaction->mObjectStoreNames.Sort(); 1.121 + 1.122 + IndexedDBTransactionChild* actor = nullptr; 1.123 + 1.124 + if (IndexedDatabaseManager::IsMainProcess()) { 1.125 + if (aMode != IDBTransaction::VERSION_CHANGE) { 1.126 + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); 1.127 + NS_ENSURE_TRUE(pool, nullptr); 1.128 + 1.129 + static StartTransactionRunnable sStartTransactionRunnable; 1.130 + pool->Dispatch(transaction, &sStartTransactionRunnable, false, nullptr); 1.131 + } 1.132 + } 1.133 + else if (!aIsVersionChangeTransactionChild) { 1.134 + IndexedDBDatabaseChild* dbActor = aDatabase->GetActorChild(); 1.135 + NS_ASSERTION(dbActor, "Must have an actor here!"); 1.136 + 1.137 + ipc::NormalTransactionParams params; 1.138 + params.names().AppendElements(aObjectStoreNames); 1.139 + params.mode() = aMode; 1.140 + 1.141 + actor = new IndexedDBTransactionChild(); 1.142 + 1.143 + dbActor->SendPIndexedDBTransactionConstructor(actor, params); 1.144 + } 1.145 + 1.146 + if (!aDispatchDelayed) { 1.147 + nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); 1.148 + NS_ENSURE_TRUE(appShell, nullptr); 1.149 + 1.150 + nsresult rv = appShell->RunBeforeNextEvent(transaction); 1.151 + NS_ENSURE_SUCCESS(rv, nullptr); 1.152 + 1.153 + transaction->mCreating = true; 1.154 + } 1.155 + 1.156 + if (actor) { 1.157 + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.158 + actor->SetTransaction(transaction); 1.159 + } 1.160 + 1.161 + return transaction.forget(); 1.162 +} 1.163 + 1.164 +IDBTransaction::IDBTransaction(IDBDatabase* aDatabase) 1.165 +: IDBWrapperCache(aDatabase), 1.166 + mReadyState(IDBTransaction::INITIAL), 1.167 + mMode(IDBTransaction::READ_ONLY), 1.168 + mPendingRequests(0), 1.169 + mSavepointCount(0), 1.170 + mActorChild(nullptr), 1.171 + mActorParent(nullptr), 1.172 + mAbortCode(NS_OK), 1.173 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.174 + mSerialNumber(gNextTransactionSerialNumber++), 1.175 +#endif 1.176 + mCreating(false) 1.177 +#ifdef DEBUG 1.178 + , mFiredCompleteOrAbort(false) 1.179 +#endif 1.180 +{ 1.181 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.182 +} 1.183 + 1.184 +IDBTransaction::~IDBTransaction() 1.185 +{ 1.186 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.187 + NS_ASSERTION(!mPendingRequests, "Should have no pending requests here!"); 1.188 + NS_ASSERTION(!mSavepointCount, "Should have released them all!"); 1.189 + NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!"); 1.190 + NS_ASSERTION(!mCreating, "Should have been cleared already!"); 1.191 + NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!"); 1.192 + 1.193 + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); 1.194 + if (mActorChild) { 1.195 + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.196 + mActorChild->Send__delete__(mActorChild); 1.197 + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); 1.198 + } 1.199 +} 1.200 + 1.201 +void 1.202 +IDBTransaction::OnNewRequest() 1.203 +{ 1.204 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.205 + if (!mPendingRequests) { 1.206 + NS_ASSERTION(mReadyState == IDBTransaction::INITIAL, 1.207 + "Reusing a transaction!"); 1.208 + mReadyState = IDBTransaction::LOADING; 1.209 + } 1.210 + ++mPendingRequests; 1.211 +} 1.212 + 1.213 +void 1.214 +IDBTransaction::OnRequestFinished() 1.215 +{ 1.216 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.217 + NS_ASSERTION(mPendingRequests, "Mismatched calls!"); 1.218 + --mPendingRequests; 1.219 + if (!mPendingRequests) { 1.220 + NS_ASSERTION(NS_FAILED(mAbortCode) || mReadyState == IDBTransaction::LOADING, 1.221 + "Bad state!"); 1.222 + mReadyState = IDBTransaction::COMMITTING; 1.223 + CommitOrRollback(); 1.224 + } 1.225 +} 1.226 + 1.227 +void 1.228 +IDBTransaction::OnRequestDisconnected() 1.229 +{ 1.230 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.231 + NS_ASSERTION(mPendingRequests, "Mismatched calls!"); 1.232 + --mPendingRequests; 1.233 +} 1.234 + 1.235 +void 1.236 +IDBTransaction::RemoveObjectStore(const nsAString& aName) 1.237 +{ 1.238 + NS_ASSERTION(mMode == IDBTransaction::VERSION_CHANGE, 1.239 + "Only remove object stores on VERSION_CHANGE transactions"); 1.240 + 1.241 + mDatabaseInfo->RemoveObjectStore(aName); 1.242 + 1.243 + for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) { 1.244 + if (mCreatedObjectStores[i]->Name() == aName) { 1.245 + nsRefPtr<IDBObjectStore> objectStore = mCreatedObjectStores[i]; 1.246 + mCreatedObjectStores.RemoveElementAt(i); 1.247 + mDeletedObjectStores.AppendElement(objectStore); 1.248 + break; 1.249 + } 1.250 + } 1.251 +} 1.252 + 1.253 +void 1.254 +IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener) 1.255 +{ 1.256 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.257 + NS_ASSERTION(!mListener, "Shouldn't already have a listener!"); 1.258 + mListener = aListener; 1.259 +} 1.260 + 1.261 +nsresult 1.262 +IDBTransaction::CommitOrRollback() 1.263 +{ 1.264 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.265 + 1.266 + if (!IndexedDatabaseManager::IsMainProcess()) { 1.267 + if (mActorChild) { 1.268 + mActorChild->SendAllRequestsFinished(); 1.269 + } 1.270 + 1.271 + return NS_OK; 1.272 + } 1.273 + 1.274 + nsRefPtr<CommitHelper> helper = 1.275 + new CommitHelper(this, mListener, mCreatedObjectStores); 1.276 + 1.277 + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); 1.278 + NS_ENSURE_STATE(pool); 1.279 + 1.280 + mCachedStatements.Enumerate(DoomCachedStatements, helper); 1.281 + NS_ASSERTION(!mCachedStatements.Count(), "Statements left!"); 1.282 + 1.283 + nsresult rv = pool->Dispatch(this, helper, true, helper); 1.284 + NS_ENSURE_SUCCESS(rv, rv); 1.285 + 1.286 + return NS_OK; 1.287 +} 1.288 + 1.289 +bool 1.290 +IDBTransaction::StartSavepoint() 1.291 +{ 1.292 + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); 1.293 + NS_PRECONDITION(mConnection, "No connection!"); 1.294 + 1.295 + nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING( 1.296 + "SAVEPOINT " SAVEPOINT_NAME 1.297 + )); 1.298 + NS_ENSURE_TRUE(stmt, false); 1.299 + 1.300 + mozStorageStatementScoper scoper(stmt); 1.301 + 1.302 + nsresult rv = stmt->Execute(); 1.303 + NS_ENSURE_SUCCESS(rv, false); 1.304 + 1.305 + if (IsWriteAllowed()) { 1.306 + mUpdateFileRefcountFunction->StartSavepoint(); 1.307 + } 1.308 + 1.309 + ++mSavepointCount; 1.310 + 1.311 + return true; 1.312 +} 1.313 + 1.314 +nsresult 1.315 +IDBTransaction::ReleaseSavepoint() 1.316 +{ 1.317 + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); 1.318 + NS_PRECONDITION(mConnection, "No connection!"); 1.319 + 1.320 + NS_ASSERTION(mSavepointCount, "Mismatch!"); 1.321 + 1.322 + nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING( 1.323 + "RELEASE SAVEPOINT " SAVEPOINT_NAME 1.324 + )); 1.325 + NS_ENSURE_TRUE(stmt, NS_OK); 1.326 + 1.327 + mozStorageStatementScoper scoper(stmt); 1.328 + 1.329 + nsresult rv = stmt->Execute(); 1.330 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.331 + 1.332 + if (IsWriteAllowed()) { 1.333 + mUpdateFileRefcountFunction->ReleaseSavepoint(); 1.334 + } 1.335 + 1.336 + --mSavepointCount; 1.337 + 1.338 + return NS_OK; 1.339 +} 1.340 + 1.341 +void 1.342 +IDBTransaction::RollbackSavepoint() 1.343 +{ 1.344 + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); 1.345 + NS_PRECONDITION(mConnection, "No connection!"); 1.346 + 1.347 + NS_ASSERTION(mSavepointCount == 1, "Mismatch!"); 1.348 + mSavepointCount = 0; 1.349 + 1.350 + nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING( 1.351 + "ROLLBACK TO SAVEPOINT " SAVEPOINT_NAME 1.352 + )); 1.353 + NS_ENSURE_TRUE_VOID(stmt); 1.354 + 1.355 + mozStorageStatementScoper scoper(stmt); 1.356 + 1.357 + nsresult rv = stmt->Execute(); 1.358 + NS_ENSURE_SUCCESS_VOID(rv); 1.359 + 1.360 + if (IsWriteAllowed()) { 1.361 + mUpdateFileRefcountFunction->RollbackSavepoint(); 1.362 + } 1.363 +} 1.364 + 1.365 +nsresult 1.366 +IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult) 1.367 +{ 1.368 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.369 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.370 + 1.371 + PROFILER_LABEL("IndexedDB", "IDBTransaction::GetOrCreateConnection"); 1.372 + 1.373 + if (mDatabase->IsInvalidated()) { 1.374 + return NS_ERROR_NOT_AVAILABLE; 1.375 + } 1.376 + 1.377 + if (!mConnection) { 1.378 + nsCOMPtr<mozIStorageConnection> connection = 1.379 + IDBFactory::GetConnection(mDatabase->FilePath(), mDatabase->Type(), 1.380 + mDatabase->Group(), mDatabase->Origin()); 1.381 + NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE); 1.382 + 1.383 + nsresult rv; 1.384 + 1.385 + nsRefPtr<UpdateRefcountFunction> function; 1.386 + nsCString beginTransaction; 1.387 + if (mMode != IDBTransaction::READ_ONLY) { 1.388 + function = new UpdateRefcountFunction(Database()->Manager()); 1.389 + NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY); 1.390 + 1.391 + rv = connection->CreateFunction( 1.392 + NS_LITERAL_CSTRING("update_refcount"), 2, function); 1.393 + NS_ENSURE_SUCCESS(rv, rv); 1.394 + 1.395 + beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;"); 1.396 + } 1.397 + else { 1.398 + beginTransaction.AssignLiteral("BEGIN TRANSACTION;"); 1.399 + } 1.400 + 1.401 + nsCOMPtr<mozIStorageStatement> stmt; 1.402 + rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt)); 1.403 + NS_ENSURE_SUCCESS(rv, rv); 1.404 + 1.405 + rv = stmt->Execute(); 1.406 + NS_ENSURE_SUCCESS(rv, rv); 1.407 + 1.408 + function.swap(mUpdateFileRefcountFunction); 1.409 + connection.swap(mConnection); 1.410 + } 1.411 + 1.412 + nsCOMPtr<mozIStorageConnection> result(mConnection); 1.413 + result.forget(aResult); 1.414 + return NS_OK; 1.415 +} 1.416 + 1.417 +already_AddRefed<mozIStorageStatement> 1.418 +IDBTransaction::GetCachedStatement(const nsACString& aQuery) 1.419 +{ 1.420 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.421 + NS_ASSERTION(!aQuery.IsEmpty(), "Empty sql statement!"); 1.422 + NS_ASSERTION(mConnection, "No connection!"); 1.423 + 1.424 + nsCOMPtr<mozIStorageStatement> stmt; 1.425 + 1.426 + if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) { 1.427 + nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt)); 1.428 +#ifdef DEBUG 1.429 + if (NS_FAILED(rv)) { 1.430 + nsCString error; 1.431 + error.AppendLiteral("The statement `"); 1.432 + error.Append(aQuery); 1.433 + error.AppendLiteral("` failed to compile with the error message `"); 1.434 + nsCString msg; 1.435 + (void)mConnection->GetLastErrorString(msg); 1.436 + error.Append(msg); 1.437 + error.AppendLiteral("`."); 1.438 + NS_ERROR(error.get()); 1.439 + } 1.440 +#endif 1.441 + NS_ENSURE_SUCCESS(rv, nullptr); 1.442 + 1.443 + mCachedStatements.Put(aQuery, stmt); 1.444 + } 1.445 + 1.446 + return stmt.forget(); 1.447 +} 1.448 + 1.449 +bool 1.450 +IDBTransaction::IsOpen() const 1.451 +{ 1.452 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.453 + 1.454 + // If we haven't started anything then we're open. 1.455 + if (mReadyState == IDBTransaction::INITIAL) { 1.456 + return true; 1.457 + } 1.458 + 1.459 + // If we've already started then we need to check to see if we still have the 1.460 + // mCreating flag set. If we do (i.e. we haven't returned to the event loop 1.461 + // from the time we were created) then we are open. Otherwise check the 1.462 + // currently running transaction to see if it's the same. We only allow other 1.463 + // requests to be made if this transaction is currently running. 1.464 + if (mReadyState == IDBTransaction::LOADING) { 1.465 + if (mCreating) { 1.466 + return true; 1.467 + } 1.468 + 1.469 + if (AsyncConnectionHelper::GetCurrentTransaction() == this) { 1.470 + return true; 1.471 + } 1.472 + } 1.473 + 1.474 + return false; 1.475 +} 1.476 + 1.477 +already_AddRefed<IDBObjectStore> 1.478 +IDBTransaction::GetOrCreateObjectStore(const nsAString& aName, 1.479 + ObjectStoreInfo* aObjectStoreInfo, 1.480 + bool aCreating) 1.481 +{ 1.482 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.483 + NS_ASSERTION(aObjectStoreInfo, "Null pointer!"); 1.484 + NS_ASSERTION(!aCreating || GetMode() == IDBTransaction::VERSION_CHANGE, 1.485 + "How else can we create here?!"); 1.486 + 1.487 + nsRefPtr<IDBObjectStore> retval; 1.488 + 1.489 + for (uint32_t index = 0; index < mCreatedObjectStores.Length(); index++) { 1.490 + nsRefPtr<IDBObjectStore>& objectStore = mCreatedObjectStores[index]; 1.491 + if (objectStore->Name() == aName) { 1.492 + retval = objectStore; 1.493 + return retval.forget(); 1.494 + } 1.495 + } 1.496 + 1.497 + retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id, 1.498 + aCreating); 1.499 + 1.500 + mCreatedObjectStores.AppendElement(retval); 1.501 + 1.502 + return retval.forget(); 1.503 +} 1.504 + 1.505 +already_AddRefed<FileInfo> 1.506 +IDBTransaction::GetFileInfo(nsIDOMBlob* aBlob) 1.507 +{ 1.508 + nsRefPtr<FileInfo> fileInfo; 1.509 + mCreatedFileInfos.Get(aBlob, getter_AddRefs(fileInfo)); 1.510 + return fileInfo.forget(); 1.511 +} 1.512 + 1.513 +void 1.514 +IDBTransaction::AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo) 1.515 +{ 1.516 + mCreatedFileInfos.Put(aBlob, aFileInfo); 1.517 +} 1.518 + 1.519 +void 1.520 +IDBTransaction::ClearCreatedFileInfos() 1.521 +{ 1.522 + mCreatedFileInfos.Clear(); 1.523 +} 1.524 + 1.525 +nsresult 1.526 +IDBTransaction::AbortInternal(nsresult aAbortCode, 1.527 + already_AddRefed<DOMError> aError) 1.528 +{ 1.529 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.530 + 1.531 + nsRefPtr<DOMError> error = aError; 1.532 + 1.533 + if (IsFinished()) { 1.534 + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; 1.535 + } 1.536 + 1.537 + if (mActorChild) { 1.538 + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.539 + mActorChild->SendAbort(aAbortCode); 1.540 + } 1.541 + 1.542 + bool needToCommitOrRollback = mReadyState == IDBTransaction::INITIAL; 1.543 + 1.544 + mAbortCode = aAbortCode; 1.545 + mReadyState = IDBTransaction::DONE; 1.546 + mError = error.forget(); 1.547 + 1.548 + if (GetMode() == IDBTransaction::VERSION_CHANGE) { 1.549 + // If a version change transaction is aborted, we must revert the world 1.550 + // back to its previous state. 1.551 + mDatabase->RevertToPreviousState(); 1.552 + 1.553 + DatabaseInfo* dbInfo = mDatabase->Info(); 1.554 + 1.555 + for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) { 1.556 + nsRefPtr<IDBObjectStore>& objectStore = mCreatedObjectStores[i]; 1.557 + ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); 1.558 + 1.559 + if (!info) { 1.560 + info = new ObjectStoreInfo(*objectStore->Info()); 1.561 + info->indexes.Clear(); 1.562 + } 1.563 + 1.564 + objectStore->SetInfo(info); 1.565 + } 1.566 + 1.567 + for (uint32_t i = 0; i < mDeletedObjectStores.Length(); i++) { 1.568 + nsRefPtr<IDBObjectStore>& objectStore = mDeletedObjectStores[i]; 1.569 + ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); 1.570 + 1.571 + if (!info) { 1.572 + info = new ObjectStoreInfo(*objectStore->Info()); 1.573 + info->indexes.Clear(); 1.574 + } 1.575 + 1.576 + objectStore->SetInfo(info); 1.577 + } 1.578 + 1.579 + // and then the db must be closed 1.580 + mDatabase->Close(); 1.581 + } 1.582 + 1.583 + // Fire the abort event if there are no outstanding requests. Otherwise the 1.584 + // abort event will be fired when all outstanding requests finish. 1.585 + if (needToCommitOrRollback) { 1.586 + return CommitOrRollback(); 1.587 + } 1.588 + 1.589 + return NS_OK; 1.590 +} 1.591 + 1.592 +nsresult 1.593 +IDBTransaction::Abort(IDBRequest* aRequest) 1.594 +{ 1.595 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.596 + NS_ASSERTION(aRequest, "This is undesirable."); 1.597 + 1.598 + ErrorResult rv; 1.599 + nsRefPtr<DOMError> error = aRequest->GetError(rv); 1.600 + 1.601 + return AbortInternal(aRequest->GetErrorCode(), error.forget()); 1.602 +} 1.603 + 1.604 +nsresult 1.605 +IDBTransaction::Abort(nsresult aErrorCode) 1.606 +{ 1.607 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.608 + 1.609 + nsRefPtr<DOMError> error = new DOMError(GetOwner(), aErrorCode); 1.610 + return AbortInternal(aErrorCode, error.forget()); 1.611 +} 1.612 + 1.613 +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction) 1.614 + 1.615 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction, 1.616 + IDBWrapperCache) 1.617 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase) 1.618 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) 1.619 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCreatedObjectStores) 1.620 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores) 1.621 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.622 + 1.623 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) 1.624 + // Don't unlink mDatabase! 1.625 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) 1.626 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCreatedObjectStores) 1.627 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores) 1.628 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.629 + 1.630 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) 1.631 + NS_INTERFACE_MAP_ENTRY(nsIRunnable) 1.632 +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) 1.633 + 1.634 +NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) 1.635 +NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) 1.636 + 1.637 +JSObject* 1.638 +IDBTransaction::WrapObject(JSContext* aCx) 1.639 +{ 1.640 + return IDBTransactionBinding::Wrap(aCx, this); 1.641 +} 1.642 + 1.643 +mozilla::dom::IDBTransactionMode 1.644 +IDBTransaction::GetMode(ErrorResult& aRv) const 1.645 +{ 1.646 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.647 + 1.648 + switch (mMode) { 1.649 + case READ_ONLY: 1.650 + return mozilla::dom::IDBTransactionMode::Readonly; 1.651 + 1.652 + case READ_WRITE: 1.653 + return mozilla::dom::IDBTransactionMode::Readwrite; 1.654 + 1.655 + case VERSION_CHANGE: 1.656 + return mozilla::dom::IDBTransactionMode::Versionchange; 1.657 + 1.658 + case MODE_INVALID: 1.659 + default: 1.660 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.661 + return mozilla::dom::IDBTransactionMode::Readonly; 1.662 + } 1.663 +} 1.664 + 1.665 +DOMError* 1.666 +IDBTransaction::GetError(ErrorResult& aRv) 1.667 +{ 1.668 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.669 + 1.670 + if (IsOpen()) { 1.671 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.672 + return nullptr; 1.673 + } 1.674 + 1.675 + return mError; 1.676 +} 1.677 + 1.678 +already_AddRefed<DOMStringList> 1.679 +IDBTransaction::GetObjectStoreNames(ErrorResult& aRv) 1.680 +{ 1.681 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.682 + 1.683 + nsRefPtr<DOMStringList> list(new DOMStringList()); 1.684 + 1.685 + if (mMode == IDBTransaction::VERSION_CHANGE) { 1.686 + mDatabaseInfo->GetObjectStoreNames(list->StringArray()); 1.687 + } 1.688 + else { 1.689 + list->StringArray() = mObjectStoreNames; 1.690 + } 1.691 + 1.692 + return list.forget(); 1.693 +} 1.694 + 1.695 +already_AddRefed<IDBObjectStore> 1.696 +IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv) 1.697 +{ 1.698 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.699 + 1.700 + if (IsFinished()) { 1.701 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); 1.702 + return nullptr; 1.703 + } 1.704 + 1.705 + ObjectStoreInfo* info = nullptr; 1.706 + 1.707 + if (mMode == IDBTransaction::VERSION_CHANGE || 1.708 + mObjectStoreNames.Contains(aName)) { 1.709 + info = mDatabaseInfo->GetObjectStore(aName); 1.710 + } 1.711 + 1.712 + if (!info) { 1.713 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); 1.714 + return nullptr; 1.715 + } 1.716 + 1.717 + nsRefPtr<IDBObjectStore> objectStore = 1.718 + GetOrCreateObjectStore(aName, info, false); 1.719 + if (!objectStore) { 1.720 + IDB_WARNING("Failed to get or create object store!"); 1.721 + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.722 + return nullptr; 1.723 + } 1.724 + 1.725 + return objectStore.forget(); 1.726 +} 1.727 + 1.728 +nsresult 1.729 +IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) 1.730 +{ 1.731 + aVisitor.mCanHandle = true; 1.732 + aVisitor.mParentTarget = mDatabase; 1.733 + return NS_OK; 1.734 +} 1.735 + 1.736 +NS_IMETHODIMP 1.737 +IDBTransaction::Run() 1.738 +{ 1.739 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.740 + 1.741 + // We're back at the event loop, no longer newborn. 1.742 + mCreating = false; 1.743 + 1.744 + // Maybe set the readyState to DONE if there were no requests generated. 1.745 + if (mReadyState == IDBTransaction::INITIAL) { 1.746 + mReadyState = IDBTransaction::DONE; 1.747 + 1.748 + if (NS_FAILED(CommitOrRollback())) { 1.749 + NS_WARNING("Failed to commit!"); 1.750 + } 1.751 + } 1.752 + 1.753 + return NS_OK; 1.754 +} 1.755 + 1.756 +CommitHelper::CommitHelper( 1.757 + IDBTransaction* aTransaction, 1.758 + IDBTransactionListener* aListener, 1.759 + const nsTArray<nsRefPtr<IDBObjectStore> >& aUpdatedObjectStores) 1.760 +: mTransaction(aTransaction), 1.761 + mListener(aListener), 1.762 + mAbortCode(aTransaction->mAbortCode) 1.763 +{ 1.764 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.765 + 1.766 + mConnection.swap(aTransaction->mConnection); 1.767 + mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction); 1.768 + 1.769 + for (uint32_t i = 0; i < aUpdatedObjectStores.Length(); i++) { 1.770 + ObjectStoreInfo* info = aUpdatedObjectStores[i]->Info(); 1.771 + if (info->comittedAutoIncrementId != info->nextAutoIncrementId) { 1.772 + mAutoIncrementObjectStores.AppendElement(aUpdatedObjectStores[i]); 1.773 + } 1.774 + } 1.775 +} 1.776 + 1.777 +CommitHelper::CommitHelper(IDBTransaction* aTransaction, 1.778 + nsresult aAbortCode) 1.779 +: mTransaction(aTransaction), 1.780 + mAbortCode(aAbortCode) 1.781 +{ 1.782 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.783 +} 1.784 + 1.785 +CommitHelper::~CommitHelper() 1.786 +{ 1.787 +} 1.788 + 1.789 +NS_IMPL_ISUPPORTS(CommitHelper, nsIRunnable) 1.790 + 1.791 +NS_IMETHODIMP 1.792 +CommitHelper::Run() 1.793 +{ 1.794 + if (NS_IsMainThread()) { 1.795 + PROFILER_MAIN_THREAD_LABEL("IndexedDB", "CommitHelper::Run"); 1.796 + 1.797 + NS_ASSERTION(mDoomedObjects.IsEmpty(), "Didn't release doomed objects!"); 1.798 + 1.799 + mTransaction->mReadyState = IDBTransaction::DONE; 1.800 + 1.801 + // Release file infos on the main thread, so they will eventually get 1.802 + // destroyed on correct thread. 1.803 + mTransaction->ClearCreatedFileInfos(); 1.804 + if (mUpdateFileRefcountFunction) { 1.805 + mUpdateFileRefcountFunction->ClearFileInfoEntries(); 1.806 + mUpdateFileRefcountFunction = nullptr; 1.807 + } 1.808 + 1.809 + nsCOMPtr<nsIDOMEvent> event; 1.810 + if (NS_FAILED(mAbortCode)) { 1.811 + if (mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) { 1.812 + // This will make the database take a snapshot of it's DatabaseInfo 1.813 + mTransaction->Database()->Close(); 1.814 + // Then remove the info from the hash as it contains invalid data. 1.815 + DatabaseInfo::Remove(mTransaction->Database()->Id()); 1.816 + } 1.817 + 1.818 + event = CreateGenericEvent(mTransaction, 1.819 + NS_LITERAL_STRING(ABORT_EVT_STR), 1.820 + eDoesBubble, eNotCancelable); 1.821 + 1.822 + // The transaction may already have an error object (e.g. if one of the 1.823 + // requests failed). If it doesn't, and it wasn't aborted 1.824 + // programmatically, create one now. 1.825 + if (!mTransaction->mError && 1.826 + mAbortCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { 1.827 + mTransaction->mError = new DOMError(mTransaction->GetOwner(), mAbortCode); 1.828 + } 1.829 + } 1.830 + else { 1.831 + event = CreateGenericEvent(mTransaction, 1.832 + NS_LITERAL_STRING(COMPLETE_EVT_STR), 1.833 + eDoesNotBubble, eNotCancelable); 1.834 + } 1.835 + IDB_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 1.836 + 1.837 + if (mListener) { 1.838 + mListener->NotifyTransactionPreComplete(mTransaction); 1.839 + } 1.840 + 1.841 + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", 1.842 + "IDBTransaction[%llu] MT Complete", 1.843 + mTransaction->GetSerialNumber(), mAbortCode); 1.844 + 1.845 + bool dummy; 1.846 + if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) { 1.847 + NS_WARNING("Dispatch failed!"); 1.848 + } 1.849 + 1.850 +#ifdef DEBUG 1.851 + mTransaction->mFiredCompleteOrAbort = true; 1.852 +#endif 1.853 + 1.854 + if (mListener) { 1.855 + mListener->NotifyTransactionPostComplete(mTransaction); 1.856 + } 1.857 + 1.858 + mTransaction = nullptr; 1.859 + 1.860 + return NS_OK; 1.861 + } 1.862 + 1.863 + PROFILER_LABEL("IndexedDB", "CommitHelper::Run"); 1.864 + 1.865 + IDBDatabase* database = mTransaction->Database(); 1.866 + if (database->IsInvalidated()) { 1.867 + IDB_REPORT_INTERNAL_ERR(); 1.868 + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 1.869 + } 1.870 + 1.871 + if (mConnection) { 1.872 + QuotaManager::SetCurrentWindow(database->GetOwner()); 1.873 + 1.874 + if (NS_SUCCEEDED(mAbortCode) && mUpdateFileRefcountFunction && 1.875 + NS_FAILED(mUpdateFileRefcountFunction->WillCommit(mConnection))) { 1.876 + IDB_REPORT_INTERNAL_ERR(); 1.877 + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 1.878 + } 1.879 + 1.880 + if (NS_SUCCEEDED(mAbortCode) && NS_FAILED(WriteAutoIncrementCounts())) { 1.881 + IDB_REPORT_INTERNAL_ERR(); 1.882 + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 1.883 + } 1.884 + 1.885 + if (NS_SUCCEEDED(mAbortCode)) { 1.886 + NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION"); 1.887 + nsresult rv = mConnection->ExecuteSimpleSQL(release); 1.888 + if (NS_SUCCEEDED(rv)) { 1.889 + if (mUpdateFileRefcountFunction) { 1.890 + mUpdateFileRefcountFunction->DidCommit(); 1.891 + } 1.892 + CommitAutoIncrementCounts(); 1.893 + } 1.894 + else if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { 1.895 + // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, 1.896 + // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. 1.897 + mAbortCode = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; 1.898 + } 1.899 + else { 1.900 + IDB_REPORT_INTERNAL_ERR(); 1.901 + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 1.902 + } 1.903 + } 1.904 + 1.905 + if (NS_FAILED(mAbortCode)) { 1.906 + if (mUpdateFileRefcountFunction) { 1.907 + mUpdateFileRefcountFunction->DidAbort(); 1.908 + } 1.909 + RevertAutoIncrementCounts(); 1.910 + NS_NAMED_LITERAL_CSTRING(rollback, "ROLLBACK TRANSACTION"); 1.911 + if (NS_FAILED(mConnection->ExecuteSimpleSQL(rollback))) { 1.912 + NS_WARNING("Failed to rollback transaction!"); 1.913 + } 1.914 + } 1.915 + } 1.916 + 1.917 + mDoomedObjects.Clear(); 1.918 + 1.919 + if (mConnection) { 1.920 + if (mUpdateFileRefcountFunction) { 1.921 + nsresult rv = mConnection->RemoveFunction( 1.922 + NS_LITERAL_CSTRING("update_refcount")); 1.923 + if (NS_FAILED(rv)) { 1.924 + NS_WARNING("Failed to remove function!"); 1.925 + } 1.926 + } 1.927 + 1.928 + mConnection->Close(); 1.929 + mConnection = nullptr; 1.930 + 1.931 + QuotaManager::SetCurrentWindow(nullptr); 1.932 + } 1.933 + 1.934 + return NS_OK; 1.935 +} 1.936 + 1.937 +nsresult 1.938 +CommitHelper::WriteAutoIncrementCounts() 1.939 +{ 1.940 + nsCOMPtr<mozIStorageStatement> stmt; 1.941 + nsresult rv; 1.942 + for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { 1.943 + ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); 1.944 + if (!stmt) { 1.945 + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( 1.946 + "UPDATE object_store SET auto_increment = :ai " 1.947 + "WHERE id = :osid;"), getter_AddRefs(stmt)); 1.948 + NS_ENSURE_SUCCESS(rv, rv); 1.949 + } 1.950 + else { 1.951 + stmt->Reset(); 1.952 + } 1.953 + 1.954 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), info->id); 1.955 + NS_ENSURE_SUCCESS(rv, rv); 1.956 + 1.957 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("ai"), 1.958 + info->nextAutoIncrementId); 1.959 + NS_ENSURE_SUCCESS(rv, rv); 1.960 + 1.961 + rv = stmt->Execute(); 1.962 + NS_ENSURE_SUCCESS(rv, rv); 1.963 + } 1.964 + 1.965 + return NS_OK; 1.966 +} 1.967 + 1.968 +void 1.969 +CommitHelper::CommitAutoIncrementCounts() 1.970 +{ 1.971 + for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { 1.972 + ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); 1.973 + info->comittedAutoIncrementId = info->nextAutoIncrementId; 1.974 + } 1.975 +} 1.976 + 1.977 +void 1.978 +CommitHelper::RevertAutoIncrementCounts() 1.979 +{ 1.980 + for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { 1.981 + ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); 1.982 + info->nextAutoIncrementId = info->comittedAutoIncrementId; 1.983 + } 1.984 +} 1.985 + 1.986 +NS_IMPL_ISUPPORTS(UpdateRefcountFunction, mozIStorageFunction) 1.987 + 1.988 +NS_IMETHODIMP 1.989 +UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues, 1.990 + nsIVariant** _retval) 1.991 +{ 1.992 + *_retval = nullptr; 1.993 + 1.994 + uint32_t numEntries; 1.995 + nsresult rv = aValues->GetNumEntries(&numEntries); 1.996 + NS_ENSURE_SUCCESS(rv, rv); 1.997 + NS_ASSERTION(numEntries == 2, "unexpected number of arguments"); 1.998 + 1.999 +#ifdef DEBUG 1.1000 + int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL; 1.1001 + aValues->GetTypeOfIndex(0, &type1); 1.1002 + 1.1003 + int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL; 1.1004 + aValues->GetTypeOfIndex(1, &type2); 1.1005 + 1.1006 + NS_ASSERTION(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL && 1.1007 + type2 == mozIStorageValueArray::VALUE_TYPE_NULL), 1.1008 + "Shouldn't be called!"); 1.1009 +#endif 1.1010 + 1.1011 + rv = ProcessValue(aValues, 0, eDecrement); 1.1012 + NS_ENSURE_SUCCESS(rv, rv); 1.1013 + 1.1014 + rv = ProcessValue(aValues, 1, eIncrement); 1.1015 + NS_ENSURE_SUCCESS(rv, rv); 1.1016 + 1.1017 + return NS_OK; 1.1018 +} 1.1019 + 1.1020 +nsresult 1.1021 +UpdateRefcountFunction::WillCommit(mozIStorageConnection* aConnection) 1.1022 +{ 1.1023 + DatabaseUpdateFunction function(aConnection, this); 1.1024 + 1.1025 + mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function); 1.1026 + 1.1027 + nsresult rv = function.ErrorCode(); 1.1028 + NS_ENSURE_SUCCESS(rv, rv); 1.1029 + 1.1030 + rv = CreateJournals(); 1.1031 + NS_ENSURE_SUCCESS(rv, rv); 1.1032 + 1.1033 + return NS_OK; 1.1034 +} 1.1035 + 1.1036 +void 1.1037 +UpdateRefcountFunction::DidCommit() 1.1038 +{ 1.1039 + mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nullptr); 1.1040 + 1.1041 + nsresult rv = RemoveJournals(mJournalsToRemoveAfterCommit); 1.1042 + NS_ENSURE_SUCCESS_VOID(rv); 1.1043 +} 1.1044 + 1.1045 +void 1.1046 +UpdateRefcountFunction::DidAbort() 1.1047 +{ 1.1048 + nsresult rv = RemoveJournals(mJournalsToRemoveAfterAbort); 1.1049 + NS_ENSURE_SUCCESS_VOID(rv); 1.1050 +} 1.1051 + 1.1052 +nsresult 1.1053 +UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues, 1.1054 + int32_t aIndex, 1.1055 + UpdateType aUpdateType) 1.1056 +{ 1.1057 + int32_t type; 1.1058 + aValues->GetTypeOfIndex(aIndex, &type); 1.1059 + if (type == mozIStorageValueArray::VALUE_TYPE_NULL) { 1.1060 + return NS_OK; 1.1061 + } 1.1062 + 1.1063 + nsString ids; 1.1064 + aValues->GetString(aIndex, ids); 1.1065 + 1.1066 + nsTArray<int64_t> fileIds; 1.1067 + nsresult rv = IDBObjectStore::ConvertFileIdsToArray(ids, fileIds); 1.1068 + NS_ENSURE_SUCCESS(rv, rv); 1.1069 + 1.1070 + for (uint32_t i = 0; i < fileIds.Length(); i++) { 1.1071 + int64_t id = fileIds.ElementAt(i); 1.1072 + 1.1073 + FileInfoEntry* entry; 1.1074 + if (!mFileInfoEntries.Get(id, &entry)) { 1.1075 + nsRefPtr<FileInfo> fileInfo = mFileManager->GetFileInfo(id); 1.1076 + NS_ASSERTION(fileInfo, "Shouldn't be null!"); 1.1077 + 1.1078 + nsAutoPtr<FileInfoEntry> newEntry(new FileInfoEntry(fileInfo)); 1.1079 + mFileInfoEntries.Put(id, newEntry); 1.1080 + entry = newEntry.forget(); 1.1081 + } 1.1082 + 1.1083 + if (mInSavepoint) { 1.1084 + mSavepointEntriesIndex.Put(id, entry); 1.1085 + } 1.1086 + 1.1087 + switch (aUpdateType) { 1.1088 + case eIncrement: 1.1089 + entry->mDelta++; 1.1090 + if (mInSavepoint) { 1.1091 + entry->mSavepointDelta++; 1.1092 + } 1.1093 + break; 1.1094 + case eDecrement: 1.1095 + entry->mDelta--; 1.1096 + if (mInSavepoint) { 1.1097 + entry->mSavepointDelta--; 1.1098 + } 1.1099 + break; 1.1100 + default: 1.1101 + NS_NOTREACHED("Unknown update type!"); 1.1102 + } 1.1103 + } 1.1104 + 1.1105 + return NS_OK; 1.1106 +} 1.1107 + 1.1108 +nsresult 1.1109 +UpdateRefcountFunction::CreateJournals() 1.1110 +{ 1.1111 + nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory(); 1.1112 + NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE); 1.1113 + 1.1114 + for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) { 1.1115 + int64_t id = mJournalsToCreateBeforeCommit[i]; 1.1116 + 1.1117 + nsCOMPtr<nsIFile> file = 1.1118 + mFileManager->GetFileForId(journalDirectory, id); 1.1119 + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 1.1120 + 1.1121 + nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); 1.1122 + NS_ENSURE_SUCCESS(rv, rv); 1.1123 + 1.1124 + mJournalsToRemoveAfterAbort.AppendElement(id); 1.1125 + } 1.1126 + 1.1127 + return NS_OK; 1.1128 +} 1.1129 + 1.1130 +nsresult 1.1131 +UpdateRefcountFunction::RemoveJournals(const nsTArray<int64_t>& aJournals) 1.1132 +{ 1.1133 + nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory(); 1.1134 + NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE); 1.1135 + 1.1136 + for (uint32_t index = 0; index < aJournals.Length(); index++) { 1.1137 + nsCOMPtr<nsIFile> file = 1.1138 + mFileManager->GetFileForId(journalDirectory, aJournals[index]); 1.1139 + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 1.1140 + 1.1141 + if (NS_FAILED(file->Remove(false))) { 1.1142 + NS_WARNING("Failed to removed journal!"); 1.1143 + } 1.1144 + } 1.1145 + 1.1146 + return NS_OK; 1.1147 +} 1.1148 + 1.1149 +PLDHashOperator 1.1150 +UpdateRefcountFunction::DatabaseUpdateCallback(const uint64_t& aKey, 1.1151 + FileInfoEntry* aValue, 1.1152 + void* aUserArg) 1.1153 +{ 1.1154 + if (!aValue->mDelta) { 1.1155 + return PL_DHASH_NEXT; 1.1156 + } 1.1157 + 1.1158 + DatabaseUpdateFunction* function = 1.1159 + static_cast<DatabaseUpdateFunction*>(aUserArg); 1.1160 + 1.1161 + if (!function->Update(aKey, aValue->mDelta)) { 1.1162 + return PL_DHASH_STOP; 1.1163 + } 1.1164 + 1.1165 + return PL_DHASH_NEXT; 1.1166 +} 1.1167 + 1.1168 +PLDHashOperator 1.1169 +UpdateRefcountFunction::FileInfoUpdateCallback(const uint64_t& aKey, 1.1170 + FileInfoEntry* aValue, 1.1171 + void* aUserArg) 1.1172 +{ 1.1173 + if (aValue->mDelta) { 1.1174 + aValue->mFileInfo->UpdateDBRefs(aValue->mDelta); 1.1175 + } 1.1176 + 1.1177 + return PL_DHASH_NEXT; 1.1178 +} 1.1179 + 1.1180 +PLDHashOperator 1.1181 +UpdateRefcountFunction::RollbackSavepointCallback(const uint64_t& aKey, 1.1182 + FileInfoEntry* aValue, 1.1183 + void* aUserArg) 1.1184 +{ 1.1185 + aValue->mDelta -= aValue->mSavepointDelta; 1.1186 + 1.1187 + return PL_DHASH_NEXT; 1.1188 +} 1.1189 + 1.1190 +bool 1.1191 +UpdateRefcountFunction::DatabaseUpdateFunction::Update(int64_t aId, 1.1192 + int32_t aDelta) 1.1193 +{ 1.1194 + nsresult rv = UpdateInternal(aId, aDelta); 1.1195 + if (NS_FAILED(rv)) { 1.1196 + mErrorCode = rv; 1.1197 + return false; 1.1198 + } 1.1199 + 1.1200 + return true; 1.1201 +} 1.1202 + 1.1203 +nsresult 1.1204 +UpdateRefcountFunction::DatabaseUpdateFunction::UpdateInternal(int64_t aId, 1.1205 + int32_t aDelta) 1.1206 +{ 1.1207 + nsresult rv; 1.1208 + 1.1209 + if (!mUpdateStatement) { 1.1210 + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( 1.1211 + "UPDATE file SET refcount = refcount + :delta WHERE id = :id" 1.1212 + ), getter_AddRefs(mUpdateStatement)); 1.1213 + NS_ENSURE_SUCCESS(rv, rv); 1.1214 + } 1.1215 + 1.1216 + mozStorageStatementScoper updateScoper(mUpdateStatement); 1.1217 + 1.1218 + rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); 1.1219 + NS_ENSURE_SUCCESS(rv, rv); 1.1220 + 1.1221 + rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); 1.1222 + NS_ENSURE_SUCCESS(rv, rv); 1.1223 + 1.1224 + rv = mUpdateStatement->Execute(); 1.1225 + NS_ENSURE_SUCCESS(rv, rv); 1.1226 + 1.1227 + int32_t rows; 1.1228 + rv = mConnection->GetAffectedRows(&rows); 1.1229 + NS_ENSURE_SUCCESS(rv, rv); 1.1230 + 1.1231 + if (rows > 0) { 1.1232 + if (!mSelectStatement) { 1.1233 + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( 1.1234 + "SELECT id FROM file where id = :id" 1.1235 + ), getter_AddRefs(mSelectStatement)); 1.1236 + NS_ENSURE_SUCCESS(rv, rv); 1.1237 + } 1.1238 + 1.1239 + mozStorageStatementScoper selectScoper(mSelectStatement); 1.1240 + 1.1241 + rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); 1.1242 + NS_ENSURE_SUCCESS(rv, rv); 1.1243 + 1.1244 + bool hasResult; 1.1245 + rv = mSelectStatement->ExecuteStep(&hasResult); 1.1246 + NS_ENSURE_SUCCESS(rv, rv); 1.1247 + 1.1248 + if (!hasResult) { 1.1249 + // Don't have to create the journal here, we can create all at once, 1.1250 + // just before commit 1.1251 + mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId); 1.1252 + } 1.1253 + 1.1254 + return NS_OK; 1.1255 + } 1.1256 + 1.1257 + if (!mInsertStatement) { 1.1258 + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( 1.1259 + "INSERT INTO file (id, refcount) VALUES(:id, :delta)" 1.1260 + ), getter_AddRefs(mInsertStatement)); 1.1261 + NS_ENSURE_SUCCESS(rv, rv); 1.1262 + } 1.1263 + 1.1264 + mozStorageStatementScoper insertScoper(mInsertStatement); 1.1265 + 1.1266 + rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); 1.1267 + NS_ENSURE_SUCCESS(rv, rv); 1.1268 + 1.1269 + rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); 1.1270 + NS_ENSURE_SUCCESS(rv, rv); 1.1271 + 1.1272 + rv = mInsertStatement->Execute(); 1.1273 + NS_ENSURE_SUCCESS(rv, rv); 1.1274 + 1.1275 + mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId); 1.1276 + 1.1277 + return NS_OK; 1.1278 +}