Bug 1143885 - Fix transaction handling when requests are killed prematurely, r=khuey.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 18 Mar 2015 14:20:59 -0700
changeset 234377 57c55ce051cd1952ffb2d6f95b5ec18ecff426ac
parent 234376 75865c5deec88c09700470f483ebb287d47d22c3
child 234378 b3fee83f129be1098c1e24a63d03fa83eb193958
push id57107
push userbturner@mozilla.com
push dateThu, 19 Mar 2015 00:59:46 +0000
treeherdermozilla-inbound@57c55ce051cd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1143885
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1143885 - Fix transaction handling when requests are killed prematurely, r=khuey.
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsChild.h
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -1823,88 +1823,68 @@ BackgroundVersionChangeTransactionChild:
 BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
   : BackgroundRequestChildBase(aRequest)
   , mTransaction(aRequest->GetTransaction())
 {
   MOZ_ASSERT(mTransaction);
   mTransaction->AssertIsOnOwningThread();
 
   MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild);
-
-  mTransaction->OnNewRequest();
 }
 
 BackgroundRequestChild::~BackgroundRequestChild()
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT_IF(!IsActorDestroyed(), mTransaction);
+  MOZ_ASSERT(!mTransaction);
 
   MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
-
-  MaybeFinishTransactionEarly();
 }
 
 void
 BackgroundRequestChild::HoldFileInfosUntilComplete(
                                        nsTArray<nsRefPtr<FileInfo>>& aFileInfos)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mFileInfos.IsEmpty());
 
   mFileInfos.SwapElements(aFileInfos);
 }
 
 void
-BackgroundRequestChild::MaybeFinishTransactionEarly()
-{
-  AssertIsOnOwningThread();
-
-  if (mTransaction) {
-    mTransaction->AssertIsOnOwningThread();
-
-    mTransaction->OnRequestFinished();
-    mTransaction = nullptr;
-  }
-}
-
-bool
 BackgroundRequestChild::HandleResponse(nsresult aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(NS_FAILED(aResponse));
   MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
   MOZ_ASSERT(mTransaction);
 
   DispatchErrorEvent(mRequest, aResponse, mTransaction);
-  return true;
 }
 
-bool
+void
 BackgroundRequestChild::HandleResponse(const Key& aResponse)
 {
   AssertIsOnOwningThread();
 
   ResultHelper helper(mRequest, mTransaction, &aResponse);
 
   DispatchSuccessEvent(&helper);
-  return true;
 }
 
-bool
+void
 BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse)
 {
   AssertIsOnOwningThread();
 
   ResultHelper helper(mRequest, mTransaction, &aResponse);
 
   DispatchSuccessEvent(&helper);
-  return true;
 }
 
-bool
+void
 BackgroundRequestChild::HandleResponse(
                              const SerializedStructuredCloneReadInfo& aResponse)
 {
   AssertIsOnOwningThread();
 
   // XXX Fix this somehow...
   auto& serializedCloneInfo =
     const_cast<SerializedStructuredCloneReadInfo&>(aResponse);
@@ -1914,20 +1894,19 @@ BackgroundRequestChild::HandleResponse(
 
   ConvertActorsToBlobs(mTransaction->Database(),
                        aResponse,
                        cloneReadInfo.mFiles);
 
   ResultHelper helper(mRequest, mTransaction, &cloneReadInfo);
 
   DispatchSuccessEvent(&helper);
-  return true;
 }
 
-bool
+void
 BackgroundRequestChild::HandleResponse(
                    const nsTArray<SerializedStructuredCloneReadInfo>& aResponse)
 {
   AssertIsOnOwningThread();
 
   nsTArray<StructuredCloneReadInfo> cloneReadInfos;
 
   if (!aResponse.IsEmpty()) {
@@ -1952,120 +1931,143 @@ BackgroundRequestChild::HandleResponse(
                            serializedCloneInfo,
                            cloneReadInfo->mFiles);
     }
   }
 
   ResultHelper helper(mRequest, mTransaction, &cloneReadInfos);
 
   DispatchSuccessEvent(&helper);
-  return true;
 }
 
-bool
+void
 BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
 {
   AssertIsOnOwningThread();
 
   ResultHelper helper(mRequest, mTransaction, &aResponse);
 
   DispatchSuccessEvent(&helper);
-  return true;
 }
 
-bool
+void
 BackgroundRequestChild::HandleResponse(uint64_t aResponse)
 {
   AssertIsOnOwningThread();
 
   JS::Value response(JS::NumberValue(aResponse));
 
   ResultHelper helper(mRequest, mTransaction, &response);
 
   DispatchSuccessEvent(&helper);
-  return true;
 }
 
 void
 BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnOwningThread();
 
   MaybeCollectGarbageOnIPCMessage();
 
-  MaybeFinishTransactionEarly();
-
   NoteActorDestroyed();
+
+  if (mTransaction) {
+    mTransaction->AssertIsOnOwningThread();
+
+    mTransaction->OnRequestFinished(/* aActorDestroyedNormally */
+                                    aWhy == Deletion);
+#ifdef DEBUG
+    mTransaction = nullptr;
+#endif
+  }
 }
 
 bool
 BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
   MOZ_ASSERT(mTransaction);
 
   MaybeCollectGarbageOnIPCMessage();
 
-  // Always fire an "error" event with ABORT_ERR if the transaction was aborted,
-  // even if the request succeeded or failed with another error.
   if (mTransaction->IsAborted()) {
-    return HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+    // Always fire an "error" event with ABORT_ERR if the transaction was
+    // aborted, even if the request succeeded or failed with another error.
+    HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+  } else {
+    switch (aResponse.type()) {
+      case RequestResponse::Tnsresult:
+        HandleResponse(aResponse.get_nsresult());
+        break;
+
+      case RequestResponse::TObjectStoreAddResponse:
+        HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
+        break;
+
+      case RequestResponse::TObjectStorePutResponse:
+        HandleResponse(aResponse.get_ObjectStorePutResponse().key());
+        break;
+
+      case RequestResponse::TObjectStoreGetResponse:
+        HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
+        break;
+
+      case RequestResponse::TObjectStoreGetAllResponse:
+        HandleResponse(aResponse.get_ObjectStoreGetAllResponse().cloneInfos());
+        break;
+
+      case RequestResponse::TObjectStoreGetAllKeysResponse:
+        HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys());
+        break;
+
+      case RequestResponse::TObjectStoreDeleteResponse:
+        HandleResponse(JS::UndefinedHandleValue);
+        break;
+
+      case RequestResponse::TObjectStoreClearResponse:
+        HandleResponse(JS::UndefinedHandleValue);
+        break;
+
+      case RequestResponse::TObjectStoreCountResponse:
+        HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
+        break;
+
+      case RequestResponse::TIndexGetResponse:
+        HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
+        break;
+
+      case RequestResponse::TIndexGetKeyResponse:
+        HandleResponse(aResponse.get_IndexGetKeyResponse().key());
+        break;
+
+      case RequestResponse::TIndexGetAllResponse:
+        HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
+        break;
+
+      case RequestResponse::TIndexGetAllKeysResponse:
+        HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
+        break;
+
+      case RequestResponse::TIndexCountResponse:
+        HandleResponse(aResponse.get_IndexCountResponse().count());
+        break;
+
+      default:
+        MOZ_CRASH("Unknown response type!");
+    }
   }
 
-  switch (aResponse.type()) {
-    case RequestResponse::Tnsresult:
-      return HandleResponse(aResponse.get_nsresult());
-
-    case RequestResponse::TObjectStoreAddResponse:
-      return HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
-
-    case RequestResponse::TObjectStorePutResponse:
-      return HandleResponse(aResponse.get_ObjectStorePutResponse().key());
-
-    case RequestResponse::TObjectStoreGetResponse:
-      return HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
-
-    case RequestResponse::TObjectStoreGetAllResponse:
-      return HandleResponse(aResponse.get_ObjectStoreGetAllResponse()
-                                     .cloneInfos());
-
-    case RequestResponse::TObjectStoreGetAllKeysResponse:
-      return HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse()
-                                     .keys());
-
-    case RequestResponse::TObjectStoreDeleteResponse:
-      return HandleResponse(JS::UndefinedHandleValue);
-
-    case RequestResponse::TObjectStoreClearResponse:
-      return HandleResponse(JS::UndefinedHandleValue);
-
-    case RequestResponse::TObjectStoreCountResponse:
-      return HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
-
-    case RequestResponse::TIndexGetResponse:
-      return HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
-
-    case RequestResponse::TIndexGetKeyResponse:
-      return HandleResponse(aResponse.get_IndexGetKeyResponse().key());
-
-    case RequestResponse::TIndexGetAllResponse:
-      return HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
-
-    case RequestResponse::TIndexGetAllKeysResponse:
-      return HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
-
-    case RequestResponse::TIndexCountResponse:
-      return HandleResponse(aResponse.get_IndexCountResponse().count());
-
-    default:
-      MOZ_CRASH("Unknown response type!");
-  }
-
-  MOZ_CRASH("Should never get here!");
+  mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
+
+  // Null this out so that we don't try to call OnRequestFinished() again in
+  // ActorDestroy.
+  mTransaction = nullptr;
+
+  return true;
 }
 
 /*******************************************************************************
  * BackgroundCursorChild
  ******************************************************************************/
 
 class BackgroundCursorChild::DelayedDeleteRunnable MOZ_FINAL
   : public nsICancelableRunnable
@@ -2365,17 +2367,18 @@ BackgroundCursorChild::ActorDestroy(Acto
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest);
   MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor);
 
   MaybeCollectGarbageOnIPCMessage();
 
   if (mStrongRequest && !mStrongCursor && mTransaction) {
-    mTransaction->OnRequestFinished();
+    mTransaction->OnRequestFinished(/* aActorDestroyedNormally */
+                                    aWhy == Deletion);
   }
 
   if (mCursor) {
     mCursor->ClearBackgroundActor();
 #ifdef DEBUG
     mCursor = nullptr;
 #endif
   }
@@ -2430,17 +2433,17 @@ BackgroundCursorChild::RecvResponse(cons
     case CursorResponse::TIndexKeyCursorResponse:
       HandleResponse(aResponse.get_IndexKeyCursorResponse());
       break;
 
     default:
       MOZ_CRASH("Should never get here!");
   }
 
-  mTransaction->OnRequestFinished();
+  mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
 
   return true;
 }
 
 // XXX This doesn't belong here. However, we're not yet porting MutableFile
 //     stuff to PBackground so this is necessary for the time being.
 void
 DispatchMutableFileResult(IDBRequest* aRequest,
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -556,53 +556,53 @@ private:
 };
 
 class BackgroundRequestChild MOZ_FINAL
   : public BackgroundRequestChildBase
   , public PBackgroundIDBRequestChild
 {
   friend class BackgroundTransactionChild;
   friend class BackgroundVersionChangeTransactionChild;
+  friend class IDBTransaction;
 
   nsRefPtr<IDBTransaction> mTransaction;
   nsTArray<nsRefPtr<FileInfo>> mFileInfos;
 
 public:
-  explicit BackgroundRequestChild(IDBRequest* aRequest);
-
   void
   HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos);
 
 private:
+  // Only created by IDBTransaction.
+  explicit
+  BackgroundRequestChild(IDBRequest* aRequest);
+
   // Only destroyed by BackgroundTransactionChild or
   // BackgroundVersionChangeTransactionChild.
   ~BackgroundRequestChild();
 
   void
-  MaybeFinishTransactionEarly();
-
-  bool
   HandleResponse(nsresult aResponse);
 
-  bool
+  void
   HandleResponse(const Key& aResponse);
 
-  bool
+  void
   HandleResponse(const nsTArray<Key>& aResponse);
 
-  bool
+  void
   HandleResponse(const SerializedStructuredCloneReadInfo& aResponse);
 
-  bool
+  void
   HandleResponse(const nsTArray<SerializedStructuredCloneReadInfo>& aResponse);
 
-  bool
+  void
   HandleResponse(JS::Handle<JS::Value> aResponse);
 
-  bool
+  void
   HandleResponse(uint64_t aResponse);
 
   // IPDL methods are only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   Recv__delete__(const RequestResponse& aResponse) MOZ_OVERRIDE;
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -287,19 +287,17 @@ IDBIndex::GetInternal(bool aKeyOnly,
                  request->LoggingSerialNumber(),
                  IDB_LOG_STRINGIFY(transaction->Database()),
                  IDB_LOG_STRINGIFY(transaction),
                  IDB_LOG_STRINGIFY(mObjectStore),
                  IDB_LOG_STRINGIFY(this),
                  IDB_LOG_STRINGIFY(keyRange));
   }
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  transaction->StartRequest(actor, params);
+  transaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBIndex::GetAllInternal(bool aKeysOnly,
                          JSContext* aCx,
                          JS::Handle<JS::Value> aKey,
@@ -370,19 +368,17 @@ IDBIndex::GetAllInternal(bool aKeysOnly,
                  IDB_LOG_STRINGIFY(transaction->Database()),
                  IDB_LOG_STRINGIFY(transaction),
                  IDB_LOG_STRINGIFY(mObjectStore),
                  IDB_LOG_STRINGIFY(this),
                  IDB_LOG_STRINGIFY(keyRange),
                  IDB_LOG_STRINGIFY(aLimit));
   }
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  transaction->StartRequest(actor, params);
+  transaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBIndex::OpenCursorInternal(bool aKeysOnly,
                              JSContext* aCx,
                              JS::Handle<JS::Value> aRange,
@@ -522,19 +518,17 @@ IDBIndex::Count(JSContext* aCx,
                transaction->LoggingSerialNumber(),
                request->LoggingSerialNumber(),
                IDB_LOG_STRINGIFY(transaction->Database()),
                IDB_LOG_STRINGIFY(transaction),
                IDB_LOG_STRINGIFY(mObjectStore),
                IDB_LOG_STRINGIFY(this),
                IDB_LOG_STRINGIFY(keyRange));
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  transaction->StartRequest(actor, params);
+  transaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1274,19 +1274,18 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
                    request->LoggingSerialNumber(),
                    IDB_LOG_STRINGIFY(mTransaction->Database()),
                    IDB_LOG_STRINGIFY(mTransaction),
                    IDB_LOG_STRINGIFY(this),
                    IDB_LOG_STRINGIFY(key));
     }
   }
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  mTransaction->StartRequest(actor, params);
+  BackgroundRequestChild* actor = mTransaction->StartRequest(request, params);
+  MOZ_ASSERT(actor);
 
   if (!fileInfosToKeepAlive.IsEmpty()) {
     nsTArray<nsRefPtr<FileInfo>> fileInfos;
     fileInfosToKeepAlive.SwapElements(fileInfos);
 
     actor->HoldFileInfosUntilComplete(fileInfos);
     MOZ_ASSERT(fileInfos.IsEmpty());
   }
@@ -1360,19 +1359,17 @@ IDBObjectStore::GetAllInternal(bool aKey
                  request->LoggingSerialNumber(),
                  IDB_LOG_STRINGIFY(mTransaction->Database()),
                  IDB_LOG_STRINGIFY(mTransaction),
                  IDB_LOG_STRINGIFY(this),
                  IDB_LOG_STRINGIFY(keyRange),
                  IDB_LOG_STRINGIFY(aLimit));
   }
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  mTransaction->StartRequest(actor, params);
+  mTransaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::Clear(ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
@@ -1398,19 +1395,17 @@ IDBObjectStore::Clear(ErrorResult& aRv)
                "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.clear()",
                IDB_LOG_ID_STRING(),
                mTransaction->LoggingSerialNumber(),
                request->LoggingSerialNumber(),
                IDB_LOG_STRINGIFY(mTransaction->Database()),
                IDB_LOG_STRINGIFY(mTransaction),
                IDB_LOG_STRINGIFY(this));
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  mTransaction->StartRequest(actor, params);
+  mTransaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBIndex>
 IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
 {
   AssertIsOnOwningThread();
@@ -1595,19 +1590,17 @@ IDBObjectStore::Get(JSContext* aCx,
                IDB_LOG_ID_STRING(),
                mTransaction->LoggingSerialNumber(),
                request->LoggingSerialNumber(),
                IDB_LOG_STRINGIFY(mTransaction->Database()),
                IDB_LOG_STRINGIFY(mTransaction),
                IDB_LOG_STRINGIFY(this),
                IDB_LOG_STRINGIFY(keyRange));
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  mTransaction->StartRequest(actor, params);
+  mTransaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::DeleteInternal(JSContext* aCx,
                                JS::Handle<JS::Value> aKey,
                                bool aFromCursor,
@@ -1652,19 +1645,17 @@ IDBObjectStore::DeleteInternal(JSContext
                  mTransaction->LoggingSerialNumber(),
                  request->LoggingSerialNumber(),
                  IDB_LOG_STRINGIFY(mTransaction->Database()),
                  IDB_LOG_STRINGIFY(mTransaction),
                  IDB_LOG_STRINGIFY(this),
                  IDB_LOG_STRINGIFY(keyRange));
   }
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  mTransaction->StartRequest(actor, params);
+  mTransaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBIndex>
 IDBObjectStore::CreateIndex(const nsAString& aName,
                             const nsAString& aKeyPath,
                             const IDBIndexParameters& aOptionalParameters,
@@ -1894,19 +1885,17 @@ IDBObjectStore::Count(JSContext* aCx,
                IDB_LOG_ID_STRING(),
                mTransaction->LoggingSerialNumber(),
                request->LoggingSerialNumber(),
                IDB_LOG_STRINGIFY(mTransaction->Database()),
                IDB_LOG_STRINGIFY(mTransaction),
                IDB_LOG_STRINGIFY(this),
                IDB_LOG_STRINGIFY(keyRange));
 
-  BackgroundRequestChild* actor = new BackgroundRequestChild(request);
-
-  mTransaction->StartRequest(actor, params);
+  mTransaction->StartRequest(request, params);
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::OpenCursorInternal(bool aKeysOnly,
                                    JSContext* aCx,
                                    JS::Handle<JS::Value> aRange,
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -320,35 +320,41 @@ IDBTransaction::SetBackgroundActor(Backg
   AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
   MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor);
   MOZ_ASSERT(mMode != VERSION_CHANGE);
 
   mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
 }
 
-void
-IDBTransaction::StartRequest(BackgroundRequestChild* aBackgroundActor,
-                             const RequestParams& aParams)
+BackgroundRequestChild*
+IDBTransaction::StartRequest(IDBRequest* aRequest, const RequestParams& aParams)
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT(aBackgroundActor);
+  MOZ_ASSERT(aRequest);
   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
 
+  BackgroundRequestChild* actor = new BackgroundRequestChild(aRequest);
+
   if (mMode == VERSION_CHANGE) {
     MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
 
     mBackgroundActor.mVersionChangeBackgroundActor->
-      SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams);
+      SendPBackgroundIDBRequestConstructor(actor, aParams);
   } else {
     MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
 
     mBackgroundActor.mNormalBackgroundActor->
-      SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams);
+      SendPBackgroundIDBRequestConstructor(actor, aParams);
   }
+
+  // Balanced in BackgroundRequestChild::Recv__delete__().
+  OnNewRequest();
+
+  return actor;
 }
 
 void
 IDBTransaction::OpenCursor(BackgroundCursorChild* aBackgroundActor,
                            const OpenCursorParams& aParams)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
@@ -397,30 +403,44 @@ IDBTransaction::OnNewRequest()
     MOZ_ASSERT(INITIAL == mReadyState);
     mReadyState = LOADING;
   }
 
   ++mPendingRequestCount;
 }
 
 void
-IDBTransaction::OnRequestFinished()
+IDBTransaction::OnRequestFinished(bool aActorDestroyedNormally)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mPendingRequestCount);
 
   --mPendingRequestCount;
 
   if (!mPendingRequestCount && !mDatabase->IsInvalidated()) {
     mReadyState = COMMITTING;
 
-    if (NS_SUCCEEDED(mAbortCode)) {
-      SendCommit();
+    if (aActorDestroyedNormally) {
+      if (NS_SUCCEEDED(mAbortCode)) {
+        SendCommit();
+      } else {
+        SendAbort(mAbortCode);
+      }
     } else {
-      SendAbort(mAbortCode);
+      // Don't try to send any more messages to the parent if the request actor
+      // was killed.
+#ifdef DEBUG
+      MOZ_ASSERT(!mSentCommitOrAbort);
+      mSentCommitOrAbort = true;
+#endif
+      IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
+                     "Request actor was killed, transaction will be aborted",
+                   "IndexedDB %s: C T[%lld]: IDBTransaction abort",
+                   IDB_LOG_ID_STRING(),
+                   LoggingSerialNumber());
     }
   }
 }
 
 void
 IDBTransaction::SendCommit()
 {
   AssertIsOnOwningThread();
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -44,16 +44,19 @@ class ObjectStoreSpec;
 class OpenCursorParams;
 class PBackgroundIDBDatabaseFileChild;
 class RequestParams;
 
 class IDBTransaction MOZ_FINAL
   : public IDBWrapperCache
   , public nsIRunnable
 {
+  friend class BackgroundCursorChild;
+  friend class BackgroundRequestChild;
+
   class WorkerFeature;
   friend class WorkerFeature;
 
 public:
   enum Mode
   {
     READ_ONLY = 0,
     READ_WRITE,
@@ -145,33 +148,26 @@ public:
 
     if (mMode == VERSION_CHANGE) {
       mBackgroundActor.mVersionChangeBackgroundActor = nullptr;
     } else {
       mBackgroundActor.mNormalBackgroundActor = nullptr;
     }
   }
 
-  void
-  StartRequest(BackgroundRequestChild* aBackgroundActor,
-               const RequestParams& aParams);
+  BackgroundRequestChild*
+  StartRequest(IDBRequest* aRequest, const RequestParams& aParams);
 
   void
   OpenCursor(BackgroundCursorChild* aBackgroundActor,
              const OpenCursorParams& aParams);
 
   void
   RefreshSpec(bool aMayDelete);
 
-  void
-  OnNewRequest();
-
-  void
-  OnRequestFinished();
-
   bool
   IsOpen() const;
 
   bool
   IsFinished() const
   {
     AssertIsOnOwningThread();
     return mReadyState > LOADING;
@@ -309,15 +305,21 @@ private:
   void
   AbortInternal(nsresult aAbortCode, already_AddRefed<DOMError> aError);
 
   void
   SendCommit();
 
   void
   SendAbort(nsresult aResultCode);
+
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished(bool aActorDestroyedNormally);
 };
 
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_indexeddb_idbtransaction_h__