Bug 808743 - Better protection for PBrowser shutdown and database invalidation in multiprocess scenarios. r=khuey, r=cjones, a=blocking-basecamp
authorBen Turner <bent.mozilla@gmail.com>
Fri, 09 Nov 2012 19:29:07 -0800
changeset 116817 6157d5ec9eb70617552a53afff081e5bb1f25d67
parent 116816 e17e8976baa280165073f0f8891b7b94991deb4d
child 116818 b35bd88a4f754271fec7ccafdb05d5e44ff670d5
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, cjones, blocking-basecamp
bugs808743
milestone18.0a2
Bug 808743 - Better protection for PBrowser shutdown and database invalidation in multiprocess scenarios. r=khuey, r=cjones, a=blocking-basecamp
dom/indexedDB/AsyncConnectionHelper.cpp
dom/indexedDB/AsyncConnectionHelper.h
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBCursor.h
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/IDBRequest.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/ipc/IndexedDBChild.cpp
dom/indexedDB/ipc/IndexedDBChild.h
dom/indexedDB/ipc/IndexedDBParent.cpp
dom/indexedDB/ipc/IndexedDBParent.h
dom/ipc/TabChild.cpp
ipc/chromium/src/chrome/common/ipc_channel_win.cc
ipc/ipdl/ipdl/lower.py
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -16,16 +16,17 @@
 #include "nsWrapperCacheInlines.h"
 
 #include "IDBEvents.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
+#include "ipc/IndexedDBParent.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
 IDBTransaction* gCurrentTransaction = nullptr;
 
 const uint32_t kProgressHandlerGranularity = 1000;
@@ -198,46 +199,57 @@ AsyncConnectionHelper::Run()
     IDBTransaction* oldTransaction = gCurrentTransaction;
     gCurrentTransaction = mTransaction;
 
     ChildProcessSendResult sendResult =
       IndexedDatabaseManager::IsMainProcess() ?
       MaybeSendResponseToChildProcess(mResultCode) :
       Success_NotSent;
 
-    NS_ASSERTION(sendResult == Success_Sent || sendResult == Success_NotSent ||
-                 sendResult == Error,
-                 "Unknown result from MaybeSendResultsToChildProcess!");
-
-    if (sendResult == Success_Sent) {
-      if (mRequest) {
-        mRequest->NotifyHelperSentResultsToChildProcess(NS_OK);
-      }
-    }
-    else if (sendResult == Error) {
-      NS_WARNING("MaybeSendResultsToChildProcess failed!");
-      mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-      if (mRequest) {
-        mRequest->NotifyHelperSentResultsToChildProcess(mResultCode);
-      }
-    }
-    else if (sendResult == Success_NotSent) {
-      if (mRequest) {
-        nsresult rv = mRequest->NotifyHelperCompleted(this);
-        if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
-          mResultCode = rv;
+    switch (sendResult) {
+      case Success_Sent: {
+        if (mRequest) {
+          mRequest->NotifyHelperSentResultsToChildProcess(NS_OK);
         }
+        break;
       }
 
-      // Call OnError if the database had an error or if the OnSuccess handler
-      // has an error.
-      if (NS_FAILED(mResultCode) ||
-          NS_FAILED((mResultCode = OnSuccess()))) {
-        OnError();
+      case Success_NotSent: {
+        if (mRequest) {
+          nsresult rv = mRequest->NotifyHelperCompleted(this);
+          if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
+            mResultCode = rv;
+          }
+        }
+
+        // Call OnError if the database had an error or if the OnSuccess
+        // handler has an error.
+        if (NS_FAILED(mResultCode) ||
+            NS_FAILED((mResultCode = OnSuccess()))) {
+          OnError();
+        }
+        break;
       }
+
+      case Success_ActorDisconnected: {
+        // Nothing needs to be done here.
+        break;
+      }
+
+      case Error: {
+        NS_WARNING("MaybeSendResultsToChildProcess failed!");
+        mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+        if (mRequest) {
+          mRequest->NotifyHelperSentResultsToChildProcess(mResultCode);
+        }
+        break;
+      }
+
+      default:
+        MOZ_NOT_REACHED("Unknown value for ChildProcessSendResult!");
     }
 
     NS_ASSERTION(gCurrentTransaction == mTransaction, "Should be unchanged!");
     gCurrentTransaction = oldTransaction;
 
     if (mDispatched && mTransaction) {
       mTransaction->OnRequestFinished();
     }
@@ -527,17 +539,18 @@ AsyncConnectionHelper::MaybeSendResponse
 
   IDBTransaction* trans = GetCurrentTransaction();
   // We may not have a transaction, e.g. for deleteDatabase
   if (!trans) {
     return Success_NotSent;
   }
 
   // Are we shutting down the child?
-  if (trans->Database()->IsDisconnectedFromActor()) {
+  IndexedDBDatabaseParent* dbActor = trans->Database()->GetActorParent();
+  if (dbActor && dbActor->IsDisconnected()) {
     return Success_ActorDisconnected;
   }
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
--- a/dom/indexedDB/AsyncConnectionHelper.h
+++ b/dom/indexedDB/AsyncConnectionHelper.h
@@ -102,22 +102,27 @@ public:
   void SetError(nsresult aErrorCode)
   {
     NS_ASSERTION(NS_FAILED(aErrorCode), "Not a failure code!");
     mResultCode = aErrorCode;
   }
 
   static IDBTransaction* GetCurrentTransaction();
 
-  bool HasTransaction()
+  bool HasTransaction() const
+  {
+    return !!mTransaction;
+  }
+
+  IDBTransaction* GetTransaction() const
   {
     return mTransaction;
   }
 
-  nsISupports* GetSource()
+  nsISupports* GetSource() const
   {
     return mRequest ? mRequest->Source() : nullptr;
   }
 
   virtual nsresult GetResultCode() MOZ_OVERRIDE
   {
     return mResultCode;
   }
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -763,16 +763,22 @@ CursorHelper::ReleaseMainThreadObjects()
 
 nsresult
 CursorHelper::Dispatch(nsIEventTarget* aDatabaseThread)
 {
   if (IndexedDatabaseManager::IsMainProcess()) {
     return AsyncConnectionHelper::Dispatch(aDatabaseThread);
   }
 
+  // If we've been invalidated then there's no point sending anything to the
+  // parent process.
+  if (mCursor->Transaction()->Database()->IsInvalidated()) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
   IndexedDBCursorChild* cursorActor = mCursor->GetActorChild();
   NS_ASSERTION(cursorActor, "Must have an actor here!");
 
   CursorRequestParams params;
   nsresult rv = PackArgumentsForParentProcess(params);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NoDispatchEventTarget target;
@@ -913,17 +919,17 @@ ContinueHelper::SendResponseToChildProce
     ContinueResponse continueResponse;
     continueResponse.key() = mKey;
     continueResponse.objectKey() = mObjectKey;
     continueResponse.cloneInfo() = mCloneReadInfo;
     continueResponse.blobsParent().SwapElements(blobsParent);
     response = continueResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   UpdateCursorState();
 
   return Success_Sent;
 }
 
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -132,16 +132,22 @@ public:
   }
 
   IndexedDBCursorChild*
   GetActorChild() const
   {
     return mActorChild;
   }
 
+  IndexedDBCursorParent*
+  GetActorParent() const
+  {
+    return mActorParent;
+  }
+
   nsresult
   ContinueInternal(const Key& aKey,
                    int32_t aCount);
 
 protected:
   IDBCursor();
   ~IDBCursor();
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBDatabase.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/storage.h"
-#include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMLists.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
@@ -214,17 +213,16 @@ IDBDatabase::Create(IDBWrapperCache* aOw
 }
 
 IDBDatabase::IDBDatabase()
 : mDatabaseId(0),
   mActorChild(nullptr),
   mActorParent(nullptr),
   mContentParent(nullptr),
   mInvalidated(false),
-  mDisconnected(false),
   mRegistered(false),
   mClosed(false),
   mRunningVersionChange(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBDatabase::~IDBDatabase()
@@ -272,48 +270,36 @@ IDBDatabase::Invalidate()
     IndexedDatabaseManager::CancelPromptsForWindow(owner);
   }
 
   DatabaseInfo::Remove(mDatabaseId);
 
   // And let the child process know as well.
   if (mActorParent) {
     NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
-    mozilla::unused << mActorParent->SendInvalidate();
+    mActorParent->Invalidate();
   }
 }
 
 void
-IDBDatabase::DisconnectFromActor()
+IDBDatabase::DisconnectFromActorParent()
 {
+  NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (IsDisconnectedFromActor()) {
-    return;
-  }
-
-  mDisconnected = true;
-
   // Make sure we're closed too.
   Close();
 
   // Kill any outstanding prompts.
   nsPIDOMWindow* owner = GetOwner();
   if (owner) {
     IndexedDatabaseManager::CancelPromptsForWindow(owner);
   }
 }
 
-bool
-IDBDatabase::IsDisconnectedFromActor() const
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  return mDisconnected;
-}
-
 void
 IDBDatabase::CloseInternal(bool aIsDead)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mClosed) {
     mClosed = true;
 
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -105,22 +105,17 @@ public:
   // Whether or not the database has been invalidated. If it has then no further
   // transactions for this database will be allowed to run. This function may be
   // called on any thread.
   bool IsInvalidated() const
   {
     return mInvalidated;
   }
 
-  void DisconnectFromActor();
-
-  // Whether or not the database has been disconnected from its actor.  If true
-  // it is not safe to send any IPC messages to the actor representing this db
-  // or any of its subactors.
-  bool IsDisconnectedFromActor() const;
+  void DisconnectFromActorParent();
 
   void CloseInternal(bool aIsDead);
 
   // Whether or not the database has had Close called on it.
   bool IsClosed() const;
 
   void EnterSetVersionTransaction();
   void ExitSetVersionTransaction();
@@ -150,16 +145,22 @@ public:
   }
 
   IndexedDBDatabaseChild*
   GetActorChild() const
   {
     return mActorChild;
   }
 
+  IndexedDBDatabaseParent*
+  GetActorParent() const
+  {
+    return mActorParent;
+  }
+
   mozilla::dom::ContentParent*
   GetContentParent() const
   {
     return mContentParent;
   }
 
   nsresult
   CreateObjectStoreInternal(IDBTransaction* aTransaction,
@@ -190,17 +191,16 @@ private:
   nsRefPtr<FileManager> mFileManager;
 
   IndexedDBDatabaseChild* mActorChild;
   IndexedDBDatabaseParent* mActorParent;
 
   mozilla::dom::ContentParent* mContentParent;
 
   bool mInvalidated;
-  bool mDisconnected;
   bool mRegistered;
   bool mClosed;
   bool mRunningVersionChange;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbdatabase_h__
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -1012,16 +1012,22 @@ IndexHelper::ReleaseMainThreadObjects()
 
 nsresult
 IndexHelper::Dispatch(nsIEventTarget* aDatabaseThread)
 {
   if (IndexedDatabaseManager::IsMainProcess()) {
     return AsyncConnectionHelper::Dispatch(aDatabaseThread);
   }
 
+  // If we've been invalidated then there's no point sending anything to the
+  // parent process.
+  if (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
   IndexedDBIndexChild* indexActor = mIndex->GetActorChild();
   NS_ASSERTION(indexActor, "Must have an actor here!");
 
   IndexRequestParams params;
   nsresult rv = PackArgumentsForParentProcess(params);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NoDispatchEventTarget target;
@@ -1123,17 +1129,17 @@ GetKeyHelper::SendResponseToChildProcess
     response = aResultCode;
   }
   else {
     GetKeyResponse getKeyResponse;
     getKeyResponse.key() = mKey;
     response = getKeyResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetKeyHelper::UnpackResponseFromParentProcess(
@@ -1266,17 +1272,17 @@ GetHelper::SendResponseToChildProcess(ns
   }
   else {
     GetResponse getResponse;
     getResponse.cloneInfo() = mCloneReadInfo;
     getResponse.blobsParent().SwapElements(blobsParent);
     response = getResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
@@ -1443,17 +1449,17 @@ GetAllKeysHelper::SendResponseToChildPro
     response = aResultCode;
   }
   else {
     GetAllKeysResponse getAllKeysResponse;
     getAllKeysResponse.keys().AppendElements(mKeys);
     response = getAllKeysResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetAllKeysHelper::UnpackResponseFromParentProcess(
@@ -1633,17 +1639,17 @@ GetAllHelper::SendResponseToChildProcess
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     response = getAllResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetAllHelper::UnpackResponseFromParentProcess(
@@ -1938,29 +1944,25 @@ OpenKeyCursorHelper::SendResponseToChild
 
       IndexCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.objectKey() = mObjectKey;
       params.optionalCloneInfo() = mozilla::void_t();
 
-      IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
-
-      if (!indexActor->SendPIndexedDBCursorConstructor(cursorActor, params)) {
+      if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) {
         return Error;
       }
-
-      openCursorResponse = cursorActor;
     }
 
     response = openCursorResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 OpenKeyCursorHelper::UnpackResponseFromParentProcess(
@@ -2298,29 +2300,25 @@ OpenCursorHelper::SendResponseToChildPro
       IndexCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.objectKey() = mObjectKey;
       params.optionalCloneInfo() = mSerializedCloneReadInfo;
       params.blobsParent().SwapElements(blobsParent);
 
-      IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
-
-      if (!indexActor->SendPIndexedDBCursorConstructor(cursorActor, params)) {
+      if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) {
         return Error;
       }
-
-      openCursorResponse = cursorActor;
     }
 
     response = openCursorResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
@@ -2427,17 +2425,17 @@ CountHelper::SendResponseToChildProcess(
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     CountResponse countResponse = mCount;
     response = countResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 CountHelper::UnpackResponseFromParentProcess(
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2555,16 +2555,22 @@ ObjectStoreHelper::ReleaseMainThreadObje
 
 nsresult
 ObjectStoreHelper::Dispatch(nsIEventTarget* aDatabaseThread)
 {
   if (IndexedDatabaseManager::IsMainProcess()) {
     return AsyncConnectionHelper::Dispatch(aDatabaseThread);
   }
 
+  // If we've been invalidated then there's no point sending anything to the
+  // parent process.
+  if (mObjectStore->Transaction()->Database()->IsInvalidated()) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
   IndexedDBObjectStoreChild* objectStoreActor = mObjectStore->GetActorChild();
   NS_ASSERTION(objectStoreActor, "Must have an actor here!");
 
   ObjectStoreRequestParams params;
   nsresult rv = PackArgumentsForParentProcess(params);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NoDispatchEventTarget target;
@@ -2873,17 +2879,17 @@ AddHelper::SendResponseToChildProcess(ns
     response = putResponse;
   }
   else {
     AddResponse addResponse;
     addResponse.key() = mKey;
     response = addResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 AddHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
@@ -3008,17 +3014,17 @@ GetHelper::SendResponseToChildProcess(ns
   }
   else {
     GetResponse getResponse;
     getResponse.cloneInfo() = mCloneReadInfo;
     getResponse.blobsParent().SwapElements(blobsParent);
     response = getResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
@@ -3108,17 +3114,17 @@ DeleteHelper::SendResponseToChildProcess
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     response = DeleteResponse();
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 DeleteHelper::UnpackResponseFromParentProcess(
@@ -3172,17 +3178,17 @@ ClearHelper::SendResponseToChildProcess(
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     response = ClearResponse();
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 ClearHelper::UnpackResponseFromParentProcess(
@@ -3457,30 +3463,25 @@ OpenCursorHelper::SendResponseToChildPro
 
       ObjectStoreCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.cloneInfo() = mSerializedCloneReadInfo;
       params.blobsParent().SwapElements(blobsParent);
 
-      IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
-
-      if (!objectStoreActor->SendPIndexedDBCursorConstructor(cursorActor,
-                                                             params)) {
+      if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
         return Error;
       }
-
-      openCursorResponse = cursorActor;
     }
 
     response = openCursorResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 OpenCursorHelper::UnpackResponseFromParentProcess(
@@ -3888,17 +3889,17 @@ GetAllHelper::SendResponseToChildProcess
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     response = getAllResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetAllHelper::UnpackResponseFromParentProcess(
@@ -4040,17 +4041,17 @@ CountHelper::SendResponseToChildProcess(
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     CountResponse countResponse = mCount;
     response = countResponse;
   }
 
-  if (!actor->Send__delete__(actor, response)) {
+  if (!actor->SendResponse(response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 CountHelper::UnpackResponseFromParentProcess(
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -227,21 +227,21 @@ IDBRequest::FillScriptErrorEvent(nsScrip
   aEvent->fileName = mFilename.get();
 }
 
 NS_IMETHODIMP
 IDBRequest::GetReadyState(nsAString& aReadyState)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (mHaveResultOrErrorCode) {
-    aReadyState.AssignLiteral("done");
+  if (IsPending()) {
+    aReadyState.AssignLiteral("pending");
   }
   else {
-    aReadyState.AssignLiteral("pending");
+    aReadyState.AssignLiteral("done");
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBRequest::GetSource(nsISupports** aSource)
 {
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -79,16 +79,21 @@ public:
   {
     return mActorParent;
   }
 
   void CaptureCaller(JSContext* aCx);
 
   void FillScriptErrorEvent(nsScriptErrorEvent* aEvent) const;
 
+  bool IsPending() const
+  {
+    return !mHaveResultOrErrorCode;
+  }
+
 protected:
   IDBRequest();
   ~IDBRequest();
 
   nsCOMPtr<nsISupports> mSource;
   nsRefPtr<IDBTransaction> mTransaction;
 
   jsval mResultVal;
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -215,16 +215,24 @@ IDBTransaction::OnRequestFinished()
     NS_ASSERTION(NS_FAILED(mAbortCode) || mReadyState == IDBTransaction::LOADING,
                  "Bad state!");
     mReadyState = IDBTransaction::COMMITTING;
     CommitOrRollback();
   }
 }
 
 void
+IDBTransaction::OnRequestDisconnected()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mPendingRequests, "Mismatched calls!");
+  --mPendingRequests;
+}
+
+void
 IDBTransaction::RemoveObjectStore(const nsAString& aName)
 {
   NS_ASSERTION(mMode == IDBTransaction::VERSION_CHANGE,
                "Only remove object stores on VERSION_CHANGE transactions");
 
   mDatabaseInfo->RemoveObjectStore(aName);
 
   for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) {
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -96,16 +96,17 @@ public:
                           false);
   }
 
   // nsIDOMEventTarget
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
 
   void OnNewRequest();
   void OnRequestFinished();
+  void OnRequestDisconnected();
 
   void RemoveObjectStore(const nsAString& aName);
 
   void SetTransactionListener(IDBTransactionListener* aListener);
 
   bool StartSavepoint();
   nsresult ReleaseSavepoint();
   void RollbackSavepoint();
@@ -183,16 +184,22 @@ public:
   }
 
   IndexedDBTransactionChild*
   GetActorChild() const
   {
     return mActorChild;
   }
 
+  IndexedDBTransactionParent*
+  GetActorParent() const
+  {
+    return mActorParent;
+  }
+
   nsresult
   ObjectStoreInternal(const nsAString& aName,
                       IDBObjectStore** _retval);
 
   nsresult
   Abort(IDBRequest* aRequest);
 
   nsresult
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -1764,20 +1764,18 @@ OriginClearRunnable::DeleteFiles(Indexed
   nsCOMPtr<nsIFile> directory =
     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   rv = directory->InitWithPath(aManager->GetBaseDirectory());
   NS_ENSURE_SUCCESS_VOID(rv);
 
   nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  if (!entries) {
+  if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
+      !entries) {
     return;
   }
 
   nsCString originSanitized(mOriginOrPattern);
   SanitizeOriginString(originSanitized);
 
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -154,16 +154,19 @@ public:
 } // anonymous namespace
 
 /*******************************************************************************
  * IndexedDBChild
  ******************************************************************************/
 
 IndexedDBChild::IndexedDBChild(const nsCString& aASCIIOrigin)
 : mFactory(nullptr), mASCIIOrigin(aASCIIOrigin)
+#ifdef DEBUG
+  , mDisconnected(false)
+#endif
 {
   MOZ_COUNT_CTOR(IndexedDBChild);
 }
 
 IndexedDBChild::~IndexedDBChild()
 {
   MOZ_COUNT_DTOR(IndexedDBChild);
   MOZ_ASSERT(!mFactory);
@@ -175,16 +178,31 @@ IndexedDBChild::SetFactory(IDBFactory* a
   MOZ_ASSERT(aFactory);
   MOZ_ASSERT(!mFactory);
 
   aFactory->SetActor(this);
   mFactory = aFactory;
 }
 
 void
+IndexedDBChild::Disconnect()
+{
+#ifdef DEBUG
+  MOZ_ASSERT(!mDisconnected);
+  mDisconnected = true;
+#endif
+
+  const InfallibleTArray<PIndexedDBDatabaseChild*>& databases =
+    ManagedPIndexedDBDatabaseChild();
+  for (uint32_t i = 0; i < databases.Length(); ++i) {
+    static_cast<IndexedDBDatabaseChild*>(databases[i])->Disconnect();
+  }
+}
+
+void
 IndexedDBChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mFactory) {
     mFactory->SetActor(static_cast<IndexedDBChild*>(NULL));
 #ifdef DEBUG
     mFactory = NULL;
 #endif
   }
@@ -241,16 +259,26 @@ void
 IndexedDBDatabaseChild::SetRequest(IDBOpenDBRequest* aRequest)
 {
   MOZ_ASSERT(aRequest);
   MOZ_ASSERT(!mRequest);
 
   mRequest = aRequest;
 }
 
+void
+IndexedDBDatabaseChild::Disconnect()
+{
+  const InfallibleTArray<PIndexedDBTransactionChild*>& transactions =
+    ManagedPIndexedDBTransactionChild();
+  for (uint32_t i = 0; i < transactions.Length(); ++i) {
+    static_cast<IndexedDBTransactionChild*>(transactions[i])->Disconnect();
+  }
+}
+
 bool
 IndexedDBDatabaseChild::EnsureDatabase(
                            IDBOpenDBRequest* aRequest,
                            const DatabaseInfoGuts& aDBInfo,
                            const InfallibleTArray<ObjectStoreInfoGuts>& aOSInfo)
 {
   nsCOMPtr<nsIAtom> databaseId;
   if (mDatabase) {
@@ -548,16 +576,26 @@ IndexedDBTransactionChild::SetTransactio
 
   aTransaction->SetActor(this);
 
   mTransaction = aTransaction;
   mStrongTransaction = aTransaction;
 }
 
 void
+IndexedDBTransactionChild::Disconnect()
+{
+  const InfallibleTArray<PIndexedDBObjectStoreChild*>& objectStores =
+    ManagedPIndexedDBObjectStoreChild();
+  for (uint32_t i = 0; i < objectStores.Length(); ++i) {
+    static_cast<IndexedDBObjectStoreChild*>(objectStores[i])->Disconnect();
+  }
+}
+
+void
 IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv)
 {
   MOZ_ASSERT(mTransaction);
   MOZ_ASSERT(mStrongTransaction);
 
   nsRefPtr<IDBTransaction> transaction;
   mStrongTransaction.swap(transaction);
 
@@ -650,16 +688,38 @@ IndexedDBObjectStoreChild::IndexedDBObje
 
 IndexedDBObjectStoreChild::~IndexedDBObjectStoreChild()
 {
   MOZ_COUNT_DTOR(IndexedDBObjectStoreChild);
   MOZ_ASSERT(!mObjectStore);
 }
 
 void
+IndexedDBObjectStoreChild::Disconnect()
+{
+  const InfallibleTArray<PIndexedDBRequestChild*>& requests =
+    ManagedPIndexedDBRequestChild();
+  for (uint32_t i = 0; i < requests.Length(); ++i) {
+    static_cast<IndexedDBRequestChildBase*>(requests[i])->Disconnect();
+  }
+
+  const InfallibleTArray<PIndexedDBIndexChild*>& indexes =
+    ManagedPIndexedDBIndexChild();
+  for (uint32_t i = 0; i < indexes.Length(); ++i) {
+    static_cast<IndexedDBIndexChild*>(indexes[i])->Disconnect();
+  }
+
+  const InfallibleTArray<PIndexedDBCursorChild*>& cursors =
+    ManagedPIndexedDBCursorChild();
+  for (uint32_t i = 0; i < cursors.Length(); ++i) {
+    static_cast<IndexedDBCursorChild*>(cursors[i])->Disconnect();
+  }
+}
+
+void
 IndexedDBObjectStoreChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mObjectStore) {
     mObjectStore->SetActor(static_cast<IndexedDBObjectStoreChild*>(NULL));
 #ifdef DEBUG
     mObjectStore = NULL;
 #endif
   }
@@ -756,16 +816,32 @@ IndexedDBIndexChild::IndexedDBIndexChild
 
 IndexedDBIndexChild::~IndexedDBIndexChild()
 {
   MOZ_COUNT_DTOR(IndexedDBIndexChild);
   MOZ_ASSERT(!mIndex);
 }
 
 void
+IndexedDBIndexChild::Disconnect()
+{
+  const InfallibleTArray<PIndexedDBRequestChild*>& requests =
+    ManagedPIndexedDBRequestChild();
+  for (uint32_t i = 0; i < requests.Length(); ++i) {
+    static_cast<IndexedDBRequestChildBase*>(requests[i])->Disconnect();
+  }
+
+  const InfallibleTArray<PIndexedDBCursorChild*>& cursors =
+    ManagedPIndexedDBCursorChild();
+  for (uint32_t i = 0; i < cursors.Length(); ++i) {
+    static_cast<IndexedDBCursorChild*>(cursors[i])->Disconnect();
+  }
+}
+
+void
 IndexedDBIndexChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mIndex) {
     mIndex->SetActor(static_cast<IndexedDBIndexChild*>(NULL));
 #ifdef DEBUG
     mIndex = NULL;
 #endif
   }
@@ -878,16 +954,26 @@ IndexedDBCursorChild::SetCursor(IDBCurso
 
   aCursor->SetActor(this);
 
   mCursor = aCursor;
   mStrongCursor = aCursor;
 }
 
 void
+IndexedDBCursorChild::Disconnect()
+{
+  const InfallibleTArray<PIndexedDBRequestChild*>& requests =
+    ManagedPIndexedDBRequestChild();
+  for (uint32_t i = 0; i < requests.Length(); ++i) {
+    static_cast<IndexedDBRequestChildBase*>(requests[i])->Disconnect();
+  }
+}
+
+void
 IndexedDBCursorChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mCursor) {
     mCursor->SetActor(static_cast<IndexedDBCursorChild*>(NULL));
 #ifdef DEBUG
     mCursor = NULL;
 #endif
   }
@@ -921,17 +1007,34 @@ IndexedDBRequestChildBase::IndexedDBRequ
 IndexedDBRequestChildBase::~IndexedDBRequestChildBase()
 {
   MOZ_COUNT_DTOR(IndexedDBRequestChildBase);
 }
 
 IDBRequest*
 IndexedDBRequestChildBase::GetRequest() const
 {
-  return mHelper ? mHelper->GetRequest() : NULL;
+  return mHelper ? mHelper->GetRequest() : nullptr;
+}
+
+void
+IndexedDBRequestChildBase::Disconnect()
+{
+  if (mHelper) {
+    IDBRequest* request = mHelper->GetRequest();
+
+    if (request->IsPending()) {
+      request->SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+      IDBTransaction* transaction = mHelper->GetTransaction();
+      if (transaction) {
+        transaction->OnRequestDisconnected();
+      }
+    }
+  }
 }
 
 bool
 IndexedDBRequestChildBase::Recv__delete__(const ResponseValue& aResponse)
 {
   MOZ_NOT_REACHED("This should be overridden!");
   return false;
 }
--- a/dom/indexedDB/ipc/IndexedDBChild.h
+++ b/dom/indexedDB/ipc/IndexedDBChild.h
@@ -34,29 +34,36 @@ class IDBTransactionListener;
  * IndexedDBChild
  ******************************************************************************/
 
 class IndexedDBChild : public PIndexedDBChild
 {
   IDBFactory* mFactory;
   nsCString mASCIIOrigin;
 
+#ifdef DEBUG
+  bool mDisconnected;
+#endif
+
 public:
   IndexedDBChild(const nsCString& aASCIIOrigin);
   virtual ~IndexedDBChild();
 
   const nsCString&
   ASCIIOrigin() const
   {
     return mASCIIOrigin;
   }
 
   void
   SetFactory(IDBFactory* aFactory);
 
+  void
+  Disconnect();
+
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PIndexedDBDatabaseChild*
   AllocPIndexedDBDatabase(const nsString& aName, const uint64_t& aVersion)
                           MOZ_OVERRIDE;
 
@@ -90,16 +97,19 @@ class IndexedDBDatabaseChild : public PI
 
 public:
   IndexedDBDatabaseChild(const nsString& aName, uint64_t aVersion);
   virtual ~IndexedDBDatabaseChild();
 
   void
   SetRequest(IDBOpenDBRequest* aRequest);
 
+  void
+  Disconnect();
+
 protected:
   bool
   EnsureDatabase(IDBOpenDBRequest* aRequest,
                  const DatabaseInfoGuts& aDBInfo,
                  const InfallibleTArray<ObjectStoreInfoGuts>& aOSInfo);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
@@ -153,16 +163,19 @@ public:
   SetTransaction(IDBTransaction* aTransaction);
 
   IDBTransaction*
   GetTransaction() const
   {
     return mTransaction;
   }
 
+  void
+  Disconnect();
+
 protected:
   void
   FireCompleteEvent(nsresult aRv);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
@@ -183,16 +196,19 @@ protected:
 class IndexedDBObjectStoreChild : public PIndexedDBObjectStoreChild
 {
   IDBObjectStore* mObjectStore;
 
 public:
   IndexedDBObjectStoreChild(IDBObjectStore* aObjectStore);
   virtual ~IndexedDBObjectStoreChild();
 
+  void
+  Disconnect();
+
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBCursorConstructor(
                               PIndexedDBCursorChild* aActor,
                               const ObjectStoreCursorConstructorParams& aParams)
@@ -225,16 +241,19 @@ protected:
 class IndexedDBIndexChild : public PIndexedDBIndexChild
 {
   IDBIndex* mIndex;
 
 public:
   IndexedDBIndexChild(IDBIndex* aIndex);
   virtual ~IndexedDBIndexChild();
 
+  void
+  Disconnect();
+
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBCursorConstructor(PIndexedDBCursorChild* aActor,
                                   const IndexCursorConstructorParams& aParams)
                                   MOZ_OVERRIDE;
@@ -271,16 +290,19 @@ public:
   SetCursor(IDBCursor* aCursor);
 
   already_AddRefed<IDBCursor>
   ForgetStrongCursor()
   {
     return mStrongCursor.forget();
   }
 
+  void
+  Disconnect();
+
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PIndexedDBRequestChild*
   AllocPIndexedDBRequest(const CursorRequestParams& aParams) MOZ_OVERRIDE;
 
   virtual bool
@@ -295,16 +317,19 @@ class IndexedDBRequestChildBase : public
 {
 protected:
   nsRefPtr<AsyncConnectionHelper> mHelper;
 
 public:
   IDBRequest*
   GetRequest() const;
 
+  void
+  Disconnect();
+
 protected:
   IndexedDBRequestChildBase(AsyncConnectionHelper* aHelper);
   virtual ~IndexedDBRequestChildBase();
 
   virtual bool
   Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE;
 };
 
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -9,16 +9,17 @@
 #include "nsIDOMFile.h"
 #include "nsIDOMEvent.h"
 #include "nsIIDBVersionChangeEvent.h"
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 
 #include "mozilla/AppProcessPermissions.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/unused.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "nsContentUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
@@ -77,23 +78,24 @@ IndexedDBParent::IndexedDBParent(TabPare
 IndexedDBParent::~IndexedDBParent()
 {
   MOZ_COUNT_DTOR(IndexedDBParent);
 }
 
 void
 IndexedDBParent::Disconnect()
 {
+  MOZ_ASSERT(!mDisconnected);
+
   mDisconnected = true;
 
-  const InfallibleTArray<PIndexedDBDatabaseParent*>& dbs =
+  const InfallibleTArray<PIndexedDBDatabaseParent*>& databases =
     ManagedPIndexedDBDatabaseParent();
-
-  for (uint32_t i = 0; i < dbs.Length(); ++i) {
-    static_cast<IndexedDBDatabaseParent*>(dbs[i])->Disconnect();
+  for (uint32_t i = 0; i < databases.Length(); ++i) {
+    static_cast<IndexedDBDatabaseParent*>(databases[i])->Disconnect();
   }
 }
 
 bool
 IndexedDBParent::CheckReadPermission(const nsAString& aDatabaseName)
 {
   NS_NAMED_LITERAL_CSTRING(permission, PERMISSION_SUFFIX_READ);
   return CheckPermissionInternal(aDatabaseName, permission);
@@ -140,22 +142,29 @@ IndexedDBParent::ActorDestroy(ActorDestr
 }
 
 bool
 IndexedDBParent::RecvPIndexedDBDatabaseConstructor(
                                                PIndexedDBDatabaseParent* aActor,
                                                const nsString& aName,
                                                const uint64_t& aVersion)
 {
-  MOZ_ASSERT(mFactory);
-
   if (!CheckReadPermission(aName)) {
     return false;
   }
 
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mFactory) {
+    return true;
+  }
+
   nsRefPtr<IDBOpenDBRequest> request;
   nsresult rv =
     mFactory->OpenCommon(aName, aVersion, false, nullptr,
                          getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
 
   IndexedDBDatabaseParent* actor =
     static_cast<IndexedDBDatabaseParent*>(aActor);
@@ -166,22 +175,29 @@ IndexedDBParent::RecvPIndexedDBDatabaseC
   return true;
 }
 
 bool
 IndexedDBParent::RecvPIndexedDBDeleteDatabaseRequestConstructor(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor,
                                   const nsString& aName)
 {
-  MOZ_ASSERT(mFactory);
-
   if (!CheckWritePermission(aName)) {
     return false;
   }
 
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mFactory) {
+    return true;
+  }
+
   IndexedDBDeleteDatabaseRequestParent* actor =
     static_cast<IndexedDBDeleteDatabaseRequestParent*>(aActor);
 
   nsRefPtr<IDBOpenDBRequest> request;
 
   nsresult rv =
     mFactory->OpenCommon(aName, 0, true, nullptr, getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
@@ -191,39 +207,29 @@ IndexedDBParent::RecvPIndexedDBDeleteDat
 
   return true;
 }
 
 PIndexedDBDatabaseParent*
 IndexedDBParent::AllocPIndexedDBDatabase(const nsString& aName,
                                          const uint64_t& aVersion)
 {
-  if (!mFactory) {
-    // This can happen if the child process dies before we set the factory.
-    return nullptr;
-  }
-
   return new IndexedDBDatabaseParent();
 }
 
 bool
 IndexedDBParent::DeallocPIndexedDBDatabase(PIndexedDBDatabaseParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 PIndexedDBDeleteDatabaseRequestParent*
 IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequest(const nsString& aName)
 {
-  if (!mFactory) {
-    // This can happen if the child process dies before we set the factory.
-    return nullptr;
-  }
-
   return new IndexedDBDeleteDatabaseRequestParent(mFactory);
 }
 
 bool
 IndexedDBParent::DeallocPIndexedDBDeleteDatabaseRequest(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor)
 {
   delete aActor;
@@ -273,18 +279,18 @@ IndexedDBDatabaseParent::SetOpenRequest(
   return NS_OK;
 }
 
 nsresult
 IndexedDBDatabaseParent::HandleEvent(nsIDOMEvent* aEvent)
 {
   MOZ_ASSERT(aEvent);
 
-  if (Manager() &&
-      static_cast<IndexedDBParent*>(Manager())->IsDisconnected()) {
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this event.
     return NS_OK;
   }
 
   nsString type;
   nsresult rv = aEvent->GetType(type);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMEventTarget> target;
@@ -312,34 +318,45 @@ IndexedDBDatabaseParent::HandleEvent(nsI
   MOZ_NOT_REACHED("Unexpected message!");
   return NS_ERROR_UNEXPECTED;
 }
 
 void
 IndexedDBDatabaseParent::Disconnect()
 {
   if (mDatabase) {
-    mDatabase->DisconnectFromActor();
+    mDatabase->DisconnectFromActorParent();
   }
 }
 
 bool
 IndexedDBDatabaseParent::CheckWritePermission(const nsAString& aDatabaseName)
 {
   IndexedDBParent* manager = static_cast<IndexedDBParent*>(Manager());
   MOZ_ASSERT(manager);
 
   return manager->CheckWritePermission(aDatabaseName);
 }
 
+void
+IndexedDBDatabaseParent::Invalidate()
+{
+  MOZ_ASSERT(mDatabase);
+
+  if (!IsDisconnected()) {
+    mozilla::unused << SendInvalidate();
+  }
+}
+
 nsresult
 IndexedDBDatabaseParent::HandleRequestEvent(nsIDOMEvent* aEvent,
                                             const nsAString& aType)
 {
   MOZ_ASSERT(mOpenRequest);
+  MOZ_ASSERT(!IsDisconnected());
 
   nsresult rv;
 
   if (aType.EqualsLiteral(ERROR_EVT_STR)) {
     nsRefPtr<IDBOpenDBRequest> request;
     mOpenRequest.swap(request);
 
     rv = request->GetErrorCode();
@@ -505,16 +522,17 @@ IndexedDBDatabaseParent::HandleRequestEv
 
 nsresult
 IndexedDBDatabaseParent::HandleDatabaseEvent(nsIDOMEvent* aEvent,
                                              const nsAString& aType)
 {
   MOZ_ASSERT(mDatabase);
   MOZ_ASSERT(!aType.EqualsLiteral(ERROR_EVT_STR),
              "Should never get error events in the parent process!");
+  MOZ_ASSERT(!IsDisconnected());
 
   nsresult rv;
 
   if (aType.EqualsLiteral(VERSIONCHANGE_EVT_STR)) {
     JSContext* cx = nsContentUtils::GetSafeJSContext();
     NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsIIDBVersionChangeEvent> changeEvent = do_QueryInterface(aEvent);
@@ -557,40 +575,58 @@ IndexedDBDatabaseParent::ActorDestroy(Ac
   }
 }
 
 bool
 IndexedDBDatabaseParent::RecvClose(const bool& aUnlinked)
 {
   MOZ_ASSERT(mDatabase);
 
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
   mDatabase->CloseInternal(aUnlinked);
   return true;
 }
 
 bool
 IndexedDBDatabaseParent::RecvPIndexedDBTransactionConstructor(
                                             PIndexedDBTransactionParent* aActor,
                                             const TransactionParams& aParams)
 {
   MOZ_ASSERT(aParams.type() ==
              TransactionParams::TNormalTransactionParams);
   MOZ_ASSERT(!mOpenRequest);
-  MOZ_ASSERT(mDatabase);
+
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mDatabase) {
+    return true;
+  }
 
   IndexedDBTransactionParent* actor =
     static_cast<IndexedDBTransactionParent*>(aActor);
 
   const NormalTransactionParams& params = aParams.get_NormalTransactionParams();
 
   if (params.mode() != IDBTransaction::READ_ONLY &&
       !CheckWritePermission(mDatabase->Name())) {
     return false;
   }
 
+  if (mDatabase->IsClosed()) {
+    // If the window was navigated then we won't be able to do anything here.
+    return true;
+  }
+
   nsTArray<nsString> storesToOpen;
   storesToOpen.AppendElements(params.names());
 
   nsRefPtr<IDBTransaction> transaction =
     IDBTransaction::Create(mDatabase, storesToOpen, params.mode(), false);
   NS_ENSURE_TRUE(transaction, false);
 
   nsresult rv = actor->SetTransaction(transaction);
@@ -598,21 +634,16 @@ IndexedDBDatabaseParent::RecvPIndexedDBT
 
   return true;
 }
 
 PIndexedDBTransactionParent*
 IndexedDBDatabaseParent::AllocPIndexedDBTransaction(
                                                const TransactionParams& aParams)
 {
-  if (!mDatabase) {
-    // This can happen if the child process dies before we set the database.
-    return nullptr;
-  }
-
   MOZ_ASSERT(aParams.type() ==
              TransactionParams::TNormalTransactionParams);
   return new IndexedDBTransactionParent();
 }
 
 bool
 IndexedDBDatabaseParent::DeallocPIndexedDBTransaction(
                                             PIndexedDBTransactionParent* aActor)
@@ -660,16 +691,23 @@ IndexedDBTransactionParent::SetTransacti
 
   mTransaction = aTransaction;
   return NS_OK;
 }
 
 nsresult
 IndexedDBTransactionParent::HandleEvent(nsIDOMEvent* aEvent)
 {
+  MOZ_ASSERT(aEvent);
+
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this event.
+    return NS_OK;
+  }
+
   nsString type;
   nsresult rv = aEvent->GetType(type);
   NS_ENSURE_SUCCESS(rv, rv);
 
   CompleteParams params;
 
   if (type.EqualsLiteral(COMPLETE_EVT_STR)) {
     params = CompleteResult();
@@ -718,26 +756,37 @@ IndexedDBTransactionParent::ActorDestroy
     mTransaction->SetActor(static_cast<IndexedDBTransactionParent*>(NULL));
   }
 }
 
 bool
 IndexedDBTransactionParent::RecvAbort(const nsresult& aAbortCode)
 {
   MOZ_ASSERT(mTransaction);
+
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
   mTransaction->Abort(aAbortCode);
   return true;
 }
 
 bool
 IndexedDBTransactionParent::RecvAllRequestsFinished()
 {
   MOZ_ASSERT(mTransaction);
   MOZ_ASSERT(mArtificialRequestCount);
 
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
   mTransaction->OnRequestFinished();
   mArtificialRequestCount = false;
 
   return true;
 }
 
 bool
 IndexedDBTransactionParent::RecvDeleteObjectStore(const nsString& aName)
@@ -746,17 +795,24 @@ IndexedDBTransactionParent::RecvDeleteOb
   return false;
 }
 
 bool
 IndexedDBTransactionParent::RecvPIndexedDBObjectStoreConstructor(
                                     PIndexedDBObjectStoreParent* aActor,
                                     const ObjectStoreConstructorParams& aParams)
 {
-  MOZ_ASSERT(mTransaction);
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mTransaction) {
+    return true;
+  }
 
   IndexedDBObjectStoreParent* actor =
     static_cast<IndexedDBObjectStoreParent*>(aActor);
 
   if (aParams.type() ==
       ObjectStoreConstructorParams::TGetObjectStoreParams) {
     const GetObjectStoreParams& params = aParams.get_GetObjectStoreParams();
     const nsString& name = params.name();
@@ -786,21 +842,16 @@ IndexedDBTransactionParent::RecvPIndexed
   MOZ_NOT_REACHED("Unknown param type!");
   return false;
 }
 
 PIndexedDBObjectStoreParent*
 IndexedDBTransactionParent::AllocPIndexedDBObjectStore(
                                     const ObjectStoreConstructorParams& aParams)
 {
-  if (!mTransaction) {
-    // This can happen if the child process dies before we set the transaction.
-    return nullptr;
-  }
-
   return new IndexedDBObjectStoreParent();
 }
 
 bool
 IndexedDBTransactionParent::DeallocPIndexedDBObjectStore(
                                             PIndexedDBObjectStoreParent* aActor)
 {
   delete aActor;
@@ -822,17 +873,33 @@ IndexedDBVersionChangeTransactionParent:
 {
   MOZ_COUNT_DTOR(IndexedDBVersionChangeTransactionParent);
 }
 
 bool
 IndexedDBVersionChangeTransactionParent::RecvDeleteObjectStore(
                                                           const nsString& aName)
 {
-  MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
+  MOZ_ASSERT(!mTransaction ||
+             mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
+
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mTransaction) {
+    return true;
+  }
+
+  if (mTransaction->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return true;
+  }
 
   IDBDatabase* db = mTransaction->Database();
   MOZ_ASSERT(db);
 
   nsresult rv;
 
   {
     AutoSetCurrentTransaction asct(mTransaction);
@@ -845,17 +912,30 @@ IndexedDBVersionChangeTransactionParent:
   return true;
 }
 
 bool
 IndexedDBVersionChangeTransactionParent::RecvPIndexedDBObjectStoreConstructor(
                                     PIndexedDBObjectStoreParent* aActor,
                                     const ObjectStoreConstructorParams& aParams)
 {
-  MOZ_ASSERT(mTransaction);
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mTransaction) {
+    return true;
+  }
+
+  if (mTransaction->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return true;
+  }
 
   IndexedDBObjectStoreParent* actor =
     static_cast<IndexedDBObjectStoreParent*>(aActor);
 
   if (aParams.type() ==
       ObjectStoreConstructorParams::TCreateObjectStoreParams) {
     MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
 
@@ -889,48 +969,123 @@ IndexedDBVersionChangeTransactionParent:
     IndexedDBTransactionParent::RecvPIndexedDBObjectStoreConstructor(aActor,
                                                                      aParams);
 }
 
 PIndexedDBObjectStoreParent*
 IndexedDBVersionChangeTransactionParent::AllocPIndexedDBObjectStore(
                                     const ObjectStoreConstructorParams& aParams)
 {
-  if (!mTransaction) {
-    // This can happen if the child process dies before we set the transaction.
-    return nullptr;
-  }
-
   if (aParams.type() ==
       ObjectStoreConstructorParams::TCreateObjectStoreParams ||
       mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
     return new IndexedDBVersionChangeObjectStoreParent();
   }
 
   return IndexedDBTransactionParent::AllocPIndexedDBObjectStore(aParams);
 }
 
 /*******************************************************************************
+ * IndexedDBCursorParent
+ ******************************************************************************/
+
+IndexedDBCursorParent::IndexedDBCursorParent(IDBCursor* aCursor)
+: mCursor(aCursor)
+{
+  MOZ_COUNT_CTOR(IndexedDBCursorParent);
+  MOZ_ASSERT(aCursor);
+  aCursor->SetActor(this);
+}
+
+IndexedDBCursorParent::~IndexedDBCursorParent()
+{
+  MOZ_COUNT_DTOR(IndexedDBCursorParent);
+}
+
+bool
+IndexedDBCursorParent::IsDisconnected() const
+{
+  MOZ_ASSERT(mCursor);
+  return mCursor->Transaction()->GetActorParent()->IsDisconnected();
+}
+
+void
+IndexedDBCursorParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MOZ_ASSERT(mCursor);
+  mCursor->SetActor(static_cast<IndexedDBCursorParent*>(NULL));
+}
+
+bool
+IndexedDBCursorParent::RecvPIndexedDBRequestConstructor(
+                                             PIndexedDBRequestParent* aActor,
+                                             const CursorRequestParams& aParams)
+{
+  MOZ_ASSERT(mCursor);
+
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  IndexedDBCursorRequestParent* actor =
+    static_cast<IndexedDBCursorRequestParent*>(aActor);
+
+  if (mCursor->Transaction()->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  }
+
+  switch (aParams.type()) {
+    case CursorRequestParams::TContinueParams:
+      return actor->Continue(aParams.get_ContinueParams());
+
+    default:
+      MOZ_NOT_REACHED("Unknown type!");
+      return false;
+  }
+
+  MOZ_NOT_REACHED("Should never get here!");
+  return false;
+}
+
+PIndexedDBRequestParent*
+IndexedDBCursorParent::AllocPIndexedDBRequest(
+                                             const CursorRequestParams& aParams)
+{
+  MOZ_ASSERT(mCursor);
+  return new IndexedDBCursorRequestParent(mCursor, aParams.type());
+}
+
+bool
+IndexedDBCursorParent::DeallocPIndexedDBRequest(PIndexedDBRequestParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+/*******************************************************************************
  * IndexedDBObjectStoreParent
  ******************************************************************************/
 
 IndexedDBObjectStoreParent::IndexedDBObjectStoreParent()
 {
   MOZ_COUNT_CTOR(IndexedDBObjectStoreParent);
 }
 
 IndexedDBObjectStoreParent::~IndexedDBObjectStoreParent()
 {
   MOZ_COUNT_DTOR(IndexedDBObjectStoreParent);
 }
 
 void
 IndexedDBObjectStoreParent::SetObjectStore(IDBObjectStore* aObjectStore)
 {
-  MOZ_ASSERT(aObjectStore);
+  // Sadly can't assert aObjectStore here...
   MOZ_ASSERT(!mObjectStore);
 
   mObjectStore = aObjectStore;
 }
 
 void
 IndexedDBObjectStoreParent::ActorDestroy(ActorDestroyReason aWhy)
 {
@@ -946,21 +1101,34 @@ IndexedDBObjectStoreParent::RecvDeleteIn
   return false;
 }
 
 bool
 IndexedDBObjectStoreParent::RecvPIndexedDBRequestConstructor(
                                         PIndexedDBRequestParent* aActor,
                                         const ObjectStoreRequestParams& aParams)
 {
-  MOZ_ASSERT(mObjectStore);
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mObjectStore) {
+    return true;
+  }
 
   IndexedDBObjectStoreRequestParent* actor =
     static_cast<IndexedDBObjectStoreRequestParent*>(aActor);
 
+  if (mObjectStore->Transaction()->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  }
+
   switch (aParams.type()) {
     case ObjectStoreRequestParams::TGetParams:
       return actor->Get(aParams.get_GetParams());
 
     case ObjectStoreRequestParams::TGetAllParams:
       return actor->GetAll(aParams.get_GetAllParams());
 
     case ObjectStoreRequestParams::TAddParams:
@@ -990,17 +1158,24 @@ IndexedDBObjectStoreParent::RecvPIndexed
   return false;
 }
 
 bool
 IndexedDBObjectStoreParent::RecvPIndexedDBIndexConstructor(
                                           PIndexedDBIndexParent* aActor,
                                           const IndexConstructorParams& aParams)
 {
-  MOZ_ASSERT(mObjectStore);
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mObjectStore) {
+    return true;
+  }
 
   IndexedDBIndexParent* actor = static_cast<IndexedDBIndexParent*>(aActor);
 
   if (aParams.type() == IndexConstructorParams::TGetIndexParams) {
     const GetIndexParams& params = aParams.get_GetIndexParams();
     const nsString& name = params.name();
 
     nsRefPtr<IDBIndex> index;
@@ -1026,41 +1201,31 @@ IndexedDBObjectStoreParent::RecvPIndexed
   MOZ_NOT_REACHED("Unknown param type!");
   return false;
 }
 
 PIndexedDBRequestParent*
 IndexedDBObjectStoreParent::AllocPIndexedDBRequest(
                                         const ObjectStoreRequestParams& aParams)
 {
-  if (!mObjectStore) {
-    // This can happen if the child process dies before we set the objectStore.
-    return nullptr;
-  }
-
   return new IndexedDBObjectStoreRequestParent(mObjectStore, aParams.type());
 }
 
 bool
 IndexedDBObjectStoreParent::DeallocPIndexedDBRequest(
                                                 PIndexedDBRequestParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 PIndexedDBIndexParent*
 IndexedDBObjectStoreParent::AllocPIndexedDBIndex(
                                           const IndexConstructorParams& aParams)
 {
-  if (!mObjectStore) {
-    // This can happen if the child process dies before we set the objectStore.
-    return nullptr;
-  }
-
   return new IndexedDBIndexParent();
 }
 
 bool
 IndexedDBObjectStoreParent::DeallocPIndexedDBIndex(
                                                   PIndexedDBIndexParent* aActor)
 {
   delete aActor;
@@ -1097,19 +1262,35 @@ IndexedDBVersionChangeObjectStoreParent:
   ~IndexedDBVersionChangeObjectStoreParent()
 {
   MOZ_COUNT_DTOR(IndexedDBVersionChangeObjectStoreParent);
 }
 
 bool
 IndexedDBVersionChangeObjectStoreParent::RecvDeleteIndex(const nsString& aName)
 {
-  MOZ_ASSERT(mObjectStore->Transaction()->GetMode() ==
+  MOZ_ASSERT(!mObjectStore ||
+             mObjectStore->Transaction()->GetMode() ==
              IDBTransaction::VERSION_CHANGE);
 
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mObjectStore) {
+    return true;
+  }
+
+  if (mObjectStore->Transaction()->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return true;
+  }
+
   nsresult rv;
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
 
     rv = mObjectStore->DeleteIndex(aName);
   }
 
@@ -1118,17 +1299,30 @@ IndexedDBVersionChangeObjectStoreParent:
   return true;
 }
 
 bool
 IndexedDBVersionChangeObjectStoreParent::RecvPIndexedDBIndexConstructor(
                                           PIndexedDBIndexParent* aActor,
                                           const IndexConstructorParams& aParams)
 {
-  MOZ_ASSERT(mObjectStore);
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mObjectStore) {
+    return true;
+  }
+
+  if (mObjectStore->Transaction()->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return true;
+  }
 
   IndexedDBIndexParent* actor = static_cast<IndexedDBIndexParent*>(aActor);
 
   if (aParams.type() == IndexConstructorParams::TCreateIndexParams) {
     MOZ_ASSERT(mObjectStore->Transaction()->GetMode() ==
                IDBTransaction::VERSION_CHANGE);
 
     const CreateIndexParams& params = aParams.get_CreateIndexParams();
@@ -1186,21 +1380,34 @@ IndexedDBIndexParent::ActorDestroy(Actor
   }
 }
 
 bool
 IndexedDBIndexParent::RecvPIndexedDBRequestConstructor(
                                               PIndexedDBRequestParent* aActor,
                                               const IndexRequestParams& aParams)
 {
-  MOZ_ASSERT(mIndex);
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this request.
+    return true;
+  }
+
+  if (!mIndex) {
+    return true;
+  }
 
   IndexedDBIndexRequestParent* actor =
     static_cast<IndexedDBIndexRequestParent*>(aActor);
 
+  if (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) {
+    // If we've invalidated this database in the parent then we should bail out
+    // now to avoid logic problems that could force-kill the child.
+    return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  }
+
   switch (aParams.type()) {
     case IndexRequestParams::TGetParams:
       return actor->Get(aParams.get_GetParams());
 
     case IndexRequestParams::TGetKeyParams:
       return actor->GetKey(aParams.get_GetKeyParams());
 
     case IndexRequestParams::TGetAllParams:
@@ -1225,21 +1432,16 @@ IndexedDBIndexParent::RecvPIndexedDBRequ
 
   MOZ_NOT_REACHED("Should never get here!");
   return false;
 }
 
 PIndexedDBRequestParent*
 IndexedDBIndexParent::AllocPIndexedDBRequest(const IndexRequestParams& aParams)
 {
-  if (!mIndex) {
-    // This can happen if the child process dies before we set the index.
-    return nullptr;
-  }
-
   return new IndexedDBIndexRequestParent(mIndex, aParams.type());
 }
 
 bool
 IndexedDBIndexParent::DeallocPIndexedDBRequest(PIndexedDBRequestParent* aActor)
 {
   delete aActor;
   return true;
@@ -1256,78 +1458,16 @@ IndexedDBIndexParent::AllocPIndexedDBCur
 bool
 IndexedDBIndexParent::DeallocPIndexedDBCursor(PIndexedDBCursorParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 /*******************************************************************************
- * IndexedDBCursorParent
- ******************************************************************************/
-
-IndexedDBCursorParent::IndexedDBCursorParent(IDBCursor* aCursor)
-: mCursor(aCursor)
-{
-  MOZ_COUNT_CTOR(IndexedDBCursorParent);
-  MOZ_ASSERT(aCursor);
-  aCursor->SetActor(this);
-}
-
-IndexedDBCursorParent::~IndexedDBCursorParent()
-{
-  MOZ_COUNT_DTOR(IndexedDBCursorParent);
-}
-
-void
-IndexedDBCursorParent::ActorDestroy(ActorDestroyReason aWhy)
-{
-  MOZ_ASSERT(mCursor);
-  mCursor->SetActor(static_cast<IndexedDBCursorParent*>(NULL));
-}
-
-bool
-IndexedDBCursorParent::RecvPIndexedDBRequestConstructor(
-                                             PIndexedDBRequestParent* aActor,
-                                             const CursorRequestParams& aParams)
-{
-  MOZ_ASSERT(mCursor);
-
-  IndexedDBCursorRequestParent* actor =
-    static_cast<IndexedDBCursorRequestParent*>(aActor);
-
-  switch (aParams.type()) {
-    case CursorRequestParams::TContinueParams:
-      return actor->Continue(aParams.get_ContinueParams());
-
-    default:
-      MOZ_NOT_REACHED("Unknown type!");
-      return false;
-  }
-
-  MOZ_NOT_REACHED("Should never get here!");
-  return false;
-}
-
-PIndexedDBRequestParent*
-IndexedDBCursorParent::AllocPIndexedDBRequest(
-                                             const CursorRequestParams& aParams)
-{
-  MOZ_ASSERT(mCursor);
-  return new IndexedDBCursorRequestParent(mCursor, aParams.type());
-}
-
-bool
-IndexedDBCursorParent::DeallocPIndexedDBRequest(PIndexedDBRequestParent* aActor)
-{
-  delete aActor;
-  return true;
-}
-
-/*******************************************************************************
  * IndexedDBRequestParentBase
  ******************************************************************************/
 
 IndexedDBRequestParentBase::IndexedDBRequestParentBase()
 {
   MOZ_COUNT_CTOR(IndexedDBRequestParentBase);
 }
 
@@ -1349,51 +1489,61 @@ IndexedDBRequestParentBase::ActorDestroy
  ******************************************************************************/
 
 IndexedDBObjectStoreRequestParent::IndexedDBObjectStoreRequestParent(
                                                    IDBObjectStore* aObjectStore,
                                                    RequestType aRequestType)
 : mObjectStore(aObjectStore), mRequestType(aRequestType)
 {
   MOZ_COUNT_CTOR(IndexedDBObjectStoreRequestParent);
-  MOZ_ASSERT(aObjectStore);
+  // Sadly can't assert aObjectStore here...
   MOZ_ASSERT(aRequestType > ParamsUnionType::T__None &&
              aRequestType <= ParamsUnionType::T__Last);
 }
 
 IndexedDBObjectStoreRequestParent::~IndexedDBObjectStoreRequestParent()
 {
   MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestParent);
 }
 
 void
 IndexedDBObjectStoreRequestParent::ConvertBlobActors(
                                   const InfallibleTArray<PBlobParent*>& aActors,
                                   nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs)
 {
   MOZ_ASSERT(aBlobs.IsEmpty());
+  MOZ_ASSERT(mObjectStore);
 
   if (!aActors.IsEmpty()) {
     // Walk the chain to get to ContentParent.
     MOZ_ASSERT(mObjectStore->Transaction()->Database()->GetContentParent());
 
     uint32_t length = aActors.Length();
     aBlobs.SetCapacity(length);
 
     for (uint32_t index = 0; index < length; index++) {
       BlobParent* actor = static_cast<BlobParent*>(aActors[index]);
       aBlobs.AppendElement(actor->GetBlob());
     }
   }
 }
 
 bool
+IndexedDBObjectStoreRequestParent::IsDisconnected()
+{
+  MOZ_ASSERT(mObjectStore);
+  MOZ_ASSERT(mObjectStore->GetActorParent());
+  return mObjectStore->GetActorParent()->IsDisconnected();
+}
+
+bool
 IndexedDBObjectStoreRequestParent::Get(const GetParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams);
+  MOZ_ASSERT(mObjectStore);
 
   nsRefPtr<IDBRequest> request;
 
   nsRefPtr<IDBKeyRange> keyRange =
     IDBKeyRange::FromSerializedKeyRange(aParams.keyRange());
   MOZ_ASSERT(keyRange);
 
   {
@@ -1401,23 +1551,25 @@ IndexedDBObjectStoreRequestParent::Get(c
 
     nsresult rv = mObjectStore->GetInternal(keyRange, nullptr,
                                             getter_AddRefs(request));
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   request->SetActor(this);
   mRequest.swap(request);
+
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::GetAll(const GetAllParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams);
+  MOZ_ASSERT(mObjectStore);
 
   nsRefPtr<IDBRequest> request;
 
   const ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
@@ -1448,16 +1600,17 @@ IndexedDBObjectStoreRequestParent::GetAl
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Add(const AddParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams);
+  MOZ_ASSERT(mObjectStore);
 
   ipc::AddPutParams params = aParams.commonParams();
 
   nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
   ConvertBlobActors(params.blobsParent(), blobs);
 
   nsRefPtr<IDBRequest> request;
 
@@ -1475,16 +1628,17 @@ IndexedDBObjectStoreRequestParent::Add(c
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Put(const PutParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams);
+  MOZ_ASSERT(mObjectStore);
 
   ipc::AddPutParams params = aParams.commonParams();
 
   nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
   ConvertBlobActors(params.blobsParent(), blobs);
 
   nsRefPtr<IDBRequest> request;
 
@@ -1502,16 +1656,17 @@ IndexedDBObjectStoreRequestParent::Put(c
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Delete(const DeleteParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TDeleteParams);
+  MOZ_ASSERT(mObjectStore);
 
   nsRefPtr<IDBRequest> request;
 
   nsRefPtr<IDBKeyRange> keyRange =
     IDBKeyRange::FromSerializedKeyRange(aParams.keyRange());
   MOZ_ASSERT(keyRange);
 
   {
@@ -1526,16 +1681,17 @@ IndexedDBObjectStoreRequestParent::Delet
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Clear(const ClearParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TClearParams);
+  MOZ_ASSERT(mObjectStore);
 
   nsRefPtr<IDBRequest> request;
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
 
     nsresult rv = mObjectStore->ClearInternal(nullptr, getter_AddRefs(request));
     NS_ENSURE_SUCCESS(rv, false);
@@ -1545,16 +1701,17 @@ IndexedDBObjectStoreRequestParent::Clear
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Count(const CountParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams);
+  MOZ_ASSERT(mObjectStore);
 
   const ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
     case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::TKeyRange:
@@ -1584,16 +1741,17 @@ IndexedDBObjectStoreRequestParent::Count
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::OpenCursor(const OpenCursorParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams);
+  MOZ_ASSERT(mObjectStore);
 
   const ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
     case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::TKeyRange:
@@ -1632,30 +1790,39 @@ IndexedDBObjectStoreRequestParent::OpenC
  ******************************************************************************/
 
 IndexedDBIndexRequestParent::IndexedDBIndexRequestParent(
                                                        IDBIndex* aIndex,
                                                        RequestType aRequestType)
 : mIndex(aIndex), mRequestType(aRequestType)
 {
   MOZ_COUNT_CTOR(IndexedDBIndexRequestParent);
-  MOZ_ASSERT(aIndex);
+  // Sadly can't assert aIndex here...
   MOZ_ASSERT(aRequestType > ParamsUnionType::T__None &&
              aRequestType <= ParamsUnionType::T__Last);
 }
 
 IndexedDBIndexRequestParent::~IndexedDBIndexRequestParent()
 {
   MOZ_COUNT_DTOR(IndexedDBIndexRequestParent);
 }
 
 bool
+IndexedDBIndexRequestParent::IsDisconnected()
+{
+  MOZ_ASSERT(mIndex);
+  MOZ_ASSERT(mIndex->GetActorParent());
+  return mIndex->GetActorParent()->IsDisconnected();
+}
+
+bool
 IndexedDBIndexRequestParent::Get(const GetParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams);
+  MOZ_ASSERT(mIndex);
 
   nsRefPtr<IDBRequest> request;
 
   nsRefPtr<IDBKeyRange> keyRange =
     IDBKeyRange::FromSerializedKeyRange(aParams.keyRange());
   MOZ_ASSERT(keyRange);
 
   {
@@ -1670,16 +1837,17 @@ IndexedDBIndexRequestParent::Get(const G
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBIndexRequestParent::GetKey(const GetKeyParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetKeyParams);
+  MOZ_ASSERT(mIndex);
 
   nsRefPtr<IDBRequest> request;
 
   nsRefPtr<IDBKeyRange> keyRange =
     IDBKeyRange::FromSerializedKeyRange(aParams.keyRange());
   MOZ_ASSERT(keyRange);
 
   {
@@ -1694,16 +1862,17 @@ IndexedDBIndexRequestParent::GetKey(cons
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBIndexRequestParent::GetAll(const GetAllParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams);
+  MOZ_ASSERT(mIndex);
 
   nsRefPtr<IDBRequest> request;
 
   const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
@@ -1733,16 +1902,17 @@ IndexedDBIndexRequestParent::GetAll(cons
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBIndexRequestParent::GetAllKeys(const GetAllKeysParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams);
+  MOZ_ASSERT(mIndex);
 
   nsRefPtr<IDBRequest> request;
 
   const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
@@ -1772,16 +1942,17 @@ IndexedDBIndexRequestParent::GetAllKeys(
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBIndexRequestParent::Count(const CountParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams);
+  MOZ_ASSERT(mIndex);
 
   const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
     case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
@@ -1811,16 +1982,17 @@ IndexedDBIndexRequestParent::Count(const
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBIndexRequestParent::OpenCursor(const OpenCursorParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams);
+  MOZ_ASSERT(mIndex);
 
   const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
     case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
@@ -1853,16 +2025,17 @@ IndexedDBIndexRequestParent::OpenCursor(
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBIndexRequestParent::OpenKeyCursor(const OpenKeyCursorParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams);
+  MOZ_ASSERT(mIndex);
 
   const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
     aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
     case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
@@ -1912,18 +2085,27 @@ IndexedDBCursorRequestParent::IndexedDBC
 }
 
 IndexedDBCursorRequestParent::~IndexedDBCursorRequestParent()
 {
   MOZ_COUNT_DTOR(IndexedDBCursorRequestParent);
 }
 
 bool
+IndexedDBCursorRequestParent::IsDisconnected()
+{
+  MOZ_ASSERT(mCursor);
+  MOZ_ASSERT(mCursor->GetActorParent());
+  return mCursor->GetActorParent()->IsDisconnected();
+}
+
+bool
 IndexedDBCursorRequestParent::Continue(const ContinueParams& aParams)
 {
+  MOZ_ASSERT(mCursor);
   MOZ_ASSERT(mRequestType == ParamsUnionType::TContinueParams);
 
   {
     AutoSetCurrentTransaction asct(mCursor->Transaction());
 
     nsresult rv = mCursor->ContinueInternal(aParams.key(), aParams.count());
     NS_ENSURE_SUCCESS(rv, false);
   }
@@ -1950,23 +2132,23 @@ IndexedDBDeleteDatabaseRequestParent::In
 IndexedDBDeleteDatabaseRequestParent::~IndexedDBDeleteDatabaseRequestParent()
 {
   MOZ_COUNT_DTOR(IndexedDBDeleteDatabaseRequestParent);
 }
 
 nsresult
 IndexedDBDeleteDatabaseRequestParent::HandleEvent(nsIDOMEvent* aEvent)
 {
-  if (Manager() &&
-      static_cast<IndexedDBParent*>(Manager())->IsDisconnected()) {
+  MOZ_ASSERT(aEvent);
+
+  if (IsDisconnected()) {
+    // We're shutting down, ignore this event.
     return NS_OK;
   }
 
-  MOZ_ASSERT(aEvent);
-
   nsString type;
   nsresult rv = aEvent->GetType(type);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (type.EqualsASCII(BLOCKED_EVT_STR)) {
     nsCOMPtr<nsIIDBVersionChangeEvent> event = do_QueryInterface(aEvent);
     MOZ_ASSERT(event);
 
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -36,16 +36,25 @@ BEGIN_INDEXEDDB_NAMESPACE
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
 class IDBIndex;
 class IDBObjectStore;
 class IDBOpenDBRequest;
 class IDBTransaction;
 
+class IndexedDBCursorParent;
+class IndexedDBDatabaseParent;
+class IndexedDBDeleteDatabaseRequestParent;
+class IndexedDBIndexParent;
+class IndexedDBObjectStoreParent;
+class IndexedDBTransactionParent;
+class IndexedDBVersionChangeTransactionParent;
+class IndexedDBVersionChangeObjectStoreParent;
+
 /*******************************************************************************
  * AutoSetCurrentTransaction
  ******************************************************************************/
 
 class AutoSetCurrentTransaction
 {
 public:
   AutoSetCurrentTransaction(IDBTransaction* aTransaction);
@@ -125,20 +134,22 @@ public:
     return mEventListener;
   }
 };
 
 /*******************************************************************************
  * IndexedDBParent
  ******************************************************************************/
 
-class IndexedDBParent : public PIndexedDBParent
+class IndexedDBParent : private PIndexedDBParent
 {
   friend class mozilla::dom::ContentParent;
   friend class mozilla::dom::TabParent;
+  friend class IndexedDBDatabaseParent;
+  friend class IndexedDBDeleteDatabaseRequestParent;
 
   nsRefPtr<IDBFactory> mFactory;
   nsCString mASCIIOrigin;
 
   ContentParent* mManagerContent;
   TabParent* mManagerTab;
 
   bool mDisconnected;
@@ -215,18 +226,22 @@ protected:
                                   PIndexedDBDeleteDatabaseRequestParent* aActor)
                                   MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * IndexedDBDatabaseParent
  ******************************************************************************/
 
-class IndexedDBDatabaseParent : public PIndexedDBDatabaseParent
+class IndexedDBDatabaseParent : private PIndexedDBDatabaseParent
 {
+  friend class IndexedDBParent;
+  friend class IndexedDBTransactionParent;
+  friend class IndexedDBVersionChangeTransactionParent;
+
   AutoWeakEventListener<IndexedDBDatabaseParent> mEventListener;
 
   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
   nsRefPtr<IDBDatabase> mDatabase;
 
 public:
   IndexedDBDatabaseParent();
   virtual ~IndexedDBDatabaseParent();
@@ -236,18 +251,27 @@ public:
 
   nsresult
   HandleEvent(nsIDOMEvent* aEvent);
 
   void
   Disconnect();
 
   bool
+  IsDisconnected() const
+  {
+    return static_cast<IndexedDBParent*>(Manager())->IsDisconnected();
+  }
+
+  bool
   CheckWritePermission(const nsAString& aDatabaseName);
 
+  void
+  Invalidate();
+
 protected:
   nsresult
   HandleRequestEvent(nsIDOMEvent* aEvent, const nsAString& aType);
 
   nsresult
   HandleDatabaseEvent(nsIDOMEvent* aEvent, const nsAString& aType);
 
   virtual void
@@ -268,29 +292,39 @@ protected:
   DeallocPIndexedDBTransaction(PIndexedDBTransactionParent* aActor)
                                MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * IndexedDBTransactionParent
  ******************************************************************************/
 
-class IndexedDBTransactionParent : public PIndexedDBTransactionParent
+class IndexedDBTransactionParent : protected PIndexedDBTransactionParent
 {
+  friend class IndexedDBCursorParent;
+  friend class IndexedDBDatabaseParent;
+  friend class IndexedDBObjectStoreParent;
+
 protected:
   AutoWeakEventListener<IndexedDBTransactionParent> mEventListener;
 
   nsRefPtr<IDBTransaction> mTransaction;
 
   bool mArtificialRequestCount;
 
 public:
   IndexedDBTransactionParent();
   virtual ~IndexedDBTransactionParent();
 
+  bool
+  IsDisconnected() const
+  {
+    return static_cast<IndexedDBDatabaseParent*>(Manager())->IsDisconnected();
+  }
+
   nsresult
   SetTransaction(IDBTransaction* aTransaction);
 
   IDBTransaction*
   GetTransaction() const
   {
     return mTransaction;
   }
@@ -328,57 +362,143 @@ protected:
 
 /*******************************************************************************
  * IndexedDBVersionChangeTransactionParent
  ******************************************************************************/
 
 class IndexedDBVersionChangeTransactionParent :
   public IndexedDBTransactionParent
 {
+  friend class IndexedDBVersionChangeObjectStoreParent;
+
 public:
   IndexedDBVersionChangeTransactionParent();
   virtual ~IndexedDBVersionChangeTransactionParent();
 
+  bool
+  IsDisconnected() const
+  {
+    return static_cast<IndexedDBDatabaseParent*>(Manager())->IsDisconnected();
+  }
+
 protected:
   virtual bool
   RecvDeleteObjectStore(const nsString& aName) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBObjectStoreConstructor(
                                     PIndexedDBObjectStoreParent* aActor,
                                     const ObjectStoreConstructorParams& aParams)
                                     MOZ_OVERRIDE;
 
   virtual PIndexedDBObjectStoreParent*
   AllocPIndexedDBObjectStore(const ObjectStoreConstructorParams& aParams)
                              MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
+ * IndexedDBCursorParent
+ ******************************************************************************/
+
+class IndexedDBCursorParent : private PIndexedDBCursorParent
+{
+  friend class IndexedDBIndexParent;
+  friend class IndexedDBObjectStoreParent;
+
+  nsRefPtr<IDBCursor> mCursor;
+
+public:
+  IDBCursor*
+  GetCursor() const
+  {
+    return mCursor;
+  }
+
+  bool
+  IsDisconnected() const;
+
+protected:
+  IndexedDBCursorParent(IDBCursor* aCursor);
+  virtual ~IndexedDBCursorParent();
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor,
+                                   const CursorRequestParams& aParams)
+                                   MOZ_OVERRIDE;
+
+  virtual PIndexedDBRequestParent*
+  AllocPIndexedDBRequest(const CursorRequestParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPIndexedDBRequest(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE;
+};
+
+/*******************************************************************************
  * IndexedDBObjectStoreParent
  ******************************************************************************/
 
-class IndexedDBObjectStoreParent : public PIndexedDBObjectStoreParent
+class IndexedDBObjectStoreParent : protected PIndexedDBObjectStoreParent
 {
+  friend class IndexedDBIndexParent;
+  friend class IndexedDBTransactionParent;
+  friend class IndexedDBVersionChangeTransactionParent;
+
+  typedef mozilla::dom::indexedDB::ipc::OpenCursorResponse OpenCursorResponse;
+
 protected:
   nsRefPtr<IDBObjectStore> mObjectStore;
 
 public:
   IndexedDBObjectStoreParent();
   virtual ~IndexedDBObjectStoreParent();
 
   void
   SetObjectStore(IDBObjectStore* aObjectStore);
 
   IDBObjectStore*
   GetObjectStore() const
   {
     return mObjectStore;
   }
 
+  bool
+  IsDisconnected() const
+  {
+    IndexedDBTransactionParent* manager =
+      static_cast<IndexedDBTransactionParent*>(Manager());
+    return manager->IsDisconnected();
+  }
+
+  // Ordinarily callers could just do this manually using
+  // PIndexedDBObjectStoreParent::SendPIndexedDBCursorConstructor but we're
+  // inheriting the abstract protocol class privately to prevent outside code
+  // from sending messages without checking the disconnected state. Therefore
+  // we need a helper method.
+  bool
+  OpenCursor(IDBCursor* aCursor,
+             const ObjectStoreCursorConstructorParams& aParams,
+             OpenCursorResponse& aResponse) NS_WARN_UNUSED_RESULT
+  {
+    if (IsDisconnected()) {
+      return true;
+    }
+
+    IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(aCursor);
+
+    if (!SendPIndexedDBCursorConstructor(cursorActor, aParams)) {
+      return false;
+    }
+
+    aResponse = cursorActor;
+    return true;
+  }
+
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvDeleteIndex(const nsString& aName) MOZ_OVERRIDE;
 
   virtual bool
@@ -413,51 +533,97 @@ protected:
 
 /*******************************************************************************
  * IndexedDBVersionChangeObjectStoreParent
  ******************************************************************************/
 
 class IndexedDBVersionChangeObjectStoreParent :
   public IndexedDBObjectStoreParent
 {
+  friend class IndexedDBVersionChangeTransactionParent;
+
 public:
   IndexedDBVersionChangeObjectStoreParent();
   virtual ~IndexedDBVersionChangeObjectStoreParent();
 
 protected:
+  bool
+  IsDisconnected() const
+  {
+    IndexedDBVersionChangeTransactionParent* manager =
+      static_cast<IndexedDBVersionChangeTransactionParent*>(Manager());
+    return manager->IsDisconnected();
+  }
+
   virtual bool
   RecvDeleteIndex(const nsString& aName) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBIndexConstructor(PIndexedDBIndexParent* aActor,
                                  const IndexConstructorParams& aParams)
                                  MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * IndexedDBIndexParent
  ******************************************************************************/
 
-class IndexedDBIndexParent : public PIndexedDBIndexParent
+class IndexedDBIndexParent : private PIndexedDBIndexParent
 {
+  friend class IndexedDBObjectStoreParent;
+  friend class IndexedDBVersionChangeObjectStoreParent;
+
+  typedef mozilla::dom::indexedDB::ipc::OpenCursorResponse OpenCursorResponse;
+
   nsRefPtr<IDBIndex> mIndex;
 
 public:
   IndexedDBIndexParent();
   virtual ~IndexedDBIndexParent();
 
   void
   SetIndex(IDBIndex* aObjectStore);
 
   IDBIndex*
   GetIndex() const
   {
     return mIndex;
   }
 
+  // Ordinarily callers could just do this manually using
+  // PIndexedDBIndexParent::SendPIndexedDBCursorConstructor but we're
+  // inheriting the abstract protocol class privately to prevent outside code
+  // from sending messages without checking the disconnected state. Therefore
+  // we need a helper method.
+  bool
+  OpenCursor(IDBCursor* aCursor, const IndexCursorConstructorParams& aParams,
+             OpenCursorResponse& aResponse) NS_WARN_UNUSED_RESULT
+  {
+    if (IsDisconnected()) {
+      return true;
+    }
+
+    IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(aCursor);
+
+    if (!SendPIndexedDBCursorConstructor(cursorActor, aParams)) {
+      return false;
+    }
+
+    aResponse = cursorActor;
+    return true;
+  }
+
+  bool
+  IsDisconnected() const
+  {
+    IndexedDBObjectStoreParent* manager =
+      static_cast<IndexedDBObjectStoreParent*>(Manager());
+    return manager->IsDisconnected();
+  }
+
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor,
                                    const IndexRequestParams& aParams)
                                    MOZ_OVERRIDE;
@@ -472,73 +638,59 @@ protected:
   AllocPIndexedDBCursor(const IndexCursorConstructorParams& aParams)
                         MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBCursor(PIndexedDBCursorParent* aActor) MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
- * IndexedDBCursorParent
- ******************************************************************************/
-
-class IndexedDBCursorParent : public PIndexedDBCursorParent
-{
-  nsRefPtr<IDBCursor> mCursor;
-
-public:
-  IndexedDBCursorParent(IDBCursor* aCursor);
-  virtual ~IndexedDBCursorParent();
-
-  IDBCursor*
-  GetCursor() const
-  {
-    return mCursor;
-  }
-
-protected:
-  virtual void
-  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
-
-  virtual bool
-  RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor,
-                                   const CursorRequestParams& aParams)
-                                   MOZ_OVERRIDE;
-
-  virtual PIndexedDBRequestParent*
-  AllocPIndexedDBRequest(const CursorRequestParams& aParams) MOZ_OVERRIDE;
-
-  virtual bool
-  DeallocPIndexedDBRequest(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE;
-};
-
-/*******************************************************************************
  * IndexedDBRequestParentBase
  ******************************************************************************/
 
 class IndexedDBRequestParentBase : public PIndexedDBRequestParent
 {
+public:
+  bool
+  SendResponse(const ResponseValue& aResponse) NS_WARN_UNUSED_RESULT
+  {
+    if (IsDisconnected()) {
+      return true;
+    }
+
+    return Send__delete__(this, aResponse);
+  }
+
 protected:
+  // Don't let anyone call this directly, instead go through SendResponse.
+  using PIndexedDBRequestParent::Send__delete__;
+
   typedef ipc::ResponseValue ResponseValue;
+  typedef PIndexedDBRequestParent::PBlobParent PBlobParent;
 
   nsRefPtr<IDBRequest> mRequest;
 
   IndexedDBRequestParentBase();
   virtual ~IndexedDBRequestParentBase();
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  IsDisconnected() = 0;
 };
 
 /*******************************************************************************
  * IndexedDBObjectStoreRequestParent
  ******************************************************************************/
 
 class IndexedDBObjectStoreRequestParent : public IndexedDBRequestParentBase
 {
+  friend class IndexedDBObjectStoreParent;
+
   nsRefPtr<IDBObjectStore> mObjectStore;
 
   typedef ipc::ObjectStoreRequestParams ParamsUnionType;
   typedef ParamsUnionType::Type RequestType;
   DebugOnly<RequestType> mRequestType;
 
   typedef ipc::AddParams AddParams;
   typedef ipc::PutParams PutParams;
@@ -577,24 +729,30 @@ public:
 
   bool
   OpenCursor(const OpenCursorParams& aParams);
 
 protected:
   void
   ConvertBlobActors(const InfallibleTArray<PBlobParent*>& aActors,
                     nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs);
+
+private:
+  virtual bool
+  IsDisconnected() MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * IndexedDBIndexRequestParent
  ******************************************************************************/
 
 class IndexedDBIndexRequestParent : public IndexedDBRequestParentBase
 {
+  friend class IndexedDBIndexParent;
+
   nsRefPtr<IDBIndex> mIndex;
 
   typedef ipc::IndexRequestParams ParamsUnionType;
   typedef ParamsUnionType::Type RequestType;
   DebugOnly<RequestType> mRequestType;
 
   typedef ipc::GetKeyParams GetKeyParams;
   typedef ipc::GetAllKeysParams GetAllKeysParams;
@@ -623,59 +781,77 @@ public:
   bool
   Count(const CountParams& aParams);
 
   bool
   OpenCursor(const OpenCursorParams& aParams);
 
   bool
   OpenKeyCursor(const OpenKeyCursorParams& aParams);
+
+private:
+  virtual bool
+  IsDisconnected() MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * IndexedDBCursorRequestParent
  ******************************************************************************/
 
 class IndexedDBCursorRequestParent : public IndexedDBRequestParentBase
 {
+  friend class IndexedDBCursorParent;
+
   nsRefPtr<IDBCursor> mCursor;
 
   typedef ipc::CursorRequestParams ParamsUnionType;
   typedef ParamsUnionType::Type RequestType;
   DebugOnly<RequestType> mRequestType;
 
   typedef ipc::ContinueParams ContinueParams;
 
 public:
   IndexedDBCursorRequestParent(IDBCursor* aCursor, RequestType aRequestType);
   virtual ~IndexedDBCursorRequestParent();
 
   bool
   Continue(const ContinueParams& aParams);
+
+private:
+  virtual bool
+  IsDisconnected() MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * IndexedDBDeleteDatabaseRequestParent
  ******************************************************************************/
 
 class IndexedDBDeleteDatabaseRequestParent :
-  public PIndexedDBDeleteDatabaseRequestParent
+  private PIndexedDBDeleteDatabaseRequestParent
 {
+  friend class IndexedDBParent;
+
   AutoWeakEventListener<IndexedDBDeleteDatabaseRequestParent> mEventListener;
 
   nsRefPtr<IDBFactory> mFactory;
   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
 
 public:
+  nsresult
+  HandleEvent(nsIDOMEvent* aEvent);
+
+protected:
   IndexedDBDeleteDatabaseRequestParent(IDBFactory* aFactory);
   virtual ~IndexedDBDeleteDatabaseRequestParent();
 
   nsresult
   SetOpenRequest(IDBOpenDBRequest* aOpenRequest);
 
-  nsresult
-  HandleEvent(nsIDOMEvent* aEvent);
+  bool
+  IsDisconnected() const
+  {
+    return static_cast<IndexedDBParent*>(Manager())->IsDisconnected();
+  }
 };
 
-
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_ipc_indexeddbparent_h__
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1643,16 +1643,22 @@ TabChild::RecvDestroy()
 
   nsCOMPtr<nsIObserverService> observerService =
     do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
 
   observerService->RemoveObserver(this, CANCEL_DEFAULT_PAN_ZOOM);
   observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT);
   observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
 
+  const InfallibleTArray<PIndexedDBChild*>& idbActors =
+    ManagedPIndexedDBChild();
+  for (uint32_t i = 0; i < idbActors.Length(); ++i) {
+    static_cast<IndexedDBChild*>(idbActors[i])->Disconnect();
+  }
+
   // XXX what other code in ~TabChild() should we be running here?
   DestroyWindow();
 
   return Send__delete__(this);
 }
 
 PRenderFrameChild*
 TabChild::AllocPRenderFrame(ScrollingBehavior* aScrolling,
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -102,17 +102,19 @@ void Channel::ChannelImpl::Close() {
     delete m;
   }
 
   if (thread_check_.get())
     thread_check_.reset();
 }
 
 bool Channel::ChannelImpl::Send(Message* message) {
-  DCHECK(thread_check_->CalledOnValidThread());
+  if (thread_check_.get()) {
+    DCHECK(thread_check_->CalledOnValidThread());
+  }
   chrome::Counters::ipc_send_counter().Increment();
 #ifdef IPC_MESSAGE_DEBUG_EXTRA
   DLOG(INFO) << "sending message @" << message << " on channel @" << this
              << " with type " << message->type()
              << " (" << output_queue_.size() << " in queue)";
 #endif
 
 #ifdef IPC_MESSAGE_LOG_ENABLED
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -2850,20 +2850,20 @@ class _GenerateProtocolActorCode(ipdl.as
                 settimeout.addstmt(StmtExpr(
                     ExprCall(
                         ExprSelect(p.channelVar(), '.', 'SetReplyTimeoutMs'),
                         args=[ timeoutvar ])))
                 self.cls.addstmts([ settimeout, Whitespace.NL ])
 
         if not ptype.isToplevel():
             if 1 == len(p.managers):
-                ## manager()
+                ## manager() const
                 managertype = p.managerActorType(self.side, ptr=1)
                 managermeth = MethodDefn(MethodDecl(
-                    p.managerMethod().name, ret=managertype))
+                    p.managerMethod().name, ret=managertype, const=1))
                 managermeth.addstmt(StmtReturn(
                     ExprCast(p.managerVar(), managertype, static=1)))
 
                 self.cls.addstmts([ managermeth, Whitespace.NL ])
 
         ## Managed[T](Array& inout) const
         ## const Array<T>& Managed() const
         for managed in ptype.manages: