Bug 701634 - Support IndexedDB in Workers, r=khuey+baku.
authorBen Turner <bent.mozilla@gmail.com>
Tue, 16 Dec 2014 22:26:15 -0800
changeset 233445 9d0ed89e7c5820309c55f12b8a81122298934666
parent 233444 aa82fdbf336298bbaa2a48ee8d0ab6245b635fcd
child 233446 f89cbdabd57cccb2c5b922a2d45a034145c5de4f
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-esr52@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs701634
milestone37.0a1
Bug 701634 - Support IndexedDB in Workers, r=khuey+baku.
dom/base/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/html/HTMLPropertiesCollection.cpp
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsChild.h
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBEvents.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFactory.h
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/IndexedDatabaseManager.h
dom/indexedDB/moz.build
dom/indexedDB/test/helpers.js
dom/indexedDB/test/test_blob_worker_crash.html
dom/indexedDB/test/unit/test_autoIncrement.js
dom/indexedDB/test/unit/test_blob_file_backed.js
dom/indexedDB/test/unit/test_invalid_cursor.js
dom/indexedDB/test/unit/test_lowDiskSpace.js
dom/indexedDB/test/unit/test_readonly_transactions.js
dom/indexedDB/test/unit/test_setVersion_events.js
dom/indexedDB/test/unit/test_transaction_abort.js
dom/indexedDB/test/unit/test_transaction_error.js
dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js
dom/indexedDB/test/unit/xpcshell-head-parent-process.js
dom/ipc/Blob.cpp
dom/ipc/BlobChild.h
dom/ipc/BlobParent.h
dom/ipc/PBlob.ipdl
dom/ipc/PBlobStream.ipdl
dom/ipc/moz.build
dom/webidl/DOMError.webidl
dom/webidl/DOMStringList.webidl
dom/webidl/IDBCursor.webidl
dom/webidl/IDBDatabase.webidl
dom/webidl/IDBEnvironment.webidl
dom/webidl/IDBFactory.webidl
dom/webidl/IDBIndex.webidl
dom/webidl/IDBKeyRange.webidl
dom/webidl/IDBObjectStore.webidl
dom/webidl/IDBOpenDBRequest.webidl
dom/webidl/IDBRequest.webidl
dom/webidl/IDBTransaction.webidl
dom/webidl/IDBVersionChangeEvent.webidl
dom/webidl/WorkerGlobalScope.webidl
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/ServiceWorkerManager.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/WorkerScope.h
dom/workers/WorkerThread.cpp
dom/workers/WorkerThread.h
dom/workers/test/test_worker_interfaces.js
ipc/glue/BackgroundUtils.cpp
ipc/glue/InputStreamParams.ipdlh
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
testing/web-platform/meta/IndexedDB/idb_webworkers.htm.ini
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1435,16 +1435,18 @@ nsDOMStyleSheetSetList::nsDOMStyleSheetS
   : mDocument(aDocument)
 {
   NS_ASSERTION(mDocument, "Must have document!");
 }
 
 void
 nsDOMStyleSheetSetList::EnsureFresh()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   mNames.Clear();
 
   if (!mDocument) {
     return; // Spec says "no exceptions", and we have no style sets if we have
             // no document, for sure
   }
 
   int32_t count = mDocument->GetNumberOfStyleSheets();
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -167,17 +167,16 @@
 #include "nsIPopupWindowManager.h"
 
 #include "nsIDragService.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Selection.h"
 #include "nsFrameLoader.h"
 #include "nsISupportsPrimitives.h"
 #include "nsXPCOMCID.h"
-#include "mozIThirdPartyUtil.h"
 #include "prlog.h"
 #include "prenv.h"
 #include "prprf.h"
 
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
@@ -255,16 +254,17 @@ static PRLogModuleInfo* gDOMLeakPRLog;
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
+using mozilla::dom::indexedDB::IDBFactory;
 
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
 
 static nsIEntropyCollector *gEntropyCollector          = nullptr;
 static int32_t              gRefCnt                    = 0;
 static int32_t              gOpenPopupSpamCount        = 0;
@@ -10579,81 +10579,21 @@ nsGlobalWindow::GetLocalStorage(nsISuppo
 
   ErrorResult rv;
   nsCOMPtr<nsIDOMStorage> storage = GetLocalStorage(rv);
   storage.forget(aLocalStorage);
 
   return rv.ErrorCode();
 }
 
-static bool
-GetIndexedDBEnabledForAboutURI(nsIURI *aURI)
-{
-  nsCOMPtr<nsIAboutModule> module;
-  nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(module));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  uint32_t flags;
-  rv = module->GetURIFlags(aURI, &flags);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  return flags & nsIAboutModule::ENABLE_INDEXED_DB;
-}
-
-mozilla::dom::indexedDB::IDBFactory*
+IDBFactory*
 nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
 {
-  using mozilla::dom::indexedDB::IDBFactory;
-
   if (!mIndexedDB) {
-    // If the document has the sandboxed origin flag set
-    // don't allow access to indexedDB.
-    if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
-      aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-      return nullptr;
-    }
-
-    if (!IsChromeWindow()) {
-      // Whitelist about:home, since it doesn't have a base domain it would not
-      // pass the thirdPartyUtil check, though it should be able to use
-      // indexedDB.
-      bool skipThirdPartyCheck = false;
-      nsIPrincipal *principal = GetPrincipal();
-      if (principal) {
-        nsCOMPtr<nsIURI> uri;
-        principal->GetURI(getter_AddRefs(uri));
-
-        if (uri) {
-          bool isAbout = false;
-          if (NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
-            skipThirdPartyCheck = GetIndexedDBEnabledForAboutURI(uri);
-          }
-        }
-      }
-
-      if (!skipThirdPartyCheck) {
-        nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-          do_GetService(THIRDPARTYUTIL_CONTRACTID);
-        if (!thirdPartyUtil) {
-          aError.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-          return nullptr;
-        }
-
-        bool isThirdParty;
-        aError = thirdPartyUtil->IsThirdPartyWindow(this, nullptr,
-                                                    &isThirdParty);
-        if (aError.Failed() || isThirdParty) {
-          NS_WARN_IF_FALSE(aError.Failed(),
-                           "IndexedDB is not permitted in a third-party window.");
-          return nullptr;
-        }
-      }
-    }
-
-    // This may be null if being created from a file.
+    // This may keep mIndexedDB null without setting an error.
     aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB));
   }
 
   return mIndexedDB;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
--- a/dom/html/HTMLPropertiesCollection.cpp
+++ b/dom/html/HTMLPropertiesCollection.cpp
@@ -9,16 +9,18 @@
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsAttrValue.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
 #include "jsapi.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLPropertiesCollection)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLPropertiesCollection)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
@@ -504,16 +506,18 @@ NS_IMPL_ADDREF_INHERITED(PropertyStringL
 NS_IMPL_RELEASE_INHERITED(PropertyStringList, DOMStringList)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PropertyStringList)
 NS_INTERFACE_MAP_END_INHERITING(DOMStringList)
 
 void
 PropertyStringList::EnsureFresh()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   mCollection->EnsureFresh();
 }
 
 bool
 PropertyStringList::ContainsInternal(const nsAString& aString)
 {
   // This method should not call EnsureFresh, otherwise we may become stuck in an infinite loop.
   return mNames.Contains(aString);
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -13,16 +13,17 @@
 #include "IDBObjectStore.h"
 #include "IDBMutableFile.h"
 #include "IDBRequest.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabase.h"
 #include "IndexedDatabaseInlines.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/TypeTraits.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
@@ -182,49 +183,76 @@ public:
 class MOZ_STACK_CLASS ResultHelper MOZ_FINAL
   : public IDBRequest::ResultCallback
 {
   IDBRequest* mRequest;
   AutoSetCurrentTransaction mAutoTransaction;
 
   union
   {
-    nsISupports* mISupports;
+    IDBDatabase* mDatabase;
+    IDBCursor* mCursor;
+    IDBMutableFile* mMutableFile;
     StructuredCloneReadInfo* mStructuredClone;
     const nsTArray<StructuredCloneReadInfo>* mStructuredCloneArray;
     const Key* mKey;
     const nsTArray<Key>* mKeyArray;
     const JS::Value* mJSVal;
     const JS::Handle<JS::Value>* mJSValHandle;
   } mResult;
 
   enum
   {
-    ResultTypeISupports,
+    ResultTypeDatabase,
+    ResultTypeCursor,
+    ResultTypeMutableFile,
     ResultTypeStructuredClone,
     ResultTypeStructuredCloneArray,
     ResultTypeKey,
     ResultTypeKeyArray,
     ResultTypeJSVal,
     ResultTypeJSValHandle,
   } mResultType;
 
 public:
   ResultHelper(IDBRequest* aRequest,
                IDBTransaction* aTransaction,
-               nsISupports* aResult)
+               IDBDatabase* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeDatabase)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aResult);
+
+    mResult.mDatabase = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               IDBCursor* aResult)
     : mRequest(aRequest)
     , mAutoTransaction(aTransaction)
-    , mResultType(ResultTypeISupports)
+    , mResultType(ResultTypeCursor)
   {
-    MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!");
     MOZ_ASSERT(aRequest);
-    MOZ_ASSERT(aResult);
-
-    mResult.mISupports = aResult;
+
+    mResult.mCursor = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               IDBMutableFile* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeMutableFile)
+  {
+    MOZ_ASSERT(aRequest);
+
+    mResult.mMutableFile = aResult;
   }
 
   ResultHelper(IDBRequest* aRequest,
                IDBTransaction* aTransaction,
                StructuredCloneReadInfo* aResult)
     : mRequest(aRequest)
     , mAutoTransaction(aTransaction)
     , mResultType(ResultTypeStructuredClone)
@@ -313,18 +341,24 @@ public:
 
   virtual nsresult
   GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) MOZ_OVERRIDE
   {
     MOZ_ASSERT(aCx);
     MOZ_ASSERT(mRequest);
 
     switch (mResultType) {
-      case ResultTypeISupports:
-        return GetResult(aCx, mResult.mISupports, aResult);
+      case ResultTypeDatabase:
+        return GetResult(aCx, mResult.mDatabase, aResult);
+
+      case ResultTypeCursor:
+        return GetResult(aCx, mResult.mCursor, aResult);
+
+      case ResultTypeMutableFile:
+        return GetResult(aCx, mResult.mMutableFile, aResult);
 
       case ResultTypeStructuredClone:
         return GetResult(aCx, mResult.mStructuredClone, aResult);
 
       case ResultTypeStructuredCloneArray:
         return GetResult(aCx, mResult.mStructuredCloneArray, aResult);
 
       case ResultTypeKey:
@@ -344,30 +378,32 @@ public:
       default:
         MOZ_CRASH("Unknown result type!");
     }
 
     MOZ_CRASH("Should never get here!");
   }
 
 private:
-  nsresult
+  template <class T>
+  typename EnableIf<IsSame<T, IDBDatabase>::value ||
+                    IsSame<T, IDBCursor>::value ||
+                    IsSame<T, IDBMutableFile>::value,
+                    nsresult>::Type
   GetResult(JSContext* aCx,
-            nsISupports* aSupports,
+            T* aDOMObject,
             JS::MutableHandle<JS::Value> aResult)
   {
-    MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!");
-
-    if (!aSupports) {
+    if (!aDOMObject) {
       aResult.setNull();
       return NS_OK;
     }
 
-    nsresult rv = nsContentUtils::WrapNative(aCx, aSupports, aResult);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
+    bool ok = GetOrCreateDOMReflector(aCx, aDOMObject, aResult);
+    if (NS_WARN_IF(!ok)) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     return NS_OK;
   }
 
   nsresult
@@ -604,19 +640,17 @@ DispatchErrorEvent(IDBRequest* aRequest,
 
   nsCOMPtr<nsIDOMEvent> errorEvent;
   if (!aEvent) {
     // Make an error event and fire it at the target.
     errorEvent = CreateGenericEvent(request,
                                     nsDependentString(kErrorEventType),
                                     eDoesBubble,
                                     eCancelable);
-    if (NS_WARN_IF(!errorEvent)) {
-      return;
-    }
+    MOZ_ASSERT(errorEvent);
 
     aEvent = errorEvent;
   }
 
   Maybe<AutoSetCurrentTransaction> asct;
   if (aTransaction) {
     asct.emplace(aTransaction);
   }
@@ -682,19 +716,17 @@ DispatchSuccessEvent(ResultHelper* aResu
   }
 
   nsCOMPtr<nsIDOMEvent> successEvent;
   if (!aEvent) {
     successEvent = CreateGenericEvent(request,
                                       nsDependentString(kSuccessEventType),
                                       eDoesNotBubble,
                                       eNotCancelable);
-    if (NS_WARN_IF(!successEvent)) {
-      return;
-    }
+    MOZ_ASSERT(successEvent);
 
     aEvent = successEvent;
   }
 
   request->SetResultCallback(aResultHelper);
 
   MOZ_ASSERT(aEvent);
   MOZ_ASSERT_IF(transaction, transaction->IsOpen());
@@ -982,18 +1014,17 @@ BackgroundFactoryRequestChild::HandleRes
     static_cast<BackgroundDatabaseChild*>(aResponse.databaseChild());
   MOZ_ASSERT(databaseActor);
 
   databaseActor->EnsureDOMObject();
 
   IDBDatabase* database = databaseActor->GetDOMObject();
   MOZ_ASSERT(database);
 
-  ResultHelper helper(mRequest, nullptr,
-                      static_cast<IDBWrapperCache*>(database));
+  ResultHelper helper(mRequest, nullptr, database);
 
   DispatchSuccessEvent(&helper);
 
   databaseActor->ReleaseDOMObject();
 
   return true;
 }
 
@@ -1004,59 +1035,78 @@ BackgroundFactoryRequestChild::HandleRes
   AssertIsOnOwningThread();
 
   ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue);
 
   nsCOMPtr<nsIDOMEvent> successEvent =
     IDBVersionChangeEvent::Create(mRequest,
                                   nsDependentString(kSuccessEventType),
                                   aResponse.previousVersion());
-  if (NS_WARN_IF(!successEvent)) {
-    return false;
-  }
+  MOZ_ASSERT(successEvent);
 
   DispatchSuccessEvent(&helper, successEvent);
 
   return true;
 }
 
 void
 BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnOwningThread();
 
   MaybeCollectGarbageOnIPCMessage();
 
   NoteActorDestroyed();
+
+  if (aWhy != Deletion) {
+    IDBOpenDBRequest* openRequest = GetOpenDBRequest();
+    if (openRequest) {
+      openRequest->NoteComplete();
+    }
+  }
 }
 
 bool
 BackgroundFactoryRequestChild::Recv__delete__(
                                         const FactoryRequestResponse& aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
   MaybeCollectGarbageOnIPCMessage();
 
+  bool result;
+
   switch (aResponse.type()) {
     case FactoryRequestResponse::Tnsresult:
-      return HandleResponse(aResponse.get_nsresult());
+      result = HandleResponse(aResponse.get_nsresult());
+      break;
 
     case FactoryRequestResponse::TOpenDatabaseRequestResponse:
-      return HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
+      result = HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
+      break;
 
     case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
-      return HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
+      result = HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
+      break;
 
     default:
       MOZ_CRASH("Unknown response type!");
   }
 
-  MOZ_CRASH("Should never get here!");
+  IDBOpenDBRequest* request = GetOpenDBRequest();
+  MOZ_ASSERT(request);
+  
+  request->NoteComplete();
+
+  if (NS_WARN_IF(!result)) {
+    return false;
+  }
+
+  return true;
 }
 
 bool
 BackgroundFactoryRequestChild::RecvPermissionChallenge(
                                             const PrincipalInfo& aPrincipalInfo)
 {
   AssertIsOnOwningThread();
 
@@ -1116,26 +1166,24 @@ BackgroundFactoryRequestChild::RecvBlock
   MaybeCollectGarbageOnIPCMessage();
 
   const nsDependentString type(kBlockedEventType);
 
   nsCOMPtr<nsIDOMEvent> blockedEvent;
   if (mIsDeleteOp) {
     blockedEvent =
       IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion);
+    MOZ_ASSERT(blockedEvent);
   } else {
     blockedEvent =
       IDBVersionChangeEvent::Create(mRequest,
                                     type,
                                     aCurrentVersion,
                                     mRequestedVersion);
-  }
-
-  if (NS_WARN_IF(!blockedEvent)) {
-    return false;
+    MOZ_ASSERT(blockedEvent);
   }
 
   nsRefPtr<IDBRequest> kungFuDeathGrip = mRequest;
 
   IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: Firing \"blocked\" event",
                "IndexedDB %s: C R[%llu]: \"blocked\"",
                IDB_LOG_ID_STRING(),
                mRequest->LoggingSerialNumber());
@@ -1310,50 +1358,55 @@ BackgroundDatabaseChild::RecvPBackground
   AssertIsOnOwningThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(mOpenRequestActor);
 
   MaybeCollectGarbageOnIPCMessage();
 
   EnsureDOMObject();
 
-  auto actor = static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
+  auto* actor = static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
 
   nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
   MOZ_ASSERT(request);
 
   nsRefPtr<IDBTransaction> transaction =
     IDBTransaction::CreateVersionChange(mDatabase,
                                         actor,
                                         request,
                                         aNextObjectStoreId,
                                         aNextIndexId);
   if (NS_WARN_IF(!transaction)) {
-    return false;
+    // This can happen if we receive events after a worker has begun its
+    // shutdown process.
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    // Report this to the console.
+    IDB_REPORT_INTERNAL_ERR();
+
+    MOZ_ALWAYS_TRUE(aActor->SendDeleteMe());
+    return true;
   }
 
   transaction->AssertIsOnOwningThread();
 
   actor->SetDOMTransaction(transaction);
 
   mDatabase->EnterSetVersionTransaction(aRequestedVersion);
 
   request->SetTransaction(transaction);
 
   nsCOMPtr<nsIDOMEvent> upgradeNeededEvent =
     IDBVersionChangeEvent::Create(request,
                                   nsDependentString(kUpgradeNeededEventType),
                                   aCurrentVersion,
                                   aRequestedVersion);
-  if (NS_WARN_IF(!upgradeNeededEvent)) {
-    return false;
-  }
-
-  ResultHelper helper(request, transaction,
-                      static_cast<IDBWrapperCache*>(mDatabase));
+  MOZ_ASSERT(upgradeNeededEvent);
+
+  ResultHelper helper(request, transaction, mDatabase);
 
   DispatchSuccessEvent(&helper, upgradeNeededEvent);
 
   return true;
 }
 
 bool
 BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild(
@@ -1406,34 +1459,32 @@ BackgroundDatabaseChild::RecvVersionChan
   const nsDependentString type(kVersionChangeEventType);
 
   nsCOMPtr<nsIDOMEvent> versionChangeEvent;
 
   switch (aNewVersion.type()) {
     case NullableVersion::Tnull_t:
       versionChangeEvent =
         IDBVersionChangeEvent::Create(mDatabase, type, aOldVersion);
+      MOZ_ASSERT(versionChangeEvent);
       break;
 
     case NullableVersion::Tuint64_t:
       versionChangeEvent =
         IDBVersionChangeEvent::Create(mDatabase,
                                       type,
                                       aOldVersion,
                                       aNewVersion.get_uint64_t());
+      MOZ_ASSERT(versionChangeEvent);
       break;
 
     default:
       MOZ_CRASH("Should never get here!");
   }
 
-  if (NS_WARN_IF(!versionChangeEvent)) {
-    return false;
-  }
-
   IDB_LOG_MARK("IndexedDB %s: Child : Firing \"versionchange\" event",
                "IndexedDB %s: C: IDBDatabase \"versionchange\" event",
                IDB_LOG_ID_STRING());
 
   bool dummy;
   if (NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))) {
     NS_WARNING("Failed to dispatch event!");
   }
@@ -1665,21 +1716,22 @@ void
 BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const
 {
   static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
 }
 
 #endif // DEBUG
 
 void
-BackgroundVersionChangeTransactionChild::SendDeleteMeInternal()
+BackgroundVersionChangeTransactionChild::SendDeleteMeInternal(
+                                                        bool aFailedConstructor)
 {
   AssertIsOnOwningThread();
 
-  if (mTransaction) {
+  if (mTransaction || aFailedConstructor) {
     NoteActorDestroyed();
 
     MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild::
                       SendDeleteMe());
   }
 }
 
 void
@@ -2009,39 +2061,41 @@ BackgroundRequestChild::Recv__delete__(c
   MOZ_CRASH("Should never get here!");
 }
 
 /*******************************************************************************
  * BackgroundCursorChild
  ******************************************************************************/
 
 class BackgroundCursorChild::DelayedDeleteRunnable MOZ_FINAL
-  : public nsIRunnable
+  : public nsICancelableRunnable
 {
   BackgroundCursorChild* mActor;
   nsRefPtr<IDBRequest> mRequest;
 
 public:
-  explicit DelayedDeleteRunnable(BackgroundCursorChild* aActor)
+  explicit
+  DelayedDeleteRunnable(BackgroundCursorChild* aActor)
     : mActor(aActor)
     , mRequest(aActor->mRequest)
   {
     MOZ_ASSERT(aActor);
     aActor->AssertIsOnOwningThread();
     MOZ_ASSERT(mRequest);
   }
 
   // Does not need to be threadsafe since this only runs on one thread.
   NS_DECL_ISUPPORTS
 
 private:
   ~DelayedDeleteRunnable()
   { }
 
   NS_DECL_NSIRUNNABLE
+  NS_DECL_NSICANCELABLERUNNABLE
 };
 
 BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest,
                                              IDBObjectStore* aObjectStore,
                                              Direction aDirection)
   : mRequest(aRequest)
   , mTransaction(aRequest->GetTransaction())
   , mObjectStore(aObjectStore)
@@ -2400,17 +2454,18 @@ DispatchMutableFileResult(IDBRequest* aR
     ResultHelper helper(aRequest, nullptr, aMutableFile);
     DispatchSuccessEvent(&helper);
   } else {
     DispatchErrorEvent(aRequest, aResultCode);
   }
 }
 
 NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedDeleteRunnable,
-                  nsIRunnable)
+                  nsIRunnable,
+                  nsICancelableRunnable)
 
 NS_IMETHODIMP
 BackgroundCursorChild::
 DelayedDeleteRunnable::Run()
 {
   MOZ_ASSERT(mActor);
   mActor->AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
@@ -2418,11 +2473,25 @@ DelayedDeleteRunnable::Run()
   mActor->SendDeleteMeInternal();
 
   mActor = nullptr;
   mRequest = nullptr;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+BackgroundCursorChild::
+DelayedDeleteRunnable::Cancel()
+{
+  if (NS_WARN_IF(!mActor)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // This must always run to clean up our state.
+  Run();
+
+  return NS_OK;
+}
+
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -509,17 +509,17 @@ class BackgroundVersionChangeTransaction
 
 public:
 #ifdef DEBUG
   virtual void
   AssertIsOnOwningThread() const MOZ_OVERRIDE;
 #endif
 
   void
-  SendDeleteMeInternal();
+  SendDeleteMeInternal(bool aFailedConstructor);
 
 private:
   // Only created by BackgroundDatabaseChild.
   explicit BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest);
 
   // Only destroyed by BackgroundDatabaseChild.
   ~BackgroundVersionChangeTransactionChild();
 
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -7816,23 +7816,18 @@ NormalTransaction::IsSameProcessActor()
 
   return !BackgroundParent::IsOtherProcessActor(actor);
 }
 
 bool
 NormalTransaction::SendCompleteNotification(nsresult aResult)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!IsActorDestroyed());
-
-  if (NS_WARN_IF(!SendComplete(aResult))) {
-    return false;
-  }
-
-  return true;
+
+  return IsActorDestroyed() || !NS_WARN_IF(!SendComplete(aResult));
 }
 
 void
 NormalTransaction::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
 
   if (!mCommittedOrAborted) {
@@ -8101,28 +8096,27 @@ VersionChangeTransaction::UpdateMetadata
   }
 }
 
 bool
 VersionChangeTransaction::SendCompleteNotification(nsresult aResult)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mOpenDatabaseOp);
-  MOZ_ASSERT(!IsActorDestroyed());
 
   nsRefPtr<OpenDatabaseOp> openDatabaseOp;
   mOpenDatabaseOp.swap(openDatabaseOp);
 
   if (NS_FAILED(aResult) && NS_SUCCEEDED(openDatabaseOp->mResultCode)) {
     openDatabaseOp->mResultCode = aResult;
   }
 
   openDatabaseOp->mState = OpenDatabaseOp::State_SendingResults;
 
-  bool result = SendComplete(aResult);
+  bool result = IsActorDestroyed() || !NS_WARN_IF(!SendComplete(aResult));
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(openDatabaseOp->Run()));
 
   return result;
 }
 
 void
 VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy)
@@ -10545,20 +10539,33 @@ FactoryOp::Open()
   MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
              permission == PermissionRequestBase::kPermissionDenied ||
              permission == PermissionRequestBase::kPermissionPrompt);
 
   if (permission == PermissionRequestBase::kPermissionDenied) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
-  // This has to be started on the main thread currently.
-  if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  {
+    // These services have to be started on the main thread currently.
+    if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    nsCOMPtr<mozIStorageService> ss;
+    if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    if (NS_WARN_IF(!QuotaManager::GetOrCreate())) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
   }
 
   const DatabaseMetadata& metadata = mCommonParams.metadata();
 
   QuotaManager::GetStorageId(metadata.persistenceType(),
                              mOrigin,
                              Client::IDB,
                              metadata.name(),
@@ -11931,18 +11938,17 @@ OpenDatabaseOp::SendResults()
 
   if (mVersionChangeTransaction) {
     MOZ_ASSERT(NS_FAILED(mResultCode));
 
     mVersionChangeTransaction->Abort(mResultCode, /* aForce */ true);
     mVersionChangeTransaction = nullptr;
   }
 
-  if (!IsActorDestroyed() &&
-      (!mDatabase || !mDatabase->IsInvalidated())) {
+  if (!IsActorDestroyed()) {
     FactoryRequestResponse response;
 
     if (NS_SUCCEEDED(mResultCode)) {
       // If we just successfully completed a versionchange operation then we
       // need to update the version in our metadata.
       mMetadata->mCommonMetadata.version() = mRequestedVersion;
 
       nsresult rv = EnsureDatabaseActorIsAlive();
@@ -11972,16 +11978,20 @@ OpenDatabaseOp::SendResults()
       PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
   }
 
   if (NS_FAILED(mResultCode) && mOfflineStorage) {
     mOfflineStorage->CloseOnOwningThread();
     DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget());
   }
 
+  // Make sure to release the database on this thread.
+  nsRefPtr<Database> database;
+  mDatabase.swap(database);
+
   FinishSendResults();
 }
 
 void
 OpenDatabaseOp::EnsureDatabaseActor()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State_BeginVersionChange ||
@@ -13372,19 +13382,17 @@ CommitOp::TransactionFinishedAfterUnbloc
                  "IndexedDB %s: P T[%lld]: Transaction finished (0x%x)",
                  IDB_LOG_ID_STRING(mTransaction->GetLoggingInfo()->Id()),
                  mTransaction->LoggingSerialNumber(),
                  mResultCode);
   }
 
   mTransaction->ReleaseBackgroundThreadObjects();
 
-  if (!mTransaction->IsActorDestroyed()) {
-    mTransaction->SendCompleteNotification(ClampResultCode(mResultCode));
-  }
+  mTransaction->SendCompleteNotification(ClampResultCode(mResultCode));
 
   mTransaction->GetDatabase()->UnregisterTransaction(mTransaction);
 
   mTransaction = nullptr;
 
 #ifdef DEBUG
   // A bit hacky but the CommitOp is not really a normal database operation
   // that is tied to an actor. Do this to make our assertions happy.
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -62,16 +62,39 @@ using namespace mozilla::ipc;
 using namespace mozilla::services;
 
 namespace {
 
 const char kCycleCollectionObserverTopic[] = "cycle-collector-end";
 const char kMemoryPressureObserverTopic[] = "memory-pressure";
 const char kWindowObserverTopic[] = "inner-window-destroyed";
 
+class CancelableRunnableWrapper MOZ_FINAL
+  : public nsICancelableRunnable
+{
+  nsCOMPtr<nsIRunnable> mRunnable;
+
+public:
+  explicit
+  CancelableRunnableWrapper(nsIRunnable* aRunnable)
+    : mRunnable(aRunnable)
+  {
+    MOZ_ASSERT(aRunnable);
+  }
+
+  NS_DECL_ISUPPORTS
+
+private:
+  ~CancelableRunnableWrapper()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSICANCELABLERUNNABLE
+};
+
 // XXX This should either be ported to PBackground or removed someday.
 class CreateFileHelper MOZ_FINAL
   : public nsRunnable
 {
   nsRefPtr<IDBDatabase> mDatabase;
   nsRefPtr<IDBRequest> mRequest;
   nsRefPtr<FileInfo> mFileInfo;
 
@@ -151,16 +174,56 @@ private:
 #ifdef DEBUG
     mDatabase = nullptr;
 #endif
   }
 };
 
 } // anonymous namespace
 
+class IDBDatabase::LogWarningRunnable MOZ_FINAL
+  : public nsRunnable
+{
+  nsCString mMessageName;
+  nsString mFilename;
+  uint32_t mLineNumber;
+  uint64_t mInnerWindowID;
+  bool mIsChrome;
+
+public:
+  LogWarningRunnable(const char* aMessageName,
+                     const nsAString& aFilename,
+                     uint32_t aLineNumber,
+                     bool aIsChrome,
+                     uint64_t aInnerWindowID)
+    : mMessageName(aMessageName)
+    , mFilename(aFilename)
+    , mLineNumber(aLineNumber)
+    , mInnerWindowID(aInnerWindowID)
+    , mIsChrome(aIsChrome)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  static void
+  LogWarning(const char* aMessageName,
+             const nsAString& aFilename,
+             uint32_t aLineNumber,
+             bool aIsChrome,
+             uint64_t aInnerWindowID);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~LogWarningRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
 class IDBDatabase::Observer MOZ_FINAL
   : public nsIObserver
 {
   IDBDatabase* mWeakDatabase;
   const uint64_t mWindowId;
 
 public:
   Observer(IDBDatabase* aDatabase, uint64_t aWindowId)
@@ -675,17 +738,21 @@ IDBDatabase::Transaction(const Sequence<
   for (uint32_t nameIndex = nameCount - 1; nameIndex > 0; nameIndex--) {
     if (sortedStoreNames[nameIndex] == sortedStoreNames[nameIndex - 1]) {
       sortedStoreNames.RemoveElementAt(nameIndex);
     }
   }
 
   nsRefPtr<IDBTransaction> transaction =
     IDBTransaction::Create(this, sortedStoreNames, mode);
-  MOZ_ASSERT(transaction);
+  if (NS_WARN_IF(!transaction)) {
+    IDB_REPORT_INTERNAL_ERR();
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    return nullptr;
+  }
 
   BackgroundTransactionChild* actor =
     new BackgroundTransactionChild(transaction);
 
   IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
                  "database(%s).transaction(%s)",
                "IndexedDB %s: C T[%lld]: IDBDatabase.transaction()",
                IDB_LOG_ID_STRING(),
@@ -1001,16 +1068,22 @@ IDBDatabase::DelayedMaybeExpireFileActor
   }
 
   nsCOMPtr<nsIRunnable> runnable =
     NS_NewRunnableMethodWithArg<bool>(this,
                                       &IDBDatabase::ExpireFileActors,
                                       /* aExpireAll */ false);
   MOZ_ASSERT(runnable);
 
+  if (!NS_IsMainThread()) {
+    // Wrap as a nsICancelableRunnable to make workers happy.
+    nsCOMPtr<nsIRunnable> cancelable = new CancelableRunnableWrapper(runnable);
+    cancelable.swap(runnable);
+  }
+
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
 }
 
 nsresult
 IDBDatabase::GetQuotaInfo(nsACString& aOrigin,
                           PersistenceType* aPersistenceType)
 {
   using mozilla::dom::quota::QuotaManager;
@@ -1165,20 +1238,19 @@ IDBDatabase::NoteFinishedMutableFile(IDB
   // is in the list already.
 
   mLiveMutableFiles.RemoveElement(aMutableFile);
 }
 
 void
 IDBDatabase::InvalidateMutableFiles()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
   if (!mLiveMutableFiles.IsEmpty()) {
     MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
+    MOZ_ASSERT(NS_IsMainThread());
 
     for (uint32_t count = mLiveMutableFiles.Length(), index = 0;
          index < count;
          index++) {
       mLiveMutableFiles[index]->Invalidate();
     }
 
     mLiveMutableFiles.Clear();
@@ -1200,65 +1272,31 @@ IDBDatabase::Invalidate()
 void
 IDBDatabase::LogWarning(const char* aMessageName,
                         const nsAString& aFilename,
                         uint32_t aLineNumber)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aMessageName);
 
-  // For now this is main-thread only.
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsXPIDLString localizedMessage;
-  if (NS_WARN_IF(NS_FAILED(
-    nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
-                                       aMessageName,
-                                       localizedMessage)))) {
-    return;
-  }
-
-  nsAutoCString category;
-  if (mFactory->IsChrome()) {
-    category.AssignLiteral("chrome ");
+  if (NS_IsMainThread()) {
+    LogWarningRunnable::LogWarning(aMessageName,
+                                   aFilename,
+                                   aLineNumber,
+                                   mFactory->IsChrome(),
+                                   mFactory->InnerWindowID());
   } else {
-    category.AssignLiteral("content ");
+    nsRefPtr<LogWarningRunnable> runnable =
+      new LogWarningRunnable(aMessageName,
+                             aFilename,
+                             aLineNumber,
+                             mFactory->IsChrome(),
+                             mFactory->InnerWindowID());
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
   }
-  category.AppendLiteral("javascript");
-
-  nsCOMPtr<nsIConsoleService> consoleService =
-    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-  MOZ_ASSERT(consoleService);
-
-  nsCOMPtr<nsIScriptError> scriptError =
-    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
-  MOZ_ASSERT(consoleService);
-
-  if (mFactory->GetParentObject()) {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      scriptError->InitWithWindowID(localizedMessage,
-                                    aFilename,
-                                    /* aSourceLine */ EmptyString(),
-                                    aLineNumber,
-                                    /* aColumnNumber */ 0,
-                                    nsIScriptError::warningFlag,
-                                    category,
-                                    mFactory->InnerWindowID())));
-  } else {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      scriptError->Init(localizedMessage,
-                        aFilename,
-                        /* aSourceLine */ EmptyString(),
-                        aLineNumber,
-                        /* aColumnNumber */ 0,
-                        nsIScriptError::warningFlag,
-                        category.get())));
-  }
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError)));
 }
 
 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
 
@@ -1290,25 +1328,57 @@ IDBDatabase::LastRelease()
     mBackgroundActor->SendDeleteMeInternal();
     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
   }
 }
 
 nsresult
 IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor)
 {
-  return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
+  nsresult rv =
+    IndexedDatabaseManager::CommonPostHandleEvent(this, mFactory, aVisitor);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
 }
 
 JSObject*
 IDBDatabase::WrapObject(JSContext* aCx)
 {
   return IDBDatabaseBinding::Wrap(aCx, this);
 }
 
+NS_IMPL_ISUPPORTS(CancelableRunnableWrapper, nsIRunnable, nsICancelableRunnable)
+
+NS_IMETHODIMP
+CancelableRunnableWrapper::Run()
+{
+  nsCOMPtr<nsIRunnable> runnable;
+  mRunnable.swap(runnable);
+
+  if (runnable) {
+    return runnable->Run();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CancelableRunnableWrapper::Cancel()
+{
+  if (mRunnable) {
+    mRunnable = nullptr;
+    return NS_OK;
+  }
+
+  return NS_ERROR_UNEXPECTED;
+}
+
 CreateFileHelper::CreateFileHelper(IDBDatabase* aDatabase,
                                    IDBRequest* aRequest,
                                    const nsAString& aName,
                                    const nsAString& aType,
                                    const nsACString& aOrigin)
   : mDatabase(aDatabase)
   , mRequest(aRequest)
   , mName(aName)
@@ -1481,16 +1551,94 @@ CreateFileHelper::Run()
     mResultCode = rv;
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
 
   return NS_OK;
 }
 
+
+// static
+void
+IDBDatabase::
+LogWarningRunnable::LogWarning(const char* aMessageName,
+                               const nsAString& aFilename,
+                               uint32_t aLineNumber,
+                               bool aIsChrome,
+                               uint64_t aInnerWindowID)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aMessageName);
+
+  nsXPIDLString localizedMessage;
+  if (NS_WARN_IF(NS_FAILED(
+    nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+                                       aMessageName,
+                                       localizedMessage)))) {
+    return;
+  }
+
+  nsAutoCString category;
+  if (aIsChrome) {
+    category.AssignLiteral("chrome ");
+  } else {
+    category.AssignLiteral("content ");
+  }
+  category.AppendLiteral("javascript");
+
+  nsCOMPtr<nsIConsoleService> consoleService =
+    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  MOZ_ASSERT(consoleService);
+
+  nsCOMPtr<nsIScriptError> scriptError =
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+  MOZ_ASSERT(consoleService);
+
+  if (aInnerWindowID) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      scriptError->InitWithWindowID(localizedMessage,
+                                    aFilename,
+                                    /* aSourceLine */ EmptyString(),
+                                    aLineNumber,
+                                    /* aColumnNumber */ 0,
+                                    nsIScriptError::warningFlag,
+                                    category,
+                                    aInnerWindowID)));
+  } else {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      scriptError->Init(localizedMessage,
+                        aFilename,
+                        /* aSourceLine */ EmptyString(),
+                        aLineNumber,
+                        /* aColumnNumber */ 0,
+                        nsIScriptError::warningFlag,
+                        category.get())));
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError)));
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(IDBDatabase::LogWarningRunnable, nsRunnable)
+
+NS_IMETHODIMP
+IDBDatabase::
+LogWarningRunnable::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  LogWarning(mMessageName.get(),
+             mFilename,
+             mLineNumber,
+             mIsChrome,
+             mInnerWindowID);
+
+  return NS_OK;
+}
+
 NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver)
 
 NS_IMETHODIMP
 IDBDatabase::
 Observer::Observe(nsISupports* aSubject,
                   const char* aTopic,
                   const char16_t* aData)
 {
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -47,16 +47,19 @@ class IDBTransaction;
 class PBackgroundIDBDatabaseFileChild;
 
 class IDBDatabase MOZ_FINAL
   : public IDBWrapperCache
 {
   typedef mozilla::dom::StorageType StorageType;
   typedef mozilla::dom::quota::PersistenceType PersistenceType;
 
+  class LogWarningRunnable;
+  friend class LogWarningRunnable;
+
   class Observer;
   friend class Observer;
 
   // The factory must be kept alive when IndexedDB is used in multiple
   // processes. If it dies then the entire actor tree will be destroyed with it
   // and the world will explode.
   nsRefPtr<IDBFactory> mFactory;
 
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -28,28 +28,22 @@ const char16_t* kUpgradeNeededEventType 
 const char16_t* kVersionChangeEventType = MOZ_UTF16("versionchange");
 
 already_AddRefed<nsIDOMEvent>
 CreateGenericEvent(EventTarget* aOwner,
                    const nsDependentString& aType,
                    Bubbles aBubbles,
                    Cancelable aCancelable)
 {
-  nsCOMPtr<nsIDOMEvent> event;
-  nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), aOwner, nullptr, nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
+  nsRefPtr<Event> event = new Event(aOwner, nullptr, nullptr);
 
-  rv = event->InitEvent(aType,
-                        aBubbles == eDoesBubble ? true : false,
-                        aCancelable == eCancelable ? true : false);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    event->InitEvent(aType,
+                     aBubbles == eDoesBubble ? true : false,
+                     aCancelable == eCancelable ? true : false)));
 
   event->SetTrusted(true);
 
   return event.forget();
 }
 
 // static
 already_AddRefed<IDBVersionChangeEvent>
@@ -59,20 +53,17 @@ IDBVersionChangeEvent::CreateInternal(Ev
                                       Nullable<uint64_t> aNewVersion)
 {
   nsRefPtr<IDBVersionChangeEvent> event =
     new IDBVersionChangeEvent(aOwner, aOldVersion);
   if (!aNewVersion.IsNull()) {
     event->mNewVersion.SetValue(aNewVersion.Value());
   }
 
-  nsresult rv = event->InitEvent(aType, false, false);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(event->InitEvent(aType, false, false)));
 
   event->SetTrusted(true);
 
   return event.forget();
 }
 
 already_AddRefed<IDBVersionChangeEvent>
 IDBVersionChangeEvent::Constructor(const GlobalObject& aGlobal,
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -13,22 +13,28 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackground.h"
 #include "mozilla/ipc/PBackgroundChild.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsAboutProtocolUtils.h"
+#include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
+#include "nsIAboutModule.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsILoadContext.h"
 #include "nsIPrincipal.h"
+#include "nsIURI.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIWebNavigation.h"
+#include "nsSandboxFlags.h"
 #include "nsServiceManagerUtils.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 #ifdef DEBUG
@@ -41,43 +47,16 @@ namespace indexedDB {
 
 using namespace mozilla::dom::quota;
 using namespace mozilla::ipc;
 
 namespace {
 
 const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
 
-nsresult
-GetPrincipalInfoFromPrincipal(nsIPrincipal* aPrincipal,
-                              PrincipalInfo* aPrincipalInfo)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aPrincipal);
-  MOZ_ASSERT(aPrincipalInfo);
-
-  bool isNullPrincipal;
-  nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (isNullPrincipal) {
-    NS_WARNING("IndexedDB not supported from this principal!");
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
-  rv = PrincipalToPrincipalInfo(aPrincipal, aPrincipalInfo);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
-}
-
 } // anonymous namespace
 
 class IDBFactory::BackgroundCreateCallback MOZ_FINAL
   : public nsIIPCBackgroundChildCreateCallback
 {
   nsRefPtr<IDBFactory> mFactory;
   LoggingInfo mLoggingInfo;
 
@@ -150,54 +129,53 @@ IDBFactory::CreateForWindow(nsPIDOMWindo
   MOZ_ASSERT(aWindow->IsInnerWindow());
   MOZ_ASSERT(aFactory);
 
   if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
     *aFactory = nullptr;
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
-  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
-  if (NS_WARN_IF(!sop)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = AllowedForWindowInternal(aWindow, getter_AddRefs(principal));
+  if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) {
+    NS_WARNING("IndexedDB is not permitted in a third-party window.");
+    *aFactory = nullptr;
+    return NS_OK;
   }
 
-  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
-  if (NS_WARN_IF(!principal)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    if (rv == NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR) {
+      IDB_REPORT_INTERNAL_ERR();
+    }
+    return rv;
+  }
+
+  MOZ_ASSERT(principal);
+
+  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
+  rv = PrincipalToPrincipalInfo(principal, principalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
-  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
-
-  if (NS_WARN_IF(NS_FAILED(GetPrincipalInfoFromPrincipal(principal,
-                                                         principalInfo)))) {
-    // Not allowed.
-    *aFactory = nullptr;
-    return NS_OK;
-  }
-
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
-  if (NS_WARN_IF(!mgr)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
+  MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
+             principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
 
   nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
 
-  bool privateBrowsingMode = loadContext && loadContext->UsePrivateBrowsing();
-
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mPrincipalInfo = Move(principalInfo);
   factory->mWindow = aWindow;
   factory->mTabChild = TabChild::GetFrom(aWindow);
   factory->mInnerWindowID = aWindow->WindowID();
-  factory->mPrivateBrowsingMode = privateBrowsingMode;
+  factory->mPrivateBrowsingMode =
+    loadContext && loadContext->UsePrivateBrowsing();
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
 IDBFactory::CreateForChromeJS(JSContext* aCx,
@@ -206,88 +184,246 @@ IDBFactory::CreateForChromeJS(JSContext*
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(nsContentUtils::IsCallerChrome());
 
   nsAutoPtr<PrincipalInfo> principalInfo(
     new PrincipalInfo(SystemPrincipalInfo()));
 
   nsresult rv =
-    CreateForJSInternal(aCx, aOwningObject, principalInfo, aFactory);
+    CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(!principalInfo);
 
   return NS_OK;
 }
 
+// static
 nsresult
 IDBFactory::CreateForDatastore(JSContext* aCx,
                                JS::Handle<JSObject*> aOwningObject,
                                IDBFactory** aFactory)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // There should be a null principal pushed here, but it's still chrome...
   MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
 
   nsAutoPtr<PrincipalInfo> principalInfo(
     new PrincipalInfo(SystemPrincipalInfo()));
 
   nsresult rv =
-    CreateForJSInternal(aCx, aOwningObject, principalInfo, aFactory);
+    CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(!principalInfo);
 
   return NS_OK;
 }
 
 // static
 nsresult
-IDBFactory::CreateForJSInternal(JSContext* aCx,
-                                JS::Handle<JSObject*> aOwningObject,
-                                nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
-                                IDBFactory** aFactory)
+IDBFactory::CreateForWorker(JSContext* aCx,
+                            JS::Handle<JSObject*> aOwningObject,
+                            const PrincipalInfo& aPrincipalInfo,
+                            uint64_t aInnerWindowID,
+                            IDBFactory** aFactory)
 {
-  MOZ_ASSERT(aCx);
-  MOZ_ASSERT(aOwningObject);
-  MOZ_ASSERT(aPrincipalInfo);
-  MOZ_ASSERT(aFactory);
-  MOZ_ASSERT(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
-             "Not a global object!");
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None);
+
+  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo(aPrincipalInfo));
+
+  nsresult rv =
+    CreateForJSInternal(aCx,
+                        aOwningObject,
+                        principalInfo,
+                        aInnerWindowID,
+                        aFactory);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  if (!NS_IsMainThread()) {
-    MOZ_CRASH("Not yet supported off the main thread!");
-  }
+  MOZ_ASSERT(!principalInfo);
+
+  return NS_OK;
+}
+
+// static
+nsresult
+IDBFactory::CreateForMainThreadJSInternal(
+                                       JSContext* aCx,
+                                       JS::Handle<JSObject*> aOwningObject,
+                                       nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
+                                       IDBFactory** aFactory)
+{
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
     *aFactory = nullptr;
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
   if (NS_WARN_IF(!mgr)) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  nsresult rv =
+    CreateForJSInternal(aCx,
+                        aOwningObject,
+                        aPrincipalInfo,
+                        /* aInnerWindowID */ 0,
+                        aFactory);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+// static
+nsresult
+IDBFactory::CreateForJSInternal(JSContext* aCx,
+                                JS::Handle<JSObject*> aOwningObject,
+                                nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
+                                uint64_t aInnerWindowID,
+                                IDBFactory** aFactory)
+{
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aOwningObject);
+  MOZ_ASSERT(aPrincipalInfo);
+  MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None);
+  MOZ_ASSERT(aFactory);
+  MOZ_ASSERT(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
+             "Not a global object!");
+
+  if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
+      aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
+    NS_WARNING("IndexedDB not allowed for this principal!");
+    *aFactory = nullptr;
+    return NS_OK;
+  }
+
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mPrincipalInfo = aPrincipalInfo.forget();
   factory->mOwningObject = aOwningObject;
   mozilla::HoldJSObjects(factory.get());
+  factory->mInnerWindowID = aInnerWindowID;
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
+// static
+bool
+IDBFactory::AllowedForWindow(nsPIDOMWindow* aWindow)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = AllowedForWindowInternal(aWindow, getter_AddRefs(principal));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return true;
+}
+
+// static
+nsresult
+IDBFactory::AllowedForWindowInternal(nsPIDOMWindow* aWindow,
+                                     nsIPrincipal** aPrincipal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+
+  if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  nsIDocument* document = aWindow->GetExtantDoc();
+  if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
+  MOZ_ASSERT(sop);
+
+  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
+  if (NS_WARN_IF(!principal)) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  if (nsContentUtils::IsSystemPrincipal(principal)) {
+    principal.forget(aPrincipal);
+    return NS_OK;
+  }
+
+  bool isNullPrincipal;
+  if (NS_WARN_IF(NS_FAILED(principal->GetIsNullPrincipal(&isNullPrincipal))) ||
+      isNullPrincipal) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  // Whitelist about:home, since it doesn't have a base domain it would not
+  // pass the ThirdPartyUtil check, though it should be able to use indexedDB.
+  bool skipThirdPartyCheck = false;
+
+  nsCOMPtr<nsIURI> uri;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(principal->GetURI(getter_AddRefs(uri))));
+
+  bool isAbout;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)));
+
+  if (isAbout) {
+    nsCOMPtr<nsIAboutModule> module;
+    if (NS_SUCCEEDED(NS_GetAboutModule(uri, getter_AddRefs(module)))) {
+      uint32_t flags;
+      if (NS_SUCCEEDED(module->GetURIFlags(uri, &flags))) {
+        skipThirdPartyCheck = flags & nsIAboutModule::ENABLE_INDEXED_DB;
+      } else {
+        NS_WARNING("GetURIFlags failed!");
+      }
+    } else {
+      NS_WARNING("NS_GetAboutModule failed!");
+    }
+  }
+
+  if (!skipThirdPartyCheck) {
+    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+      do_GetService(THIRDPARTYUTIL_CONTRACTID);
+    MOZ_ASSERT(thirdPartyUtil);
+
+    bool isThirdParty;
+    if (NS_WARN_IF(NS_FAILED(
+          thirdPartyUtil->IsThirdPartyWindow(aWindow,
+                                             nullptr,
+                                             &isThirdParty)))) {
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    if (isThirdParty) {
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+  }
+
+  principal.forget(aPrincipal);
+  return NS_OK;
+}
+
 #ifdef DEBUG
 
 void
 IDBFactory::AssertIsOnOwningThread() const
 {
   MOZ_ASSERT(mOwningThread);
   MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
 }
@@ -463,18 +599,25 @@ IDBFactory::OpenInternal(nsIPrincipal* a
   PrincipalInfo& principalInfo = commonParams.principalInfo();
 
   if (aPrincipal) {
     if (!NS_IsMainThread()) {
       MOZ_CRASH("Figure out security checks for workers!");
     }
     MOZ_ASSERT(nsContentUtils::IsCallerChrome());
 
-    if (NS_WARN_IF(NS_FAILED(GetPrincipalInfoFromPrincipal(aPrincipal,
-                                                           &principalInfo)))) {
+    if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
+                                                      &principalInfo)))) {
+      IDB_REPORT_INTERNAL_ERR();
+      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      return nullptr;
+    }
+
+    if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
+        principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
       IDB_REPORT_INTERNAL_ERR();
       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
       return nullptr;
     }
   } else {
     principalInfo = *mPrincipalInfo;
   }
 
@@ -621,18 +764,16 @@ IDBFactory::BackgroundActorCreated(PBack
 {
   MOZ_ASSERT(aBackgroundActor);
   MOZ_ASSERT(!mBackgroundActor);
   MOZ_ASSERT(!mBackgroundActorFailed);
 
   {
     BackgroundFactoryChild* actor = new BackgroundFactoryChild(this);
 
-    MOZ_ASSERT(NS_IsMainThread(), "Fix this windowId stuff for workers!");
-
     mBackgroundActor =
       static_cast<BackgroundFactoryChild*>(
         aBackgroundActor->SendPBackgroundIDBFactoryConstructor(actor,
                                                                aLoggingInfo));
   }
 
   if (NS_WARN_IF(!mBackgroundActor)) {
     BackgroundActorFailed();
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -30,16 +30,17 @@ namespace ipc {
 class PBackgroundChild;
 class PrincipalInfo;
 
 } // namespace ipc
 
 namespace dom {
 
 struct IDBOpenDBOptions;
+template <typename> class Optional;
 class TabChild;
 
 namespace indexedDB {
 
 class BackgroundFactoryChild;
 class FactoryRequestParams;
 class IDBOpenDBRequest;
 class LoggingInfo;
@@ -89,16 +90,26 @@ public:
                     JS::Handle<JSObject*> aOwningObject,
                     IDBFactory** aFactory);
 
   static nsresult
   CreateForDatastore(JSContext* aCx,
                     JS::Handle<JSObject*> aOwningObject,
                     IDBFactory** aFactory);
 
+  static nsresult
+  CreateForWorker(JSContext* aCx,
+                  JS::Handle<JSObject*> aOwningObject,
+                  const PrincipalInfo& aPrincipalInfo,
+                  uint64_t aInnerWindowID,
+                  IDBFactory** aFactory);
+
+  static bool
+  AllowedForWindow(nsPIDOMWindow* aWindow);
+
   void
   AssertIsOnOwningThread() const
 #ifdef DEBUG
   ;
 #else
   { }
 #endif
 
@@ -193,21 +204,32 @@ public:
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
 private:
   IDBFactory();
   ~IDBFactory();
 
   static nsresult
+  CreateForMainThreadJSInternal(JSContext* aCx,
+                                JS::Handle<JSObject*> aOwningObject,
+                                nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
+                                IDBFactory** aFactory);
+
+  static nsresult
   CreateForJSInternal(JSContext* aCx,
                       JS::Handle<JSObject*> aOwningObject,
                       nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
+                      uint64_t aInnerWindowID,
                       IDBFactory** aFactory);
 
+  static nsresult
+  AllowedForWindowInternal(nsPIDOMWindow* aWindow,
+                           nsIPrincipal** aPrincipal);
+
   already_AddRefed<IDBOpenDBRequest>
   OpenInternal(nsIPrincipal* aPrincipal,
                const nsAString& aName,
                const Optional<uint64_t>& aVersion,
                const Optional<StorageType>& aStorageType,
                bool aDeleting,
                ErrorResult& aRv);
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -38,25 +38,28 @@
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsCOMPtr.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
+#include "WorkerPrivate.h"
+#include "WorkerScope.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 using namespace mozilla::dom::quota;
+using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 
 struct IDBObjectStore::StructuredCloneWriteInfo
 {
   struct BlobOrFileInfo
   {
     nsRefPtr<File> mBlob;
     nsRefPtr<FileInfo> mFileInfo;
@@ -290,18 +293,16 @@ StructuredCloneWriteCallback(JSContext* 
     IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo*
       newBlobOrFileInfo =
         cloneWriteInfo->mBlobOrFileInfos.AppendElement();
     newBlobOrFileInfo->mFileInfo.swap(fileInfo);
 
     return true;
   }
 
-  MOZ_ASSERT(NS_IsMainThread(), "This can't work off the main thread!");
-
   {
     File* blob = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
       uint64_t size;
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blob->GetSize(&size)));
 
       size = NativeEndian::swapToLittleEndian(size);
 
@@ -600,26 +601,33 @@ public:
                           const BlobOrFileData& aData,
                           JS::MutableHandle<JSObject*> aResult)
   {
     MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
                aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
                aData.tag == SCTAG_DOM_BLOB);
     MOZ_ASSERT(aFile.mFile);
 
-    MOZ_ASSERT(NS_IsMainThread(),
-               "This wrapping currently only works on the main thread!");
-
     // It can happen that this IDB is chrome code, so there is no parent, but
     // still we want to set a correct parent for the new File object.
     nsCOMPtr<nsISupports> parent;
-    if (aDatabase && aDatabase->GetParentObject()) {
-      parent = aDatabase->GetParentObject();
+    if (NS_IsMainThread()) {
+      if (aDatabase && aDatabase->GetParentObject()) {
+        parent = aDatabase->GetParentObject();
+      } else {
+        parent = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+      }
     } else {
-      parent  = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+      MOZ_ASSERT(workerPrivate);
+
+      WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
+      MOZ_ASSERT(globalScope);
+
+      parent = do_QueryObject(globalScope);
     }
 
     MOZ_ASSERT(parent);
     nsRefPtr<File> file = new File(parent, aFile.mFile->Impl());
 
     if (aData.tag == SCTAG_DOM_BLOB) {
       if (NS_WARN_IF(!ResolveMysteryBlob(aFile.mFile,
                                          aData.type,
@@ -969,19 +977,16 @@ IDBObjectStore::ClearCloneReadInfo(Struc
 {
   // This is kind of tricky, we only want to release stuff on the main thread,
   // but we can end up being called on other threads if we have already been
   // cleared on the main thread.
   if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
     return;
   }
 
-  // If there's something to clear, we should be on the main thread.
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
   ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer);
   aReadInfo.mFiles.Clear();
 }
 
 // static
 bool
 IDBObjectStore::DeserializeValue(JSContext* aCx,
                                  StructuredCloneReadInfo& aCloneReadInfo,
@@ -1510,18 +1515,16 @@ IDBObjectStore::GetParentObject() const
   return mTransaction->GetParentObject();
 }
 
 void
 IDBObjectStore::GetKeyPath(JSContext* aCx,
                            JS::MutableHandle<JS::Value> aResult,
                            ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
   if (!mCachedKeyPath.isUndefined()) {
     JS::ExposeValueToActiveJS(mCachedKeyPath);
     aResult.set(mCachedKeyPath);
     return;
   }
 
   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   if (NS_WARN_IF(aRv.Failed())) {
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -9,38 +9,43 @@
 #include "BackgroundChildImpl.h"
 #include "IDBCursor.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
+#include "IndexedDatabaseManager.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/Move.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIScriptContext.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "ReportInternalError.h"
+#include "WorkerFeature.h"
+#include "WorkerPrivate.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
+using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 
 IDBRequest::IDBRequest(IDBDatabase* aDatabase)
   : IDBWrapperCache(aDatabase)
 {
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
 
@@ -218,19 +223,17 @@ IDBRequest::DispatchNonTransactionError(
   SetError(aErrorCode);
 
   // Make an error event and fire it at the target.
   nsCOMPtr<nsIDOMEvent> event =
     CreateGenericEvent(this,
                        nsDependentString(kErrorEventType),
                        eDoesBubble,
                        eCancelable);
-  if (NS_WARN_IF(!event)) {
-    return;
-  }
+  MOZ_ASSERT(event);
 
   bool ignored;
   if (NS_FAILED(DispatchEvent(event, &ignored))) {
     NS_WARNING("Failed to dispatch event!");
   }
 }
 
 void
@@ -254,16 +257,25 @@ nsresult
 IDBRequest::GetErrorCode() const
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mHaveResultOrErrorCode);
 
   return mErrorCode;
 }
 
+DOMError*
+IDBRequest::GetErrorAfterResult() const
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mHaveResultOrErrorCode);
+
+  return mError;
+}
+
 #endif // DEBUG
 
 void
 IDBRequest::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aLineNo);
 
@@ -414,16 +426,46 @@ IDBRequest::PreHandleEvent(EventChainPre
 {
   AssertIsOnOwningThread();
 
   aVisitor.mCanHandle = true;
   aVisitor.mParentTarget = mTransaction;
   return NS_OK;
 }
 
+class IDBOpenDBRequest::WorkerFeature MOZ_FINAL
+  : public mozilla::dom::workers::WorkerFeature
+{
+  WorkerPrivate* mWorkerPrivate;
+
+public:
+  explicit
+  WorkerFeature(WorkerPrivate* aWorkerPrivate)
+    : mWorkerPrivate(aWorkerPrivate)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    MOZ_COUNT_CTOR(IDBOpenDBRequest::WorkerFeature);
+  }
+
+  ~WorkerFeature()
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    MOZ_COUNT_DTOR(IDBOpenDBRequest::WorkerFeature);
+
+    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+  }
+
+private:
+  virtual bool
+  Notify(JSContext* aCx, Status aStatus) MOZ_OVERRIDE;
+};
+
 IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner)
   : IDBRequest(aOwner)
   , mFactory(aFactory)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aFactory);
 
   // aOwner may be null.
@@ -462,29 +504,57 @@ IDBOpenDBRequest::CreateForJS(IDBFactory
   aFactory->AssertIsOnOwningThread();
   MOZ_ASSERT(aScriptOwner);
 
   nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, nullptr);
   CaptureCaller(request->mFilename, &request->mLineNo);
 
   request->SetScriptOwner(aScriptOwner);
 
+  if (!NS_IsMainThread()) {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    workerPrivate->AssertIsOnWorkerThread();
+
+    JSContext* cx = workerPrivate->GetJSContext();
+    MOZ_ASSERT(cx);
+
+    nsAutoPtr<WorkerFeature> feature(new WorkerFeature(workerPrivate));
+    if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
+      return nullptr;
+    }
+
+    request->mWorkerFeature = Move(feature);
+  }
+
   return request.forget();
 }
 
 void
 IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction)
 {
   AssertIsOnOwningThread();
 
   MOZ_ASSERT(!aTransaction || !mTransaction);
 
   mTransaction = aTransaction;
 }
 
+void
+IDBOpenDBRequest::NoteComplete()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerFeature);
+
+  // If we have a WorkerFeature installed on the worker then nulling this out
+  // will uninstall it from the worker.
+  mWorkerFeature = nullptr;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest,
                                                   IDBRequest)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest,
@@ -496,25 +566,42 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
 NS_INTERFACE_MAP_END_INHERITING(IDBRequest)
 
 NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest)
 NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest)
 
 nsresult
 IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor)
 {
-  // XXX Fix me!
-  MOZ_ASSERT(NS_IsMainThread());
+  nsresult rv =
+    IndexedDatabaseManager::CommonPostHandleEvent(this, mFactory, aVisitor);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
+  return NS_OK;
 }
 
 JSObject*
 IDBOpenDBRequest::WrapObject(JSContext* aCx)
 {
   AssertIsOnOwningThread();
 
   return IDBOpenDBRequestBinding::Wrap(aCx, this);
 }
 
+bool
+IDBOpenDBRequest::
+WorkerFeature::Notify(JSContext* aCx, Status aStatus)
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(aStatus > Running);
+
+  // There's nothing we can really do here at the moment...
+  NS_WARNING("Worker closing but IndexedDB is waiting to open a database!");
+
+  return true;
+}
+
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -110,16 +110,26 @@ public:
   ;
 #else
   {
     return mErrorCode;
   }
 #endif
 
   DOMError*
+  GetErrorAfterResult() const
+#ifdef DEBUG
+  ;
+#else
+  {
+    return mError;
+  }
+#endif
+
+  DOMError*
   GetError(ErrorResult& aRv);
 
   void
   GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
 
   bool
   IsPending() const
   {
@@ -207,42 +217,43 @@ public:
 protected:
   ResultCallback()
   { }
 };
 
 class IDBOpenDBRequest MOZ_FINAL
   : public IDBRequest
 {
+  class WorkerFeature;
+
   // Only touched on the owning thread.
   nsRefPtr<IDBFactory> mFactory;
 
+  nsAutoPtr<WorkerFeature> mWorkerFeature;
+
 public:
   static already_AddRefed<IDBOpenDBRequest>
   CreateForWindow(IDBFactory* aFactory,
                   nsPIDOMWindow* aOwner,
                   JS::Handle<JSObject*> aScriptOwner);
 
   static already_AddRefed<IDBOpenDBRequest>
   CreateForJS(IDBFactory* aFactory,
               JS::Handle<JSObject*> aScriptOwner);
 
   void
   SetTransaction(IDBTransaction* aTransaction);
 
+  void
+  NoteComplete();
+
   // nsIDOMEventTarget
   virtual nsresult
   PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE;
 
-  DOMError*
-  GetError(ErrorResult& aRv)
-  {
-    return IDBRequest::GetError(aRv);
-  }
-
   IDBFactory*
   Factory() const
   {
     return mFactory;
   }
 
   IMPL_EVENT_HANDLER(blocked);
   IMPL_EVENT_HANDLER(upgradeneeded);
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -19,47 +19,111 @@
 #include "nsIAppShell.h"
 #include "nsIDOMFile.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTHashtable.h"
 #include "nsWidgetsCID.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
+#include "WorkerFeature.h"
+#include "WorkerPrivate.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
+using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 
 namespace {
 
 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
+bool
+RunBeforeNextEvent(IDBTransaction* aTransaction)
+{
+  MOZ_ASSERT(aTransaction);
+
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+    MOZ_ASSERT(appShell);
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appShell->RunBeforeNextEvent(aTransaction)));
+
+    return true;
+  }
+
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  if (NS_WARN_IF(!workerPrivate->RunBeforeNextEvent(aTransaction))) {
+    return false;
+  }
+
+  return true;
+}
+
 } // anonymous namespace
 
+class IDBTransaction::WorkerFeature MOZ_FINAL
+  : public mozilla::dom::workers::WorkerFeature
+{
+  WorkerPrivate* mWorkerPrivate;
+
+  // The IDBTransaction owns this object so we only need a weak reference back
+  // to it.
+  IDBTransaction* mTransaction;
+
+public:
+  WorkerFeature(WorkerPrivate* aWorkerPrivate, IDBTransaction* aTransaction)
+    : mWorkerPrivate(aWorkerPrivate)
+    , mTransaction(aTransaction)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aTransaction);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    aTransaction->AssertIsOnOwningThread();
+
+    MOZ_COUNT_CTOR(IDBTransaction::WorkerFeature);
+  }
+
+  ~WorkerFeature()
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    MOZ_COUNT_DTOR(IDBTransaction::WorkerFeature);
+
+    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+  }
+
+private:
+  virtual bool
+  Notify(JSContext* aCx, Status aStatus) MOZ_OVERRIDE;
+};
+
 IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
                                const nsTArray<nsString>& aObjectStoreNames,
                                Mode aMode)
   : IDBWrapperCache(aDatabase)
   , mDatabase(aDatabase)
   , mObjectStoreNames(aObjectStoreNames)
   , mLoggingSerialNumber(0)
   , mNextObjectStoreId(0)
   , mNextIndexId(0)
   , mAbortCode(NS_OK)
   , mPendingRequestCount(0)
   , mLineNo(0)
   , mReadyState(IDBTransaction::INITIAL)
   , mMode(aMode)
   , mCreating(false)
+  , mRegistered(false)
   , mAbortedByScript(false)
 #ifdef DEBUG
   , mSentCommitOrAbort(false)
   , mFiredCompleteOrAbort(false)
 #endif
 {
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
@@ -105,21 +169,26 @@ IDBTransaction::~IDBTransaction()
   MOZ_ASSERT(mSentCommitOrAbort);
   MOZ_ASSERT_IF(mMode == VERSION_CHANGE &&
                   mBackgroundActor.mVersionChangeBackgroundActor,
                 mFiredCompleteOrAbort);
   MOZ_ASSERT_IF(mMode != VERSION_CHANGE &&
                   mBackgroundActor.mNormalBackgroundActor,
                 mFiredCompleteOrAbort);
 
-  mDatabase->UnregisterTransaction(this);
+  if (mRegistered) {
+    mDatabase->UnregisterTransaction(this);
+#ifdef DEBUG
+    mRegistered = false;
+#endif
+  }
 
   if (mMode == VERSION_CHANGE) {
     if (auto* actor = mBackgroundActor.mVersionChangeBackgroundActor) {
-      actor->SendDeleteMeInternal();
+      actor->SendDeleteMeInternal(/* aFailedConstructor */ false);
 
       MOZ_ASSERT(!mBackgroundActor.mVersionChangeBackgroundActor,
                  "SendDeleteMeInternal should have cleared!");
     }
   } else if (auto* actor = mBackgroundActor.mNormalBackgroundActor) {
     actor->SendDeleteMeInternal();
 
     MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor,
@@ -146,32 +215,34 @@ IDBTransaction::CreateVersionChange(
   nsTArray<nsString> emptyObjectStoreNames;
 
   nsRefPtr<IDBTransaction> transaction =
     new IDBTransaction(aDatabase, emptyObjectStoreNames, VERSION_CHANGE);
   aOpenRequest->GetCallerLocation(transaction->mFilename,
                                   &transaction->mLineNo);
 
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
+
+  if (NS_WARN_IF(!RunBeforeNextEvent(transaction))) {
+    MOZ_ASSERT(!NS_IsMainThread());
+#ifdef DEBUG
+    // Silence assertions.
+    transaction->mSentCommitOrAbort = true;
+#endif
+    aActor->SendDeleteMeInternal(/* aFailedConstructor */ true);
+    return nullptr;
+  }
+
   transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
   transaction->mNextObjectStoreId = aNextObjectStoreId;
   transaction->mNextIndexId = aNextIndexId;
-
-  // XXX Fix!
-  MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
-
-  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
-  if (NS_WARN_IF(!appShell) ||
-      NS_WARN_IF(NS_FAILED(appShell->RunBeforeNextEvent(transaction)))) {
-    return nullptr;
-  }
-
   transaction->mCreating = true;
 
   aDatabase->RegisterTransaction(transaction);
+  transaction->mRegistered = true;
 
   return transaction.forget();
 }
 
 // static
 already_AddRefed<IDBTransaction>
 IDBTransaction::Create(IDBDatabase* aDatabase,
                        const nsTArray<nsString>& aObjectStoreNames,
@@ -183,28 +254,38 @@ IDBTransaction::Create(IDBDatabase* aDat
   MOZ_ASSERT(aMode == READ_ONLY || aMode == READ_WRITE);
 
   nsRefPtr<IDBTransaction> transaction =
     new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
   IDBRequest::CaptureCaller(transaction->mFilename, &transaction->mLineNo);
 
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
 
-  // XXX Fix!
-  MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
-
-  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
-  if (NS_WARN_IF(!appShell) ||
-      NS_WARN_IF(NS_FAILED(appShell->RunBeforeNextEvent(transaction)))) {
+  if (NS_WARN_IF(!RunBeforeNextEvent(transaction))) {
+    MOZ_ASSERT(!NS_IsMainThread());
     return nullptr;
   }
 
   transaction->mCreating = true;
 
   aDatabase->RegisterTransaction(transaction);
+  transaction->mRegistered = true;
+
+  if (!NS_IsMainThread()) {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    workerPrivate->AssertIsOnWorkerThread();
+
+    JSContext* cx = workerPrivate->GetJSContext();
+    MOZ_ASSERT(cx);
+
+    transaction->mWorkerFeature = new WorkerFeature(workerPrivate, transaction);
+    MOZ_ALWAYS_TRUE(workerPrivate->AddFeature(cx, transaction->mWorkerFeature));
+  }
 
   return transaction.forget();
 }
 
 // static
 IDBTransaction*
 IDBTransaction::GetCurrent()
 {
@@ -674,35 +755,36 @@ IDBTransaction::FireCompleteOrAbortEvent
   MOZ_ASSERT(!mFiredCompleteOrAbort);
 
   mReadyState = DONE;
 
 #ifdef DEBUG
   mFiredCompleteOrAbort = true;
 #endif
 
+  // Make sure we drop the WorkerFeature when this function completes.
+  nsAutoPtr<WorkerFeature> workerFeature = Move(mWorkerFeature);
+
   nsCOMPtr<nsIDOMEvent> event;
   if (NS_SUCCEEDED(aResult)) {
     event = CreateGenericEvent(this,
                                nsDependentString(kCompleteEventType),
                                eDoesNotBubble,
                                eNotCancelable);
+    MOZ_ASSERT(event);
   } else {
     if (!mError && !mAbortedByScript) {
       mError = new DOMError(GetOwner(), aResult);
     }
 
     event = CreateGenericEvent(this,
                                nsDependentString(kAbortEventType),
                                eDoesBubble,
                                eNotCancelable);
-  }
-
-  if (NS_WARN_IF(!event)) {
-    return;
+    MOZ_ASSERT(event);
   }
 
   if (NS_SUCCEEDED(mAbortCode)) {
     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
                    "Firing 'complete' event",
                  "IndexedDB %s: C T[%lld]: IDBTransaction 'complete' event",
                  IDB_LOG_ID_STRING(),
                  mLoggingSerialNumber);
@@ -905,11 +987,32 @@ IDBTransaction::Run()
     mReadyState = DONE;
 
     SendCommit();
   }
 
   return NS_OK;
 }
 
+bool
+IDBTransaction::
+WorkerFeature::Notify(JSContext* aCx, Status aStatus)
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(aStatus > Running);
+
+  if (mTransaction && aStatus > Terminating) {
+    mTransaction->AssertIsOnOwningThread();
+
+    nsRefPtr<IDBTransaction> transaction = mTransaction;
+    mTransaction = nullptr;
+
+    IDB_REPORT_INTERNAL_ERR();
+    transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, nullptr);
+  }
+
+  return true;
+}
+
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -44,16 +44,19 @@ class ObjectStoreSpec;
 class OpenCursorParams;
 class PBackgroundIDBDatabaseFileChild;
 class RequestParams;
 
 class IDBTransaction MOZ_FINAL
   : public IDBWrapperCache
   , public nsIRunnable
 {
+  class WorkerFeature;
+  friend class WorkerFeature;
+
 public:
   enum Mode
   {
     READ_ONLY = 0,
     READ_WRITE,
     VERSION_CHANGE,
 
     // Only needed for IPC serialization helper, should never be used in code.
@@ -69,16 +72,17 @@ public:
   };
 
 private:
   nsRefPtr<IDBDatabase> mDatabase;
   nsRefPtr<DOMError> mError;
   nsTArray<nsString> mObjectStoreNames;
   nsTArray<nsRefPtr<IDBObjectStore>> mObjectStores;
   nsTArray<nsRefPtr<IDBObjectStore>> mDeletedObjectStores;
+  nsAutoPtr<WorkerFeature> mWorkerFeature;
 
   // Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be
   // a BackgroundVersionChangeTransactionChild. Otherwise it will be a
   // BackgroundTransactionChild.
   union {
     BackgroundTransactionChild* mNormalBackgroundActor;
     BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor;
   } mBackgroundActor;
@@ -94,16 +98,17 @@ private:
 
   nsString mFilename;
   uint32_t mLineNo;
 
   ReadyState mReadyState;
   Mode mMode;
 
   bool mCreating;
+  bool mRegistered;
   bool mAbortedByScript;
 
 #ifdef DEBUG
   bool mSentCommitOrAbort;
   bool mFiredCompleteOrAbort;
 #endif
 
 public:
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -11,39 +11,42 @@
 #include "nsIFile.h"
 #include "nsIObserverService.h"
 #include "nsIScriptError.h"
 
 #include "jsapi.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/ContentEvents.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/Utilities.h"
 #include "mozilla/dom/TabContext.h"
-#include "mozilla/EventDispatcher.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
-#include "mozilla/Services.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/storage.h"
 #include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
 #include "IDBRequest.h"
 #include "ProfilerHelpers.h"
+#include "WorkerScope.h"
+#include "WorkerPrivate.h"
 
 // Bindings for ResolveConstructors
 #include "mozilla/dom/IDBCursorBinding.h"
 #include "mozilla/dom/IDBDatabaseBinding.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/IDBIndexBinding.h"
 #include "mozilla/dom/IDBKeyRangeBinding.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
@@ -60,16 +63,17 @@
 #define LOW_DISK_SPACE_DATA_FULL "full"
 #define LOW_DISK_SPACE_DATA_FREE "free"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 using namespace mozilla::dom::quota;
+using namespace mozilla::dom::workers;
 
 class FileManagerInfo
 {
 public:
   already_AddRefed<FileManager>
   GetFileManager(PersistenceType aPersistenceType,
                  const nsAString& aName) const;
 
@@ -111,35 +115,37 @@ private:
   nsTArray<nsRefPtr<FileManager> > mDefaultStorageFileManagers;
 };
 
 namespace {
 
 #define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
 
 const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
+const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
 
 #define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
 
 const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
 const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
 
 #if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
 const char kPrefLoggingProfiler[] =
   IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
 #endif
 
 #undef IDB_PREF_LOGGING_BRANCH_ROOT
 #undef IDB_PREF_BRANCH_ROOT
 
-mozilla::StaticRefPtr<IndexedDatabaseManager> gDBManager;
+StaticRefPtr<IndexedDatabaseManager> gDBManager;
 
-mozilla::Atomic<bool> gInitialized(false);
-mozilla::Atomic<bool> gClosed(false);
-mozilla::Atomic<bool> gTestingMode(false);
+Atomic<bool> gInitialized(false);
+Atomic<bool> gClosed(false);
+Atomic<bool> gTestingMode(false);
+Atomic<bool> gExperimentalFeaturesEnabled(false);
 
 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
   AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
@@ -193,23 +199,22 @@ private:
   int32_t mMemRefCnt;
   int32_t mDBRefCnt;
   int32_t mSliceRefCnt;
   bool mResult;
   bool mWaiting;
 };
 
 void
-TestingPrefChangedCallback(const char* aPrefName, void* aClosure)
+AtomicBoolPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!strcmp(aPrefName, kTestingPref));
-  MOZ_ASSERT(!aClosure);
+  MOZ_ASSERT(aClosure);
 
-  gTestingMode = Preferences::GetBool(aPrefName);
+  *static_cast<Atomic<bool>*>(aClosure) = Preferences::GetBool(aPrefName);
 }
 
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
 : mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -292,38 +297,33 @@ IndexedDatabaseManager::Get()
   return gDBManager;
 }
 
 nsresult
 IndexedDatabaseManager::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  // Make sure that the quota manager is up.
-  QuotaManager* qm = QuotaManager::GetOrCreate();
-  NS_ENSURE_STATE(qm);
-
   // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess
   // directly.
   if (sIsMainProcess) {
-    // Must initialize the storage service on the main thread.
-    nsCOMPtr<mozIStorageService> ss =
-      do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
-    NS_ENSURE_STATE(ss);
-
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     NS_ENSURE_STATE(obs);
 
     nsresult rv =
       obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback,
-                                       kTestingPref);
+  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
+                                       kTestingPref,
+                                       &gTestingMode);
+  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
+                                       kPrefExperimental,
+                                       &gExperimentalFeaturesEnabled);
 
   // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
   // guarantees (unlike synchronous = OFF) atomicity and consistency, but not
   // necessarily durability in situations such as power loss. This preference
   // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
   // durability, but with an extra fsync() and the corresponding performance
   // hit.
   sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");
@@ -344,111 +344,170 @@ void
 IndexedDatabaseManager::Destroy()
 {
   // Setting the closed flag prevents the service from being recreated.
   // Don't set it though if there's no real instance created.
   if (gInitialized && gClosed.exchange(true)) {
     NS_ERROR("Shutdown more than once?!");
   }
 
-  Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref);
+  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
+                                  kTestingPref,
+                                  &gTestingMode);
+  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
+                                  kPrefExperimental,
+                                  &gExperimentalFeaturesEnabled);
 
   Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
                                   kPrefLoggingDetails);
 #ifdef MOZ_ENABLE_PROFILER_SPS
   Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
                                   kPrefLoggingProfiler);
 #endif
   Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
                                   kPrefLoggingEnabled);
 
   delete this;
 }
 
 // static
 nsresult
-IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner,
-                                          EventChainPostVisitor& aVisitor)
+IndexedDatabaseManager::CommonPostHandleEvent(
+                                             DOMEventTargetHelper* aEventTarget,
+                                             IDBFactory* aFactory,
+                                             EventChainPostVisitor& aVisitor)
 {
-  NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
-  if (!aOwner) {
-    return NS_OK;
-  }
+  MOZ_ASSERT(aEventTarget);
+  MOZ_ASSERT(aFactory);
+  MOZ_ASSERT(aVisitor.mDOMEvent);
 
   if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
     return NS_OK;
   }
 
   nsString type;
   nsresult rv = aVisitor.mDOMEvent->GetType(type);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  if (nsDependentString(kErrorEventType) != type) {
+  NS_NAMED_LITERAL_STRING(errorType, "error");
+
+  MOZ_ASSERT(nsDependentString(kErrorEventType) == errorType);
+
+  if (type != errorType) {
     return NS_OK;
   }
 
   nsCOMPtr<EventTarget> eventTarget =
     aVisitor.mDOMEvent->InternalDOMEvent()->GetTarget();
-
-  IDBRequest* request = static_cast<IDBRequest*>(eventTarget.get());
-  NS_ENSURE_TRUE(request, NS_ERROR_UNEXPECTED);
+  MOZ_ASSERT(eventTarget);
 
-  ErrorResult ret;
-  nsRefPtr<DOMError> error = request->GetError(ret);
-  if (ret.Failed()) {
-    return ret.ErrorCode();
-  }
+  auto* request = static_cast<IDBRequest*>(eventTarget.get());
+
+  nsRefPtr<DOMError> error = request->GetErrorAfterResult();
 
   nsString errorName;
   if (error) {
     error->GetName(errorName);
   }
 
   ThreadsafeAutoJSContext cx;
   RootedDictionary<ErrorEventInit> init(cx);
   request->GetCallerLocation(init.mFilename, &init.mLineno);
 
   init.mMessage = errorName;
   init.mCancelable = true;
   init.mBubbles = true;
 
-  nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aOwner));
-  NS_ASSERTION(sgo, "How can this happen?!");
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  if (NS_IsMainThread()) {
+    if (nsPIDOMWindow* window = aEventTarget->GetOwner()) {
+      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
+      MOZ_ASSERT(sgo);
+
+      if (NS_WARN_IF(NS_FAILED(sgo->HandleScriptError(init, &status)))) {
+        status = nsEventStatus_eIgnore;
+      }
+    } else {
+      // We don't fire error events at any global for non-window JS on the main
+      // thread.
+    }
+  } else {
+    // Not on the main thread, must be in a worker.
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
 
-  nsEventStatus status = nsEventStatus_eIgnore;
-  if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
-    NS_WARNING("Failed to dispatch script error event");
-    status = nsEventStatus_eIgnore;
+    nsRefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope();
+    MOZ_ASSERT(globalScope);
+
+    nsRefPtr<ErrorEvent> errorEvent =
+      ErrorEvent::Constructor(globalScope, errorType, init);
+    MOZ_ASSERT(errorEvent);
+
+    errorEvent->SetTrusted(true);
+
+    auto* target = static_cast<EventTarget*>(globalScope.get());
+
+    if (NS_WARN_IF(NS_FAILED(
+      EventDispatcher::DispatchDOMEvent(target,
+                                        /* aWidgetEvent */ nullptr,
+                                        errorEvent,
+                                        /* aPresContext */ nullptr,
+                                        &status)))) {
+      status = nsEventStatus_eIgnore;
+    }
   }
 
-  bool preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
-  if (preventDefaultCalled) {
+  if (status == nsEventStatus_eConsumeNoDefault) {
     return NS_OK;
   }
 
-  // Log an error to the error console.
+  nsAutoCString category;
+  if (aFactory->IsChrome()) {
+    category.AssignLiteral("chrome ");
+  } else {
+    category.AssignLiteral("content ");
+  }
+  category.AppendLiteral("javascript");
+
+  // Log the error to the error console.
+  nsCOMPtr<nsIConsoleService> consoleService =
+    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  MOZ_ASSERT(consoleService);
+
   nsCOMPtr<nsIScriptError> scriptError =
-    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+  MOZ_ASSERT(consoleService);
 
-  if (NS_FAILED(scriptError->InitWithWindowID(errorName,
-                                              init.mFilename,
-                                              EmptyString(), init.mLineno,
-                                              0, 0,
-                                              "IndexedDB",
-                                              aOwner->WindowID()))) {
-    NS_WARNING("Failed to init script error!");
-    return NS_ERROR_FAILURE;
+  if (uint64_t innerWindowID = aFactory->InnerWindowID()) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      scriptError->InitWithWindowID(errorName,
+                                    init.mFilename,
+                                    /* aSourceLine */ EmptyString(),
+                                    init.mLineno,
+                                    /* aColumnNumber */ 0,
+                                    nsIScriptError::errorFlag,
+                                    category,
+                                    innerWindowID)));
+  } else {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      scriptError->Init(errorName,
+                        init.mFilename,
+                        /* aSourceLine */ EmptyString(),
+                        init.mLineno,
+                        /* aColumnNumber */ 0,
+                        nsIScriptError::errorFlag,
+                        category.get())));
   }
 
-  nsCOMPtr<nsIConsoleService> consoleService =
-    do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError)));
 
-  return consoleService->LogMessage(scriptError);
+  return NS_OK;
 }
 
 // static
 bool
 IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext,
                                                   const nsACString& aOrigin)
 {
   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
@@ -470,16 +529,22 @@ bool
 IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
                                         JS::Handle<JSObject*> aGlobal)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
   MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
              "Passed object is not a global object!");
 
+  // We need to ensure that the manager has been created already here so that we
+  // load preferences that may control which properties are exposed.
+  if (NS_WARN_IF(!GetOrCreate())) {
+    return false;
+  }
+
   if (!IDBCursorBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBCursorWithValueBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBDatabaseBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBFactoryBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBIndexBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBKeyRangeBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBMutableFileBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBObjectStoreBinding::GetConstructorObject(aCx, aGlobal) ||
@@ -577,16 +642,34 @@ bool
 IndexedDatabaseManager::FullSynchronous()
 {
   MOZ_ASSERT(gDBManager,
              "FullSynchronous() called before indexedDB has been initialized!");
 
   return sFullSynchronousMode;
 }
 
+// static
+bool
+IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx,
+                                                    JSObject* aGlobal)
+{
+  if (NS_IsMainThread()) {
+    if (NS_WARN_IF(!GetOrCreate())) {
+      return false;
+    }
+  } else {
+    MOZ_ASSERT(Get(),
+               "ExperimentalFeaturesEnabled() called off the main thread "
+               "before indexedDB has been initialized!");
+  }
+
+  return gExperimentalFeaturesEnabled;
+}
+
 already_AddRefed<FileManager>
 IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
                                        const nsACString& aOrigin,
                                        const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
 
   FileManagerInfo* info;
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -16,26 +16,28 @@
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 
 class nsPIDOMWindow;
 struct PRLogModuleInfo;
 
 namespace mozilla {
 
+class DOMEventTargetHelper;
 class EventChainPostVisitor;
 
 namespace dom {
 
 class TabContext;
 
 namespace indexedDB {
 
 class FileManager;
 class FileManagerInfo;
+class IDBFactory;
 
 class IndexedDatabaseManager MOZ_FINAL : public nsIObserver
 {
   typedef mozilla::dom::quota::PersistenceType PersistenceType;
 
 public:
   enum LoggingMode
   {
@@ -101,16 +103,19 @@ public:
 #ifdef DEBUG
   ;
 #else
   {
     return sLoggingModule;
   }
 #endif
 
+  static bool
+  ExperimentalFeaturesEnabled(JSContext* aCx, JSObject* aGlobal);
+
   already_AddRefed<FileManager>
   GetFileManager(PersistenceType aPersistenceType,
                  const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
 
@@ -148,18 +153,19 @@ public:
   {
     IndexedDatabaseManager* mgr = Get();
     NS_ASSERTION(mgr, "Must have a manager here!");
 
     return mgr->mFileMutex;
   }
 
   static nsresult
-  FireWindowOnError(nsPIDOMWindow* aOwner,
-                    EventChainPostVisitor& aVisitor);
+  CommonPostHandleEvent(DOMEventTargetHelper* aEventTarget,
+                        IDBFactory* aFactory,
+                        EventChainPostVisitor& aVisitor);
 
   static bool
   TabContextMayAccessOrigin(const mozilla::dom::TabContext& aContext,
                             const nsACString& aOrigin);
 
   static bool
   DefineIndexedDB(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 
--- a/dom/indexedDB/moz.build
+++ b/dom/indexedDB/moz.build
@@ -89,11 +89,12 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '/db/sqlite3/src',
     '/dom/base',
     '/dom/storage',
+    '/dom/workers',
     '/ipc/glue',
     '/xpcom/build',
 ]
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -39,49 +39,177 @@ function clearAllDatabases(callback) {
   if (principal.appId != Components.interfaces.nsIPrincipal.UNKNOWN_APP_ID &&
       principal.appId != Components.interfaces.nsIPrincipal.NO_APP_ID) {
     appId = principal.appId;
     inBrowser = principal.isInBrowserElement;
   }
   SpecialPowers.clearStorageForURI(document.documentURI, callback, appId, inBrowser);
 }
 
+let testHarnessGenerator = testHarnessSteps();
+testHarnessGenerator.next();
+
+function testHarnessSteps() {
+  function nextTestHarnessStep(val) {
+    testHarnessGenerator.send(val);
+  }
+
+  let testScriptPath;
+  let testScriptFilename;
+
+  let scripts = document.getElementsByTagName("script");
+  for (let i = 0; i < scripts.length; i++) {
+    let src = scripts[i].src;
+    let match = src.match(/indexedDB\/test\/unit\/(test_[^\/]+\.js)$/);
+    if (match && match.length == 2) {
+      testScriptPath = src;
+      testScriptFilename = match[1];
+      break;
+    }
+  }
+
+  let limitedQuota = yield undefined;
+
+  info("Running" +
+       (testScriptFilename ? " '" + testScriptFilename + "'" : "") +
+       " with " +
+       (limitedQuota ? "" : "un") + "limited quota");
+
+  info("Pushing preferences");
+
+  SpecialPowers.pushPrefEnv(
+    {
+      "set": [
+        ["dom.indexedDB.testing", true],
+        ["dom.indexedDB.experimental", true],
+        ["dom.archivereader.enabled", true],
+        ["dom.workers.latestJSVersion", true]
+      ]
+    },
+    nextTestHarnessStep
+  );
+  yield undefined;
+
+  info("Pushing permissions");
+
+  SpecialPowers.pushPermissions(
+    [
+      {
+        type: "indexedDB",
+        allow: true,
+        context: document
+      }, {
+        type: "indexedDB-unlimited",
+        allow: !limitedQuota,
+        context: document
+      }
+    ],
+    nextTestHarnessStep
+  );
+  yield undefined;
+
+  info("Clearing old databases");
+
+  clearAllDatabases(nextTestHarnessStep);
+  yield undefined;
+
+  if (testScriptFilename && !window.disableWorkerTest) {
+    info("Running test in a worker");
+
+    let workerScriptBlob =
+      new Blob([ "(" + workerScript.toString() + ")();" ],
+               { type: "text/javascript;version=1.7" });
+    let workerScriptURL = URL.createObjectURL(workerScriptBlob);
+
+    let worker = new Worker(workerScriptURL);
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.message);
+      worker.terminate();
+      nextTestHarnessStep();
+    };
+
+    worker.onmessage = function(event) {
+      let message = event.data;
+      switch (message.op) {
+        case "ok":
+          ok(message.condition, message.name, message.diag);
+          break;
+
+        case "todo":
+          todo(message.condition, message.name, message.diag);
+          break;
+
+        case "info":
+          info(message.msg);
+          break;
+
+        case "ready":
+          worker.postMessage({ op: "load", files: [ testScriptPath ] });
+          break;
+
+        case "loaded":
+          worker.postMessage({ op: "start" });
+          break;
+
+        case "done":
+          ok(true, "Worker finished");
+          nextTestHarnessStep();
+          break;
+
+        default:
+          ok(false,
+             "Received a bad message from worker: " + JSON.stringify(message));
+          nextTestHarnessStep();
+      }
+    };
+
+    URL.revokeObjectURL(workerScriptURL);
+
+    yield undefined;
+
+    worker.terminate();
+    worker = null;
+
+    clearAllDatabases(nextTestHarnessStep);
+    yield undefined;
+  } else if (testScriptFilename) {
+    todo(false,
+         "Skipping test in a worker because it is explicitly disabled: " +
+         disableWorkerTest);
+  } else {
+    todo(false,
+         "Skipping test in a worker because it's not structured properly");
+  }
+
+  info("Running test in main thread");
+
+  // Now run the test script in the main thread.
+  testGenerator.next();
+
+  yield undefined;
+}
+
 if (!window.runTest) {
   window.runTest = function(limitedQuota)
   {
     SimpleTest.waitForExplicitFinish();
-
-    allowIndexedDB();
-    if (limitedQuota) {
-      denyUnlimitedQuota();
-    }
-    else {
-      allowUnlimitedQuota();
-    }
-
-    enableTesting();
-    enableExperimental();
-    enableArchiveReader();
-
-    clearAllDatabases(function () { testGenerator.next(); });
+    testHarnessGenerator.send(limitedQuota);
   }
 }
 
 function finishTest()
 {
-  resetArchiveReader();
-  resetExperimental();
-  resetTesting();
-  resetUnlimitedQuota();
-  resetIndexedDB();
-  SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
+  SpecialPowers.notifyObserversInParentProcess(null,
+                                               "disk-space-watcher",
                                                "free");
 
   SimpleTest.executeSoon(function() {
     testGenerator.close();
+    testHarnessGenerator.close();
     clearAllDatabases(function() { SimpleTest.finish(); });
   });
 }
 
 function browserRunTest()
 {
   testGenerator.next();
 }
@@ -149,119 +277,240 @@ ExpectError.prototype = {
     if (this._preventDefault) {
       event.preventDefault();
       event.stopPropagation();
     }
     grabEventAndContinueHandler(event);
   }
 };
 
-function compareKeys(k1, k2) {
-  let t = typeof k1;
-  if (t != typeof k2)
+function compareKeys(_k1_, _k2_) {
+  let t = typeof _k1_;
+  if (t != typeof _k2_)
     return false;
 
   if (t !== "object")
-    return k1 === k2;
+    return _k1_ === _k2_;
 
-  if (k1 instanceof Date) {
-    return (k2 instanceof Date) &&
-      k1.getTime() === k2.getTime();
+  if (_k1_ instanceof Date) {
+    return (_k2_ instanceof Date) &&
+      _k1_.getTime() === _k2_.getTime();
   }
 
-  if (k1 instanceof Array) {
-    if (!(k2 instanceof Array) ||
-        k1.length != k2.length)
+  if (_k1_ instanceof Array) {
+    if (!(_k2_ instanceof Array) ||
+        _k1_.length != _k2_.length)
       return false;
 
-    for (let i = 0; i < k1.length; ++i) {
-      if (!compareKeys(k1[i], k2[i]))
+    for (let i = 0; i < _k1_.length; ++i) {
+      if (!compareKeys(_k1_[i], _k2_[i]))
         return false;
     }
 
     return true;
   }
 
   return false;
 }
 
-function addPermission(type, allow, url)
-{
-  if (!url) {
-    url = window.document;
-  }
-  SpecialPowers.addPermission(type, allow, url);
-}
-
 function removePermission(type, url)
 {
   if (!url) {
     url = window.document;
   }
   SpecialPowers.removePermission(type, url);
 }
 
-function allowIndexedDB(url)
-{
-  addPermission("indexedDB", true, url);
-}
-
-function resetIndexedDB(url)
-{
-  removePermission("indexedDB", url);
-}
-
-function allowUnlimitedQuota(url)
-{
-  addPermission("indexedDB-unlimited", true, url);
-}
-
-function denyUnlimitedQuota(url)
-{
-  addPermission("indexedDB-unlimited", false, url);
-}
-
-function resetUnlimitedQuota(url)
-{
-  removePermission("indexedDB-unlimited", url);
-}
-
-function enableArchiveReader()
-{
-  archiveReaderEnabled = SpecialPowers.getBoolPref("dom.archivereader.enabled");
-  SpecialPowers.setBoolPref("dom.archivereader.enabled", true);
-}
-
-function resetArchiveReader()
-{
-  SpecialPowers.setBoolPref("dom.archivereader.enabled", archiveReaderEnabled);
-}
-
-function enableExperimental()
-{
-  SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
-}
-
-function resetExperimental()
-{
-  SpecialPowers.clearUserPref("dom.indexedDB.experimental");
-}
-
-function enableTesting()
-{
-  SpecialPowers.setBoolPref("dom.indexedDB.testing", true);
-}
-
-function resetTesting()
-{
-  SpecialPowers.clearUserPref("dom.indexedDB.testing");
-}
-
 function gc()
 {
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
 }
 
 function scheduleGC()
 {
   SpecialPowers.exactGC(window, continueToNextStep);
 }
+
+function workerScript() {
+  "use strict";
+
+  self.repr = function(_thing_) {
+    if (typeof(_thing_) == "undefined") {
+      return "undefined";
+    }
+
+    if (o === null) {
+      return "null";
+    }
+
+    let str;
+
+    try {
+      str = _thing_ + "";
+    } catch (e) {
+      return "[" + typeof(_thing_) + "]";
+    }
+
+    if (typeof(_thing_) == "function") {
+      str = str.replace(/^\s+/, "");
+      let idx = str.indexOf("{");
+      if (idx != -1) {
+        str = str.substr(0, idx) + "{...}";
+      }
+    }
+
+    return str;
+  };
+
+  self.ok = function(_condition_, _name_, _diag_) {
+    self.postMessage({ op: "ok",
+                       condition: !!_condition_,
+                       name: _name_,
+                       diag: _diag_ });
+  };
+
+  self.is = function(_a_, _b_, _name_) {
+    let pass = (_a_ == _b_);
+    let diag = pass ? "" : "got " + repr(_a_) + ", expected " + repr(_b_);
+    ok(pass, _name_, diag);
+  };
+
+  self.isnot = function(_a_, _b_, _name_) {
+    let pass = (_a_ != _b_);
+    let diag = pass ? "" : "didn't expect " + repr(_a_) + ", but got it";
+    ok(pass, _name_, diag);
+  };
+
+  self.todo = function(_condition_, _name_, _diag_) {
+    self.postMessage({ op: "todo",
+                       condition: !!_condition_,
+                       name: _name_,
+                       diag: _diag_ });
+  };
+
+  self.info = function(_msg_) {
+    self.postMessage({ op: "info", msg: _msg_ });
+  };
+
+  self.executeSoon = function(_fun_) {
+    setTimeout(_fun_, 0);
+  };
+
+  self.finishTest = function() {
+    self.postMessage({ op: "done" });
+  };
+
+  self.grabEventAndContinueHandler = function(_event_) {
+    testGenerator.send(_event_);
+  };
+
+  self.continueToNextStep = function() {
+    executeSoon(function() {
+      testGenerator.next();
+    });
+  };
+
+  self.continueToNextStepSync = function() {
+    testGenerator.next();
+  };
+
+  self.errorHandler = function(_event_) {
+    ok(false, "indexedDB error, '" + _event_.target.error.name + "'");
+    finishTest();
+  };
+
+  self.unexpectedSuccessHandler = function()
+  {
+    ok(false, "Got success, but did not expect it!");
+    finishTest();
+  };
+
+  self.expectedErrorHandler = function(_name_)
+  {
+    return function(_event_) {
+      is(_event_.type, "error", "Got an error event");
+      is(_event_.target.error.name, _name_, "Expected error was thrown.");
+      _event_.preventDefault();
+      grabEventAndContinueHandler(_event_);
+    };
+  };
+
+  self.ExpectError = function(_name_, _preventDefault_)
+  {
+    this._name = _name_;
+    this._preventDefault = _preventDefault_;
+  }
+  self.ExpectError.prototype = {
+    handleEvent: function(_event_)
+    {
+      is(_event_.type, "error", "Got an error event");
+      is(_event_.target.error.name, this._name, "Expected error was thrown.");
+      if (this._preventDefault) {
+        _event_.preventDefault();
+        _event_.stopPropagation();
+      }
+      grabEventAndContinueHandler(_event_);
+    }
+  };
+
+  self.compareKeys = function(_k1_, _k2_) {
+    let t = typeof _k1_;
+    if (t != typeof _k2_)
+      return false;
+
+    if (t !== "object")
+      return _k1_ === _k2_;
+
+    if (_k1_ instanceof Date) {
+      return (_k2_ instanceof Date) &&
+        _k1_.getTime() === _k2_.getTime();
+    }
+
+    if (_k1_ instanceof Array) {
+      if (!(_k2_ instanceof Array) ||
+          _k1_.length != _k2_.length)
+        return false;
+
+      for (let i = 0; i < _k1_.length; ++i) {
+        if (!compareKeys(_k1_[i], _k2_[i]))
+          return false;
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+
+  self.onerror = function(_message_, _file_, _line_) {
+    ok(false,
+       "Worker: uncaught exception [" + _file_ + ":" + _line_ + "]: '" +
+         _message_ + "'");
+    self.finishTest();
+    self.close();
+    return true;
+  };
+
+  self.onmessage = function(_event_) {
+    let message = _event_.data;
+    switch (message.op) {
+      case "load":
+        info("Worker: loading " + JSON.stringify(message.files));
+        self.importScripts(message.files);
+        self.postMessage({ op: "loaded" });
+        break;
+
+      case "start":
+        executeSoon(function() {
+          info("Worker: starting tests");
+          testGenerator.next();
+        });
+        break;
+
+      default:
+        throw new Error("Received a bad message from parent: " +
+                        JSON.stringify(message));
+    }
+  };
+
+  self.postMessage({ op: "ready" });
+}
--- a/dom/indexedDB/test/test_blob_worker_crash.html
+++ b/dom/indexedDB/test/test_blob_worker_crash.html
@@ -22,17 +22,16 @@
 
   function testSteps()
   {
     info("Open iframe, wait for it to do its IndexedDB stuff.");
 
     let iframe = document.getElementById("iframe1");
     window.addEventListener("message", grabEventAndContinueHandler, false);
     // Put it in a different origin to be safe
-    //allowUnlimitedQuota("http://example.org/");
     iframe.src = //"http://example.org" +
                  window.location.pathname.replace(
                    "test_blob_worker_crash.html",
                    "blob_worker_crash_iframe.html");
 
     let event = yield unexpectedSuccessHandler;
     is(event.data.result, "ready", "worker initialized correctly");
 
--- a/dom/indexedDB/test/unit/test_autoIncrement.js
+++ b/dom/indexedDB/test/unit/test_autoIncrement.js
@@ -1,13 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+let disableWorkerTest = "Need to implement a gc() function for worker tests";
+
 if (!this.window) {
   this.runTest = function() {
     todo(false, "Test disabled in xpcshell test suite for now");
     finishTest();
   }
 }
 
 var testGenerator = testSteps();
@@ -368,17 +370,17 @@ function testSteps()
   trans = db.transaction(["store1", "store2"], RW);
   trans.objectStore("store1").clear().onsuccess =
     grabEventAndContinueHandler;
   trans.objectStore("store2").clear().onsuccess =
     grabEventAndContinueHandler;
   yield undefined; yield undefined;
   db.close();
 
-  SpecialPowers.gc();
+  gc();
 
   openRequest = indexedDB.open(dbname, 2);
   openRequest.onerror = errorHandler;
   openRequest.onupgradeneeded = grabEventAndContinueHandler;
   openRequest.onsuccess = unexpectedSuccessHandler;
   event = yield undefined;
   db = event.target.result;
   trans = event.target.transaction;
--- a/dom/indexedDB/test/unit/test_blob_file_backed.js
+++ b/dom/indexedDB/test/unit/test_blob_file_backed.js
@@ -1,12 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
+
+let disableWorkerTest = "This test uses SpecialPowers";
+
 let testGenerator = testSteps();
 
 function createFileReader() {
   return SpecialPowers.Cc["@mozilla.org/files/filereader;1"]
                       .createInstance(SpecialPowers.Ci.nsIDOMFileReader);
 }
 
 function testSteps()
--- a/dom/indexedDB/test/unit/test_invalid_cursor.js
+++ b/dom/indexedDB/test/unit/test_invalid_cursor.js
@@ -1,13 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+let disableWorkerTest = "Need to implement a gc() function for worker tests";
+
 let testGenerator = testSteps();
 
 function testSteps()
 {
   const dbName = ("window" in this) ? window.location.pathname : "test";
   const dbVersion = 1;
   const objectStoreName = "foo";
   const data = 0;
--- a/dom/indexedDB/test/unit/test_lowDiskSpace.js
+++ b/dom/indexedDB/test/unit/test_lowDiskSpace.js
@@ -1,14 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 "use strict";
 
+let disableWorkerTest = "This test uses SpecialPowers";
+
 var self = this;
 
 var testGenerator = testSteps();
 
 function testSteps()
 {
   const dbName = self.window ? window.location.pathname : "test_lowDiskSpace";
   const dbVersion = 1;
--- a/dom/indexedDB/test/unit/test_readonly_transactions.js
+++ b/dom/indexedDB/test/unit/test_readonly_transactions.js
@@ -1,20 +1,13 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-if (!this.window) {
-  this.runTest = function() {
-    todo(false, "Test disabled in xpcshell test suite for now");
-    finishTest();
-  }
-}
-
 var testGenerator = testSteps();
 
 function testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const osName = "foo";
 
   let request = indexedDB.open(name, 1);
--- a/dom/indexedDB/test/unit/test_setVersion_events.js
+++ b/dom/indexedDB/test/unit/test_setVersion_events.js
@@ -44,22 +44,18 @@ function testSteps()
     db1.close();
   }, false);
 
   // Open the database again and trigger an upgrade that should succeed
   request = indexedDB.open(name, 2);
   request.onerror = errorHandler;
   request.onsuccess = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
-  if (SpecialPowers.isMainProcess()) {
-    request.onblocked = errorHandler;
-  }
-  else {
-    todo(false, "Need to fix blocked events in child processes!");
-  }
+  request.onblocked = errorHandler;
+
   event = yield undefined;
 
   // Test the upgradeneeded event.
   ok(event instanceof IDBVersionChangeEvent, "Event is of the right type");
   ok(event.target.result instanceof IDBDatabase, "Good result");
   db2 = event.target.result;
   is(event.target.transaction.mode, "versionchange",
      "Correct mode");
@@ -81,22 +77,18 @@ function testSteps()
     is(event.newVersion, 3, "Correct event newVersion");
     is(versionChangeEventCount++, 1, "Correct count");
   }, false);
 
   // Test opening the existing version again
   request = indexedDB.open(name, 2);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
-  if (SpecialPowers.isMainProcess()) {
-    request.onblocked = errorHandler;
-  }
-  else {
-    todo(false, "Need to fix blocked events in child processes!");
-  }
+  request.onblocked = errorHandler;
+
   event = yield undefined;
 
   db3 = event.target.result;
 
   // Test an upgrade that should fail
   request = indexedDB.open(name, 3);
   request.onerror = errorHandler;
   request.onsuccess = errorHandler;
@@ -125,22 +117,17 @@ function testSteps()
   db3 = event.target.result;
   db3.close();
 
   // Test another upgrade that should succeed.
   request = indexedDB.open(name, 4);
   request.onerror = errorHandler;
   request.onsuccess = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
-  if (SpecialPowers.isMainProcess()) {
-    request.onblocked = errorHandler;
-  }
-  else {
-    todo(false, "Need to fix blocked events in child processes!");
-  }
+  request.onblocked = errorHandler;
 
   event = yield undefined;
 
   ok(event instanceof IDBVersionChangeEvent, "Event is of the right type");
   ok(event.target.result instanceof IDBDatabase, "Good result");
   is(event.target.transaction.mode, "versionchange",
      "Correct mode");
   is(event.oldVersion, 3, "Correct event oldVersion");
--- a/dom/indexedDB/test/unit/test_transaction_abort.js
+++ b/dom/indexedDB/test/unit/test_transaction_abort.js
@@ -10,18 +10,16 @@ var abortFired = false;
 function abortListener(evt)
 {
   abortFired = true;
   is(evt.target.error, null, "Expect a null error for an aborted transaction");
 }
 
 function testSteps()
 {
-  const Ci = Components.interfaces;
-
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
   let event = yield undefined;
 
--- a/dom/indexedDB/test/unit/test_transaction_error.js
+++ b/dom/indexedDB/test/unit/test_transaction_error.js
@@ -77,16 +77,28 @@ function testSteps() {
   transaction.onabort = grabEventAndContinueHandler;
 
   objectStore = transaction.objectStore(objectStoreName);
 
   info("Adding duplicate entry without preventDefault()");
 
   if ("SimpleTest" in this) {
     SimpleTest.expectUncaughtException();
+  } else if ("DedicatedWorkerGlobalScope" in self &&
+             self instanceof DedicatedWorkerGlobalScope) {
+    let oldErrorFunction = self.onerror;
+    self.onerror = function(message, file, line) {
+      self.onerror = oldErrorFunction;
+      oldErrorFunction = null;
+
+      is(message,
+        "ConstraintError",
+        "Got expected ConstraintError on DedicatedWorkerGlobalScope");
+      return true;
+    };
   }
 
   request = objectStore.add(data, dataKey);
   request.onsuccess = unexpectedSuccessHandler;
   request.onerror = grabEventAndContinueHandler;
   event = yield undefined;
 
   is(event.type, "error", "Got an error event");
--- a/dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js
+++ b/dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js
@@ -1,13 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+let disableWorkerTest = "This test uses SpecialPowers";
+
 var testGenerator = testSteps();
 
 function testSteps()
 {
   let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   let event = yield undefined;
--- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
+++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
@@ -1,15 +1,19 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components;
 
+if (!("self" in this)) {
+  this.self = this;
+}
+
 const DOMException = Ci.nsIDOMDOMException;
 
 function is(a, b, msg) {
   do_check_eq(a, b, Components.stack.caller);
 }
 
 function ok(cond, msg) {
   do_check_true(!!cond, Components.stack.caller); 
@@ -171,26 +175,16 @@ function allowIndexedDB(url)
   throw "allowIndexedDB";
 }
 
 function disallowIndexedDB(url)
 {
   throw "disallowIndexedDB";
 }
 
-function allowUnlimitedQuota(url)
-{
-  throw "allowUnlimitedQuota";
-}
-
-function disallowUnlimitedQuota(url)
-{
-  throw "disallowUnlimitedQuota";
-}
-
 function enableExperimental()
 {
   SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
 }
 
 function resetExperimental()
 {
   SpecialPowers.clearUserPref("dom.indexedDB.experimental");
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -37,42 +37,56 @@
 #include "nsIRemoteBlob.h"
 #include "nsISeekableStream.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
+#include "WorkerPrivate.h"
 
 #ifdef DEBUG
 #include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread().
 #endif
 
+#ifdef OS_POSIX
+#include "chrome/common/file_descriptor_set_posix.h"
+#endif
+
 #define DISABLE_ASSERTS_FOR_FUZZING 0
 
 #if DISABLE_ASSERTS_FOR_FUZZING
 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
 #else
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
 #endif
 
 #define PRIVATE_REMOTE_INPUT_STREAM_IID \
   {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}}
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::ipc;
 using namespace mozilla::dom::indexedDB;
+using namespace mozilla::dom::workers;
 
 namespace {
 
 const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1";
 
+const uint32_t kMaxFileDescriptorsPerMessage = 250;
+
+#ifdef OS_POSIX
+// Keep this in sync with other platforms.
+static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250,
+              "MAX_DESCRIPTORS_PER_MESSAGE mismatch!");
+#endif
+
 StaticRefPtr<nsIUUIDGenerator> gUUIDGenerator;
 
 GeckoProcessType gProcessType = GeckoProcessType_Invalid;
 
 void
 CommonStartup()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -216,44 +230,88 @@ ReleaseOnTarget(SmartPtr<T>& aDoomed, ns
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aTarget->Dispatch(releaseRunnable,
                                                    NS_DISPATCH_NORMAL)));
   } else {
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(releaseRunnable)));
   }
 }
 
 template <class ManagerType>
-PFileDescriptorSetParent*
+void
 ConstructFileDescriptorSet(ManagerType* aManager,
-                           const nsTArray<FileDescriptor>& aFDs)
+                           nsTArray<FileDescriptor>& aFDs,
+                           OptionalFileDescriptorSet& aOptionalFDSet)
 {
   typedef typename ConcreteManagerTypeTraits<ManagerType>::Type
           ConcreteManagerType;
 
   MOZ_ASSERT(aManager);
 
   if (aFDs.IsEmpty()) {
-    return nullptr;
+    aOptionalFDSet = void_t();
+    return;
+  }
+
+  if (aFDs.Length() <= kMaxFileDescriptorsPerMessage) {
+    aOptionalFDSet = nsTArray<FileDescriptor>();
+    aOptionalFDSet.get_ArrayOfFileDescriptor().SwapElements(aFDs);
+    return;
   }
 
   auto* concreteManager = static_cast<ConcreteManagerType*>(aManager);
 
   PFileDescriptorSetParent* fdSet =
     concreteManager->SendPFileDescriptorSetConstructor(aFDs[0]);
   if (!fdSet) {
-    return nullptr;
+    aOptionalFDSet = void_t();
+    return;
   }
 
   for (uint32_t index = 1; index < aFDs.Length(); index++) {
     if (!fdSet->SendAddFileDescriptor(aFDs[index])) {
-      return nullptr;
+      aOptionalFDSet = void_t();
+      return;
     }
   }
 
-  return fdSet;
+  aOptionalFDSet = fdSet;
+}
+
+void
+OptionalFileDescriptorSetToFDs(OptionalFileDescriptorSet& aOptionalSet,
+                               nsTArray<FileDescriptor>& aFDs)
+{
+  MOZ_ASSERT(aFDs.IsEmpty());
+
+  switch (aOptionalSet.type()) {
+    case OptionalFileDescriptorSet::Tvoid_t:
+      return;
+
+    case OptionalFileDescriptorSet::TArrayOfFileDescriptor:
+      aOptionalSet.get_ArrayOfFileDescriptor().SwapElements(aFDs);
+      return;
+
+    case OptionalFileDescriptorSet::TPFileDescriptorSetChild: {
+      FileDescriptorSetChild* fdSetActor =
+        static_cast<FileDescriptorSetChild*>(
+          aOptionalSet.get_PFileDescriptorSetChild());
+      MOZ_ASSERT(fdSetActor);
+
+      fdSetActor->ForgetFileDescriptors(aFDs);
+      MOZ_ASSERT(!aFDs.IsEmpty());
+
+      PFileDescriptorSetChild::Send__delete__(fdSetActor);
+      return;
+    }
+
+    default:
+      MOZ_CRASH("Unknown type!");
+  }
+
+  MOZ_CRASH("Should never get here!");
 }
 
 class NS_NO_VTABLE IPrivateRemoteInputStream
   : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
 
@@ -337,353 +395,76 @@ NS_INTERFACE_MAP_END
 
 class RemoteInputStream MOZ_FINAL
   : public nsIInputStream
   , public nsISeekableStream
   , public nsIIPCSerializableInputStream
   , public IPrivateRemoteInputStream
 {
   Monitor mMonitor;
+  BlobChild* mActor;
   nsCOMPtr<nsIInputStream> mStream;
   nsRefPtr<FileImpl> mBlobImpl;
   nsCOMPtr<nsIEventTarget> mEventTarget;
   nsISeekableStream* mWeakSeekableStream;
+  uint64_t mStart;
+  uint64_t mLength;
 
 public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
   explicit
-  RemoteInputStream(FileImpl* aBlobImpl)
-    : mMonitor("RemoteInputStream.mMonitor")
-    , mBlobImpl(aBlobImpl)
-    , mWeakSeekableStream(nullptr)
-  {
-    MOZ_ASSERT(IsOnOwningThread());
-    MOZ_ASSERT(aBlobImpl);
-
-    if (!NS_IsMainThread()) {
-      mEventTarget = do_GetCurrentThread();
-      MOZ_ASSERT(mEventTarget);
-    }
-  }
+  RemoteInputStream(FileImpl* aBlobImpl);
+
+  RemoteInputStream(BlobChild* aActor,
+                    FileImpl* aBlobImpl,
+                    uint64_t aStart,
+                    uint64_t aLength);
 
   bool
   IsOnOwningThread() const
   {
     return EventTargetIsOnCurrentThread(mEventTarget);
   }
 
   void
   AssertIsOnOwningThread() const
   {
     MOZ_ASSERT(IsOnOwningThread());
   }
 
-  void
-  Serialize(InputStreamParams& aParams,
-            FileDescriptorArray& /* aFileDescriptors */)
+  bool
+  IsWorkerStream() const
   {
-    MOZ_RELEASE_ASSERT(mBlobImpl);
-
-    nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mBlobImpl);
-    MOZ_ASSERT(remote);
-
-    BlobChild* actor = remote->GetBlobChild();
-    MOZ_ASSERT(actor);
-
-    aParams = RemoteInputStreamParams(actor->ParentID());
-  }
-
-  bool
-  Deserialize(const InputStreamParams& /* aParams */,
-              const FileDescriptorArray& /* aFileDescriptors */)
-  {
-    // See InputStreamUtils.cpp to see how deserialization of a
-    // RemoteInputStream is special-cased.
-    MOZ_CRASH("RemoteInputStream should never be deserialized");
+    return !!mActor;
   }
 
   void
-  SetStream(nsIInputStream* aStream)
-  {
-    AssertIsOnOwningThread();
-    MOZ_ASSERT(aStream);
-
-    nsCOMPtr<nsIInputStream> stream = aStream;
-    nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
-
-    MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream));
-
-    {
-      MonitorAutoLock lock(mMonitor);
-
-      MOZ_ASSERT(!mStream);
-      MOZ_ASSERT(!mWeakSeekableStream);
-
-      mStream.swap(stream);
-      mWeakSeekableStream = seekableStream;
-
-      mMonitor.Notify();
-    }
-  }
-
-  NS_IMETHOD
-  Close() MOZ_OVERRIDE
-  {
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsRefPtr<FileImpl> blobImpl;
-    mBlobImpl.swap(blobImpl);
-
-    rv = mStream->Close();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Available(uint64_t* aAvailable) MOZ_OVERRIDE
-  {
-    if (!IsOnOwningThread()) {
-      nsresult rv = BlockAndWaitForStream();
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = mStream->Available(aAvailable);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-#ifdef DEBUG
-    if (NS_IsMainThread()) {
-      NS_WARNING("Someone is trying to do main-thread I/O...");
-    }
-#endif
-
-    nsresult rv;
-
-    // See if we already have our real stream.
-    nsCOMPtr<nsIInputStream> inputStream;
-    {
-      MonitorAutoLock lock(mMonitor);
-
-      inputStream = mStream;
-    }
-
-    // If we do then just call through.
-    if (inputStream) {
-      rv = inputStream->Available(aAvailable);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      return NS_OK;
-    }
-
-    // If the stream is already closed then we can't do anything.
-    if (!mBlobImpl) {
-      return NS_BASE_STREAM_CLOSED;
-    }
-
-    // Otherwise fake it...
-    NS_WARNING("Available() called before real stream has been delivered, "
-               "guessing the amount of data available!");
-
-    ErrorResult error;
-    *aAvailable = mBlobImpl->GetSize(error);
-    if (NS_WARN_IF(error.Failed())) {
-      return error.ErrorCode();
-    }
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Read(char* aBuffer, uint32_t aCount, uint32_t* aResult) MOZ_OVERRIDE
-  {
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mStream->Read(aBuffer, aCount, aResult);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
-               uint32_t* aResult) MOZ_OVERRIDE
-  {
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  IsNonBlocking(bool* aNonBlocking) MOZ_OVERRIDE
-  {
-    NS_ENSURE_ARG_POINTER(aNonBlocking);
-
-    *aNonBlocking = false;
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE
-  {
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mWeakSeekableStream) {
-      NS_WARNING("Underlying blob stream is not seekable!");
-      return NS_ERROR_NO_INTERFACE;
-    }
-
-    rv = mWeakSeekableStream->Seek(aWhence, aOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Tell(int64_t* aResult) MOZ_OVERRIDE
-  {
-    // We can cheat here and assume that we're going to start at 0 if we don't
-    // yet have our stream. Though, really, this should abort since most input
-    // streams could block here.
-    if (IsOnOwningThread() && !mStream) {
-      *aResult = 0;
-      return NS_OK;
-    }
-
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mWeakSeekableStream) {
-      NS_WARNING("Underlying blob stream is not seekable!");
-      return NS_ERROR_NO_INTERFACE;
-    }
-
-    rv = mWeakSeekableStream->Tell(aResult);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  SetEOF() MOZ_OVERRIDE
-  {
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mWeakSeekableStream) {
-      NS_WARNING("Underlying blob stream is not seekable!");
-      return NS_ERROR_NO_INTERFACE;
-    }
-
-    rv = mWeakSeekableStream->SetEOF();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
+  SetStream(nsIInputStream* aStream);
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+private:
+  ~RemoteInputStream();
+
+  nsresult
+  BlockAndWaitForStream();
+
+  void
+  ReallyBlockAndWaitForStream();
+
+  bool
+  IsSeekableStream();
+
+  NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
 
   virtual nsIInputStream*
-  BlockAndGetInternalStream() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(!IsOnOwningThread());
-
-    nsresult rv = BlockAndWaitForStream();
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    return mStream;
-  }
-
-private:
-  ~RemoteInputStream()
-  {
-    if (!IsOnOwningThread()) {
-      mStream = nullptr;
-      mWeakSeekableStream = nullptr;
-
-      if (mBlobImpl) {
-        ReleaseOnTarget(mBlobImpl, mEventTarget);
-      }
-    }
-  }
-
-  void
-  ReallyBlockAndWaitForStream()
-  {
-    MOZ_ASSERT(!IsOnOwningThread());
-
-    DebugOnly<bool> waited;
-
-    {
-      MonitorAutoLock lock(mMonitor);
-
-      waited = !mStream;
-
-      while (!mStream) {
-        mMonitor.Wait();
-      }
-    }
-
-    MOZ_ASSERT(mStream);
-
-#ifdef DEBUG
-    if (waited && mWeakSeekableStream) {
-      int64_t position;
-      MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
-                 "Failed to determine initial stream position!");
-      MOZ_ASSERT(!position, "Stream not starting at 0!");
-    }
-#endif
-  }
-
-  nsresult
-  BlockAndWaitForStream()
-  {
-    if (IsOnOwningThread()) {
-      NS_WARNING("Blocking the owning thread is not supported!");
-      return NS_ERROR_FAILURE;
-    }
-
-    ReallyBlockAndWaitForStream();
-
-    return NS_OK;
-  }
-
-  bool
-  IsSeekableStream()
-  {
-    if (IsOnOwningThread()) {
-      if (!mStream) {
-        NS_WARNING("Don't know if this stream is seekable yet!");
-        return true;
-      }
-    } else {
-      ReallyBlockAndWaitForStream();
-    }
-
-    return !!mWeakSeekableStream;
-  }
+  BlockAndGetInternalStream() MOZ_OVERRIDE;
 };
 
-NS_IMPL_ADDREF(RemoteInputStream)
-NS_IMPL_RELEASE(RemoteInputStream)
-
-NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
-  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
-  NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
-  NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
-NS_INTERFACE_MAP_END
-
 class InputStreamChild MOZ_FINAL
   : public PBlobStreamChild
 {
   nsRefPtr<RemoteInputStream> mRemoteStream;
 
 public:
   explicit
   InputStreamChild(RemoteInputStream* aRemoteStream)
@@ -704,22 +485,98 @@ private:
   virtual bool
   Recv__delete__(const InputStreamParams& aParams,
                  const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
 };
 
 class InputStreamParent MOZ_FINAL
   : public PBlobStreamParent
 {
+  typedef mozilla::ipc::InputStreamParams InputStreamParams;
+  typedef mozilla::ipc::OptionalFileDescriptorSet OptionalFileDescriptorSet;
+
+  bool* mSyncLoopGuard;
+  InputStreamParams* mParams;
+  OptionalFileDescriptorSet* mFDs;
+
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
 public:
   InputStreamParent()
-  { }
+    : mSyncLoopGuard(nullptr)
+    , mParams(nullptr)
+    , mFDs(nullptr)
+  {
+#ifdef DEBUG
+    mOwningThread = PR_GetCurrentThread();
+#endif
+
+    AssertIsOnOwningThread();
+
+    MOZ_COUNT_CTOR(InputStreamParent);
+  }
+
+  InputStreamParent(bool* aSyncLoopGuard,
+                    InputStreamParams* aParams,
+                    OptionalFileDescriptorSet* aFDs)
+    : mSyncLoopGuard(aSyncLoopGuard)
+    , mParams(aParams)
+    , mFDs(aFDs)
+  {
+#ifdef DEBUG
+    mOwningThread = PR_GetCurrentThread();
+#endif
+
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(aSyncLoopGuard);
+    MOZ_ASSERT(!*aSyncLoopGuard);
+    MOZ_ASSERT(aParams);
+    MOZ_ASSERT(aFDs);
+
+    MOZ_COUNT_CTOR(InputStreamParent);
+  }
 
   ~InputStreamParent()
-  { }
+  {
+    AssertIsOnOwningThread();
+
+    MOZ_COUNT_DTOR(InputStreamParent);
+  }
+
+  void
+  AssertIsOnOwningThread() const
+  {
+#ifdef DEBUG
+    MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+#endif
+  }
+
+  bool
+  Destroy(const InputStreamParams& aParams,
+          const OptionalFileDescriptorSet& aFDs)
+  {
+    AssertIsOnOwningThread();
+
+    if (mSyncLoopGuard) {
+      MOZ_ASSERT(!*mSyncLoopGuard);
+
+      *mSyncLoopGuard = true;
+      *mParams = aParams;
+      *mFDs = aFDs;
+
+      // We're not a live actor so manage the memory ourselves.
+      delete this;
+      return true;
+    }
+
+    // This will be destroyed by BlobParent::DeallocPBlobStreamParent.
+    return PBlobStreamParent::Send__delete__(this, aParams, aFDs);
+  }
 
 private:
   // This method is only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
   {
     // Nothing needs to be done here.
   }
@@ -1181,16 +1038,376 @@ BlobDataFromBlobImpl(FileImpl* aBlobImpl
 
   uint32_t readCount;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
     inputStream->Read(reinterpret_cast<char*>(blobData.Elements()),
                       uint32_t(available),
                       &readCount)));
 }
 
+RemoteInputStream::RemoteInputStream(FileImpl* aBlobImpl)
+  : mMonitor("RemoteInputStream.mMonitor")
+  , mActor(nullptr)
+  , mBlobImpl(aBlobImpl)
+  , mWeakSeekableStream(nullptr)
+  , mStart(0)
+  , mLength(0)
+{
+  MOZ_ASSERT(aBlobImpl);
+
+  if (!NS_IsMainThread()) {
+    mEventTarget = do_GetCurrentThread();
+    MOZ_ASSERT(mEventTarget);
+  }
+
+  MOZ_ASSERT(IsOnOwningThread());
+}
+
+RemoteInputStream::RemoteInputStream(BlobChild* aActor,
+                                     FileImpl* aBlobImpl,
+                                     uint64_t aStart,
+                                     uint64_t aLength)
+  : mMonitor("RemoteInputStream.mMonitor")
+  , mActor(aActor)
+  , mBlobImpl(aBlobImpl)
+  , mEventTarget(NS_GetCurrentThread())
+  , mWeakSeekableStream(nullptr)
+  , mStart(aStart)
+  , mLength(aLength)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aBlobImpl);
+
+  MOZ_ASSERT(IsOnOwningThread());
+}
+
+RemoteInputStream::~RemoteInputStream()
+{
+  if (!IsOnOwningThread()) {
+    mStream = nullptr;
+    mWeakSeekableStream = nullptr;
+
+    if (mBlobImpl) {
+      ReleaseOnTarget(mBlobImpl, mEventTarget);
+    }
+  }
+}
+
+void
+RemoteInputStream::SetStream(nsIInputStream* aStream)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aStream);
+
+  nsCOMPtr<nsIInputStream> stream = aStream;
+  nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
+
+  MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream));
+
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    MOZ_ASSERT_IF(mStream, IsWorkerStream());
+
+    if (!mStream) {
+      MOZ_ASSERT(!mWeakSeekableStream);
+
+      mStream.swap(stream);
+      mWeakSeekableStream = seekableStream;
+
+      mMonitor.Notify();
+    }
+  }
+}
+
+nsresult
+RemoteInputStream::BlockAndWaitForStream()
+{
+  if (IsOnOwningThread()) {
+    if (NS_IsMainThread()) {
+      NS_WARNING("Blocking the main thread is not supported!");
+      return NS_ERROR_FAILURE;
+    }
+
+    MOZ_ASSERT(IsWorkerStream());
+
+    InputStreamParams params;
+    OptionalFileDescriptorSet optionalFDs;
+
+    mActor->SendBlobStreamSync(mStart, mLength, &params, &optionalFDs);
+
+    nsTArray<FileDescriptor> fds;
+    OptionalFileDescriptorSetToFDs(optionalFDs, fds);
+
+    nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(params, fds);
+    MOZ_ASSERT(stream);
+
+    SetStream(stream);
+    return NS_OK;
+  }
+
+  ReallyBlockAndWaitForStream();
+
+  return NS_OK;
+}
+
+void
+RemoteInputStream::ReallyBlockAndWaitForStream()
+{
+  MOZ_ASSERT(!IsOnOwningThread());
+
+  DebugOnly<bool> waited;
+
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    waited = !mStream;
+
+    while (!mStream) {
+      mMonitor.Wait();
+    }
+  }
+
+  MOZ_ASSERT(mStream);
+
+#ifdef DEBUG
+  if (waited && mWeakSeekableStream) {
+    int64_t position;
+    MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
+                "Failed to determine initial stream position!");
+    MOZ_ASSERT(!position, "Stream not starting at 0!");
+  }
+#endif
+}
+
+bool
+RemoteInputStream::IsSeekableStream()
+{
+  if (IsOnOwningThread()) {
+    if (!mStream) {
+      NS_WARNING("Don't know if this stream is seekable yet!");
+      return true;
+    }
+  } else {
+    ReallyBlockAndWaitForStream();
+  }
+
+  return !!mWeakSeekableStream;
+}
+
+NS_IMPL_ADDREF(RemoteInputStream)
+NS_IMPL_RELEASE(RemoteInputStream)
+
+NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+RemoteInputStream::Close()
+{
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<FileImpl> blobImpl;
+  mBlobImpl.swap(blobImpl);
+
+  rv = mStream->Close();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Available(uint64_t* aAvailable)
+{
+  if (!IsOnOwningThread()) {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->Available(aAvailable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+#ifdef DEBUG
+  if (NS_IsMainThread()) {
+    NS_WARNING("Someone is trying to do main-thread I/O...");
+  }
+#endif
+
+  nsresult rv;
+
+  // See if we already have our real stream.
+  nsCOMPtr<nsIInputStream> inputStream;
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    inputStream = mStream;
+  }
+
+  // If we do then just call through.
+  if (inputStream) {
+    rv = inputStream->Available(aAvailable);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  // If the stream is already closed then we can't do anything.
+  if (!mBlobImpl) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  // Otherwise fake it...
+  NS_WARNING("Available() called before real stream has been delivered, "
+              "guessing the amount of data available!");
+
+  ErrorResult error;
+  *aAvailable = mBlobImpl->GetSize(error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aResult)
+{
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mStream->Read(aBuffer, aCount, aResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+                                void* aClosure,
+                                uint32_t aCount,
+                                uint32_t* aResult)
+{
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+  NS_ENSURE_ARG_POINTER(aNonBlocking);
+
+  *aNonBlocking = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mWeakSeekableStream) {
+    NS_WARNING("Underlying blob stream is not seekable!");
+    return NS_ERROR_NO_INTERFACE;
+  }
+
+  rv = mWeakSeekableStream->Seek(aWhence, aOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Tell(int64_t* aResult)
+{
+  // We can cheat here and assume that we're going to start at 0 if we don't yet
+  // have our stream. Though, really, this should abort since most input streams
+  // could block here.
+  if (IsOnOwningThread() && !mStream) {
+    *aResult = 0;
+    return NS_OK;
+  }
+
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mWeakSeekableStream) {
+    NS_WARNING("Underlying blob stream is not seekable!");
+    return NS_ERROR_NO_INTERFACE;
+  }
+
+  rv = mWeakSeekableStream->Tell(aResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::SetEOF()
+{
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mWeakSeekableStream) {
+    NS_WARNING("Underlying blob stream is not seekable!");
+    return NS_ERROR_NO_INTERFACE;
+  }
+
+  rv = mWeakSeekableStream->SetEOF();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+RemoteInputStream::Serialize(InputStreamParams& aParams,
+                             FileDescriptorArray& /* aFDs */)
+{
+  MOZ_RELEASE_ASSERT(mBlobImpl);
+
+  nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mBlobImpl);
+  MOZ_ASSERT(remote);
+
+  BlobChild* actor = remote->GetBlobChild();
+  MOZ_ASSERT(actor);
+
+  aParams = RemoteInputStreamParams(actor->ParentID());
+}
+
+bool
+RemoteInputStream::Deserialize(const InputStreamParams& /* aParams */,
+                               const FileDescriptorArray& /* aFDs */)
+{
+  // See InputStreamUtils.cpp to see how deserialization of a
+  // RemoteInputStream is special-cased.
+  MOZ_CRASH("RemoteInputStream should never be deserialized");
+}
+
+nsIInputStream*
+RemoteInputStream::BlockAndGetInternalStream()
+{
+  MOZ_ASSERT(!IsOnOwningThread());
+
+  nsresult rv = BlockAndWaitForStream();
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return mStream;
+}
+
 } // anonymous namespace
 
 StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
 StaticAutoPtr<Mutex> BlobParent::sIDTableMutex;
 
 /*******************************************************************************
  * BlobParent::IDTableEntry Declaration
  ******************************************************************************/
@@ -1303,29 +1520,29 @@ private:
 // stream.
 class BlobParent::OpenStreamRunnable MOZ_FINAL
   : public nsRunnable
 {
   friend class nsRevocableEventPtr<OpenStreamRunnable>;
 
   // Only safe to access these pointers if mRevoked is false!
   BlobParent* mBlobActor;
-  PBlobStreamParent* mStreamActor;
+  InputStreamParent* mStreamActor;
 
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsIIPCSerializableInputStream> mSerializable;
   nsCOMPtr<nsIEventTarget> mActorTarget;
   nsCOMPtr<nsIThread> mIOTarget;
 
   bool mRevoked;
   bool mClosing;
 
 public:
   OpenStreamRunnable(BlobParent* aBlobActor,
-                     PBlobStreamParent* aStreamActor,
+                     InputStreamParent* aStreamActor,
                      nsIInputStream* aStream,
                      nsIIPCSerializableInputStream* aSerializable,
                      nsIThread* aIOTarget)
     : mBlobActor(aBlobActor)
     , mStreamActor(aStreamActor)
     , mStream(aStream)
     , mSerializable(aSerializable)
     , mIOTarget(aIOTarget)
@@ -1472,38 +1689,31 @@ private:
       MOZ_ASSERT(!mStreamActor);
     }
     else {
       MOZ_ASSERT(mBlobActor);
       MOZ_ASSERT(mBlobActor->HasManager());
       MOZ_ASSERT(mStreamActor);
 
       InputStreamParams params;
-      nsAutoTArray<FileDescriptor, 10> fds;
+      nsTArray<FileDescriptor> fds;
       serializable->Serialize(params, fds);
 
       MOZ_ASSERT(params.type() != InputStreamParams::T__None);
 
-      PFileDescriptorSetParent* fdSet;
+      OptionalFileDescriptorSet optionalFDSet;
       if (nsIContentParent* contentManager = mBlobActor->GetContentManager()) {
-        fdSet = ConstructFileDescriptorSet(contentManager, fds);
+        ConstructFileDescriptorSet(contentManager, fds, optionalFDSet);
       } else {
-        fdSet = ConstructFileDescriptorSet(mBlobActor->GetBackgroundManager(),
-                                           fds);
+        ConstructFileDescriptorSet(mBlobActor->GetBackgroundManager(),
+                                   fds,
+                                   optionalFDSet);
       }
 
-      OptionalFileDescriptorSet optionalFDs;
-      if (fdSet) {
-        optionalFDs = fdSet;
-      } else {
-        optionalFDs = void_t();
-      }
-
-      unused <<
-        PBlobStreamParent::Send__delete__(mStreamActor, params, optionalFDs);
+      mStreamActor->Destroy(params, optionalFDSet);
 
       mBlobActor->NoteRunnableCompleted(this);
 
 #ifdef DEBUG
       mBlobActor = nullptr;
       mStreamActor = nullptr;
 #endif
     }
@@ -1590,32 +1800,31 @@ public:
   RemoteBlobImpl(BlobChild* aActor);
 
   void
   NoteDyingActor();
 
   BlobChild*
   GetActor() const
   {
-    AssertActorEventTargetIsOnCurrentThread();
+    MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
 
     return mActor;
   }
 
   nsIEventTarget*
   GetActorEventTarget() const
   {
     return mActorTarget;
   }
 
-  void
-  AssertActorEventTargetIsOnCurrentThread() const
+  bool
+  ActorEventTargetIsOnCurrentThread() const
   {
-    MOZ_ASSERT(
-      EventTargetIsOnCurrentThread(BaseRemoteBlobImpl()->mActorTarget));
+    return EventTargetIsOnCurrentThread(BaseRemoteBlobImpl()->mActorTarget);
   }
 
   bool
   IsSlice() const
   {
     return mIsSlice;
   }
 
@@ -2167,19 +2376,29 @@ CreateStreamHelper::GetStream(nsIInputSt
 
     MOZ_ASSERT(target);
 
     nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
+    DebugOnly<bool> warned = false;
+
     {
       MonitorAutoLock lock(mMonitor);
+
       while (!mDone) {
+#ifdef DEBUG
+        if (!warned) {
+          NS_WARNING("RemoteBlobImpl::GetInternalStream() called on thread "
+                     "that can't send messages, blocking here to wait for the "
+                     "actor's thread to send the message!");
+        }
+#endif
         lock.Wait();
       }
     }
   }
 
   MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mDone);
 
@@ -2192,23 +2411,29 @@ CreateStreamHelper::GetStream(nsIInputSt
 }
 
 void
 BlobChild::RemoteBlobImpl::
 CreateStreamHelper::RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl,
                                 bool aNotify)
 {
   MOZ_ASSERT(aBaseRemoteBlobImpl);
-  aBaseRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
+  MOZ_ASSERT(aBaseRemoteBlobImpl->ActorEventTargetIsOnCurrentThread());
   MOZ_ASSERT(!mInputStream);
   MOZ_ASSERT(!mDone);
 
   if (BlobChild* actor = aBaseRemoteBlobImpl->GetActor()) {
-    nsRefPtr<RemoteInputStream> stream =
-      new RemoteInputStream(aBaseRemoteBlobImpl);
+    nsRefPtr<RemoteInputStream> stream;
+
+    if (!NS_IsMainThread() && GetCurrentThreadWorkerPrivate()) {
+      stream =
+        new RemoteInputStream(actor, aBaseRemoteBlobImpl, mStart, mLength);
+    } else {
+      stream = new RemoteInputStream(aBaseRemoteBlobImpl);
+    }
 
     InputStreamChild* streamActor = new InputStreamChild(stream);
     if (actor->SendPBlobStreamConstructor(streamActor, mStart, mLength)) {
       stream.swap(mInputStream);
     }
   }
 
   mRemoteBlobImpl = nullptr;
@@ -2225,17 +2450,17 @@ CreateStreamHelper::RunInternal(RemoteBl
 NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobImpl::CreateStreamHelper,
                              nsRunnable)
 
 NS_IMETHODIMP
 BlobChild::RemoteBlobImpl::
 CreateStreamHelper::Run()
 {
   MOZ_ASSERT(mRemoteBlobImpl);
-  mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
+  MOZ_ASSERT(mRemoteBlobImpl->ActorEventTargetIsOnCurrentThread());
 
   nsRefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
     mRemoteBlobImpl->BaseRemoteBlobImpl();
   MOZ_ASSERT(baseRemoteBlobImpl);
 
   RunInternal(baseRemoteBlobImpl, true);
   return NS_OK;
 }
@@ -2275,22 +2500,25 @@ RemoteBlobSliceImpl::RemoteBlobSliceImpl
 
 NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
                              BlobChild::RemoteBlobImpl)
 
 BlobChild*
 BlobChild::
 RemoteBlobSliceImpl::GetBlobChild()
 {
-  AssertActorEventTargetIsOnCurrentThread();
+  MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
+                mActorWasCreated);
 
   if (mActorWasCreated) {
     return RemoteBlobImpl::GetBlobChild();
   }
 
+  MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
+
   mActorWasCreated = true;
 
   BlobChild* baseActor = mParent->GetActor();
   MOZ_ASSERT(baseActor);
   MOZ_ASSERT(baseActor->HasManager());
 
   nsID id;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
@@ -2576,47 +2804,50 @@ BlobChild::BlobChild(nsIContentChild* aM
 
 BlobChild::BlobChild(PBackgroundChild* aManager, FileImpl* aBlobImpl)
   : mBackgroundManager(aManager)
   , mContentManager(nullptr)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
 
-  CommonInit(aBlobImpl);
-
   if (!NS_IsMainThread()) {
     mEventTarget = do_GetCurrentThread();
     MOZ_ASSERT(mEventTarget);
   }
+
+  CommonInit(aBlobImpl);
 }
 
 BlobChild::BlobChild(nsIContentChild* aManager, BlobChild* aOther)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
 
-  CommonInit(aOther);
+  CommonInit(aOther, /* aBlobImpl */ nullptr);
 }
 
-BlobChild::BlobChild(PBackgroundChild* aManager, BlobChild* aOther)
+BlobChild::BlobChild(PBackgroundChild* aManager,
+                     BlobChild* aOther,
+                     FileImpl* aBlobImpl)
   : mBackgroundManager(aManager)
   , mContentManager(nullptr)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
-
-  CommonInit(aOther);
+  MOZ_ASSERT(aBlobImpl);
 
   if (!NS_IsMainThread()) {
     mEventTarget = do_GetCurrentThread();
     MOZ_ASSERT(mEventTarget);
   }
+
+  CommonInit(aOther, aBlobImpl);
 }
 
 BlobChild::BlobChild(nsIContentChild* aManager,
                      const ChildBlobConstructorParams& aParams)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
   AssertCorrectThreadForManager(aManager);
@@ -2628,22 +2859,22 @@ BlobChild::BlobChild(nsIContentChild* aM
 BlobChild::BlobChild(PBackgroundChild* aManager,
                      const ChildBlobConstructorParams& aParams)
   : mBackgroundManager(aManager)
   , mContentManager(nullptr)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
 
-  CommonInit(aParams);
-
   if (!NS_IsMainThread()) {
     mEventTarget = do_GetCurrentThread();
     MOZ_ASSERT(mEventTarget);
   }
+
+  CommonInit(aParams);
 }
 
 BlobChild::BlobChild(nsIContentChild* aManager,
                      const nsID& aParentID,
                      RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
@@ -2657,22 +2888,22 @@ BlobChild::BlobChild(PBackgroundChild* a
                      const nsID& aParentID,
                      RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
   : mBackgroundManager(aManager)
   , mContentManager(nullptr)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
 
-  CommonInit(aParentID, aRemoteBlobSliceImpl);
-
   if (!NS_IsMainThread()) {
     mEventTarget = do_GetCurrentThread();
     MOZ_ASSERT(mEventTarget);
   }
+
+  CommonInit(aParentID, aRemoteBlobSliceImpl);
 }
 
 BlobChild::~BlobChild()
 {
   AssertIsOnOwningThread();
 
   MOZ_COUNT_DTOR(BlobChild);
 }
@@ -2690,24 +2921,30 @@ BlobChild::CommonInit(FileImpl* aBlobImp
 
   mBlobImpl->AddRef();
   mOwnsBlobImpl = true;
 
   memset(&mParentID, 0, sizeof(mParentID));
 }
 
 void
-BlobChild::CommonInit(BlobChild* aOther)
+BlobChild::CommonInit(BlobChild* aOther, FileImpl* aBlobImpl)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aOther);
   MOZ_ASSERT_IF(mContentManager, aOther->GetBackgroundManager());
-  MOZ_ASSERT_IF(mBackgroundManager, aOther->GetContentManager());
-
-  nsRefPtr<FileImpl> otherImpl = aOther->GetBlobImpl();
+  MOZ_ASSERT_IF(mContentManager, !aBlobImpl);
+  MOZ_ASSERT_IF(mBackgroundManager, aBlobImpl);
+
+  nsRefPtr<FileImpl> otherImpl;
+  if (mBackgroundManager && aOther->GetBackgroundManager()) {
+    otherImpl = aBlobImpl;
+  } else {
+    otherImpl = aOther->GetBlobImpl();
+  }
   MOZ_ASSERT(otherImpl);
 
   nsString contentType;
   otherImpl->GetType(contentType);
 
   ErrorResult rv;
   uint64_t length = otherImpl->GetSize(rv);
   MOZ_ASSERT(!rv.Failed());
@@ -2918,17 +3155,18 @@ BlobChild::GetOrCreateFromImpl(ChildMana
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(aBlobImpl);
 
   // If the blob represents a remote blob then we can simply pass its actor back
   // here.
   if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
-    BlobChild* actor = MaybeGetActorFromRemoteBlob(remoteBlob, aManager);
+    BlobChild* actor =
+      MaybeGetActorFromRemoteBlob(remoteBlob, aManager, aBlobImpl);
     if (actor) {
       return actor;
     }
   }
 
   // All blobs shared between threads or processes must be immutable.
   if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
     return nullptr;
@@ -3057,21 +3295,23 @@ BlobChild::SendSliceConstructor(ChildMan
 
   BlobChild::Destroy(newActor);
   return nullptr;
 }
 
 // static
 BlobChild*
 BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
-                                       nsIContentChild* aManager)
+                                       nsIContentChild* aManager,
+                                       FileImpl* aBlobImpl)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aRemoteBlob);
   MOZ_ASSERT(aManager);
+  MOZ_ASSERT(aBlobImpl);
 
   if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
     if (actor->GetContentManager() == aManager) {
       return actor;
     }
 
     MOZ_ASSERT(actor->GetBackgroundManager());
 
@@ -3086,46 +3326,45 @@ BlobChild::MaybeGetActorFromRemoteBlob(n
   }
 
   return nullptr;
 }
 
 // static
 BlobChild*
 BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
-                                       PBackgroundChild* aManager)
+                                       PBackgroundChild* aManager,
+                                       FileImpl* aBlobImpl)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aRemoteBlob);
   MOZ_ASSERT(aManager);
+  MOZ_ASSERT(aBlobImpl);
 
   if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
     if (actor->GetBackgroundManager() == aManager) {
       return actor;
     }
 
-    MOZ_ASSERT(actor->GetContentManager());
-
-    actor = new BlobChild(aManager, actor);
+    actor = new BlobChild(aManager, actor, aBlobImpl);
 
     ParentBlobConstructorParams params(
       KnownBlobConstructorParams(actor->ParentID()));
 
     aManager->SendPBlobConstructor(actor, params);
 
     return actor;
   }
 
   return nullptr;
 }
 
 const nsID&
 BlobChild::ParentID() const
 {
-  AssertIsOnOwningThread();
   MOZ_ASSERT(mRemoteBlobImpl);
 
   return mParentID;
 }
 
 already_AddRefed<FileImpl>
 BlobChild::GetBlobImpl()
 {
@@ -3645,20 +3884,17 @@ BlobParent::CreateFromParams(ParentManag
 
       nsRefPtr<IDTableEntry> idTableEntry =
         IDTableEntry::Get(params.id(), ActorManagerProcessID(aManager));
       if (NS_WARN_IF(!idTableEntry)) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
-      nsRefPtr<FileImpl> blobImpl = idTableEntry->BlobImpl();
-      MOZ_ASSERT(blobImpl);
-
-      return new BlobParent(aManager, blobImpl, idTableEntry);
+      return new BlobParent(aManager, idTableEntry);
     }
 
     case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
       if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
@@ -3866,16 +4102,18 @@ BlobParent::RecvPBlobStreamConstructor(P
                                        const uint64_t& aLength)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
+  auto* actor = static_cast<InputStreamParent*>(aActor);
+
   // Make sure we can't overflow.
   if (NS_WARN_IF(UINT64_MAX - aLength < aStart)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   ErrorResult errorResult;
   uint64_t blobLength = mBlobImpl->GetSize(errorResult);
@@ -3911,17 +4149,17 @@ BlobParent::RecvPBlobStreamConstructor(P
   if (mBlobImpl->IsMemoryFile()) {
     InputStreamParams params;
     nsTArray<FileDescriptor> fds;
     SerializeInputStream(stream, params, fds);
 
     MOZ_ASSERT(params.type() != InputStreamParams::T__None);
     MOZ_ASSERT(fds.IsEmpty());
 
-    return PBlobStreamParent::Send__delete__(aActor, params, void_t());
+    return actor->Destroy(params, void_t());
   }
 
   nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlobImpl);
   nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
   if (remoteBlob) {
     remoteStream = do_QueryInterface(stream);
   }
 
@@ -3948,17 +4186,17 @@ BlobParent::RecvPBlobStreamConstructor(P
 
   nsCOMPtr<nsIThread> target;
   errorResult = NS_NewNamedThread("Blob Opener", getter_AddRefs(target));
   if (NS_WARN_IF(errorResult.Failed())) {
     return false;
   }
 
   nsRefPtr<OpenStreamRunnable> runnable =
-    new OpenStreamRunnable(this, aActor, stream, serializableStream, target);
+    new OpenStreamRunnable(this, actor, stream, serializableStream, target);
 
   errorResult = runnable->Dispatch();
   if (NS_WARN_IF(errorResult.Failed())) {
     return false;
   }
 
   // nsRevocableEventPtr lacks some of the operators needed for anything nicer.
   *mOpenStreamRunnables.AppendElement() = runnable;
@@ -4031,16 +4269,59 @@ BlobParent::RecvResolveMystery(const Res
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   MOZ_CRASH("Should never get here!");
 }
 
 bool
+BlobParent::RecvBlobStreamSync(const uint64_t& aStart,
+                               const uint64_t& aLength,
+                               InputStreamParams* aParams,
+                               OptionalFileDescriptorSet* aFDs)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mBlobImpl);
+  MOZ_ASSERT(!mRemoteBlobImpl);
+  MOZ_ASSERT(mOwnsBlobImpl);
+
+  bool finished = false;
+
+  {
+    // Calling RecvPBlobStreamConstructor() may synchronously delete the actor
+    // we pass in so don't touch it outside this block.
+    auto* streamActor = new InputStreamParent(&finished, aParams, aFDs);
+
+    if (NS_WARN_IF(!RecvPBlobStreamConstructor(streamActor, aStart, aLength))) {
+      // If RecvPBlobStreamConstructor() returns false then it is our
+      // responsibility to destroy the actor.
+      delete streamActor;
+      return false;
+    }
+  }
+
+  if (finished) {
+    // The actor is already dead and we have already set our out params.
+    return true;
+  }
+
+  // The actor is alive and will be doing asynchronous work to load the stream.
+  // Spin a nested loop here while we wait for it.
+  nsIThread* currentThread = NS_GetCurrentThread();
+  MOZ_ASSERT(currentThread);
+
+  while (!finished) {
+    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+  }
+
+  return true;
+}
+
+bool
 BlobParent::RecvWaitForSliceCreation()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
   // The whole point of this message is to ensure that the sliced blob created
   // by the child has been inserted into our IDTable.
@@ -4195,32 +4476,26 @@ IDTableEntry::GetOrCreateInternal(const 
 }
 
 /*******************************************************************************
  * Other stuff
  ******************************************************************************/
 
 bool
 InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
-                                 const OptionalFileDescriptorSet& aFDs)
+                                 const OptionalFileDescriptorSet& aOptionalSet)
 {
   MOZ_ASSERT(mRemoteStream);
   mRemoteStream->AssertIsOnOwningThread();
 
   nsTArray<FileDescriptor> fds;
-  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-    FileDescriptorSetChild* fdSetActor =
-      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
-    MOZ_ASSERT(fdSetActor);
-
-    fdSetActor->ForgetFileDescriptors(fds);
-    MOZ_ASSERT(!fds.IsEmpty());
-
-    fdSetActor->Send__delete__(fdSetActor);
-  }
+  OptionalFileDescriptorSetToFDs(
+    // XXX Fix this somehow...
+    const_cast<OptionalFileDescriptorSet&>(aOptionalSet),
+    fds);
 
   nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
   MOZ_ASSERT(stream);
 
   mRemoteStream->SetStream(stream);
   return true;
 }
 
--- a/dom/ipc/BlobChild.h
+++ b/dom/ipc/BlobChild.h
@@ -130,17 +130,17 @@ public:
 private:
   // These constructors are called on the sending side.
   BlobChild(nsIContentChild* aManager, FileImpl* aBlobImpl);
 
   BlobChild(PBackgroundChild* aManager, FileImpl* aBlobImpl);
 
   BlobChild(nsIContentChild* aManager, BlobChild* aOther);
 
-  BlobChild(PBackgroundChild* aManager, BlobChild* aOther);
+  BlobChild(PBackgroundChild* aManager, BlobChild* aOther, FileImpl* aBlobImpl);
 
   // These constructors are called on the receiving side.
   BlobChild(nsIContentChild* aManager,
             const ChildBlobConstructorParams& aParams);
 
   BlobChild(PBackgroundChild* aManager,
             const ChildBlobConstructorParams& aParams);
 
@@ -155,17 +155,17 @@ private:
 
   // Only called by Destroy().
   ~BlobChild();
 
   void
   CommonInit(FileImpl* aBlobImpl);
 
   void
-  CommonInit(BlobChild* aOther);
+  CommonInit(BlobChild* aOther, FileImpl* aBlobImpl);
 
   void
   CommonInit(const ChildBlobConstructorParams& aParams);
 
   void
   CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl);
 
   template <class ChildManagerType>
@@ -180,21 +180,23 @@ private:
   template <class ChildManagerType>
   static BlobChild*
   SendSliceConstructor(ChildManagerType* aManager,
                        RemoteBlobSliceImpl* aRemoteBlobSliceImpl,
                        const ParentBlobConstructorParams& aParams);
 
   static BlobChild*
   MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
-                              nsIContentChild* aManager);
+                              nsIContentChild* aManager,
+                              FileImpl* aBlobImpl);
 
   static BlobChild*
   MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
-                              PBackgroundChild* aManager);
+                              PBackgroundChild* aManager,
+                              FileImpl* aBlobImpl);
 
   void
   NoteDyingRemoteBlobImpl();
 
   nsIEventTarget*
   EventTarget() const
   {
     return mEventTarget;
--- a/dom/ipc/BlobParent.h
+++ b/dom/ipc/BlobParent.h
@@ -216,16 +216,22 @@ private:
 
   virtual bool
   DeallocPBlobStreamParent(PBlobStreamParent* aActor) MOZ_OVERRIDE;
 
   virtual bool
   RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
 
   virtual bool
+  RecvBlobStreamSync(const uint64_t& aStart,
+                     const uint64_t& aLength,
+                     InputStreamParams* aParams,
+                     OptionalFileDescriptorSet* aFDs) MOZ_OVERRIDE;
+
+  virtual bool
   RecvWaitForSliceCreation() MOZ_OVERRIDE;
 
   virtual bool
   RecvGetFileId(int64_t* aFileId) MOZ_OVERRIDE;
 
   virtual bool
   RecvGetFilePath(nsString* aFilePath) MOZ_OVERRIDE;
 };
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -1,18 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlobStream;
 include protocol PContent;
 include protocol PContentBridge;
+include protocol PFileDescriptorSet;
 
 include DOMTypes;
+include InputStreamParams;
 
 namespace mozilla {
 namespace dom {
 
 union ResolveMysteryParams
 {
   NormalBlobConstructorParams;
   FileBlobConstructorParams;
@@ -26,16 +28,19 @@ sync protocol PBlob
 both:
   __delete__();
 
 parent:
   PBlobStream(uint64_t begin, uint64_t length);
 
   ResolveMystery(ResolveMysteryParams params);
 
+  sync BlobStreamSync(uint64_t begin, uint64_t length)
+    returns (InputStreamParams params, OptionalFileDescriptorSet fds);
+
   sync WaitForSliceCreation();
 
   // Use only for testing!
   sync GetFileId()
     returns (int64_t fileId);
 
   // Use only for testing!
   sync GetFilePath()
--- a/dom/ipc/PBlobStream.ipdl
+++ b/dom/ipc/PBlobStream.ipdl
@@ -1,18 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBlob;
 include protocol PFileDescriptorSet;
 include InputStreamParams;
 
-using mozilla::void_t from "ipc/IPCMessageUtils.h";
-
 namespace mozilla {
 namespace dom {
 
 protocol PBlobStream
 {
   manager PBlob;
 
 child:
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -109,16 +109,17 @@ LOCAL_INCLUDES += [
     '/dom/bluetooth/ipc',
     '/dom/devicestorage',
     '/dom/filesystem',
     '/dom/fmradio/ipc',
     '/dom/geolocation',
     '/dom/media/webspeech/synth/ipc',
     '/dom/mobilemessage/ipc',
     '/dom/storage',
+    '/dom/workers',
     '/editor/libeditor',
     '/embedding/components/printingui/ipc',
     '/extensions/cookie',
     '/extensions/spellcheck/src',
     '/hal/sandbox',
     '/js/ipc',
     '/layout/base',
     '/netwerk/base/src',
--- a/dom/webidl/DOMError.webidl
+++ b/dom/webidl/DOMError.webidl
@@ -6,16 +6,16 @@
  * The origin of this IDL file is
  * http://dom.spec.whatwg.org/#domerror
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Constructor(DOMString name, optional DOMString message = ""),
- Exposed=(Window,System)]
+ Exposed=(Window,Worker,System)]
 interface DOMError {
   [Constant]
   readonly attribute DOMString name;
 
   [Constant]
   readonly attribute DOMString message;
 };
--- a/dom/webidl/DOMStringList.webidl
+++ b/dom/webidl/DOMStringList.webidl
@@ -5,13 +5,14 @@
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/2012/WD-dom-20120105/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+[Exposed=(Window,Worker)]
 interface DOMStringList {
   readonly attribute unsigned long length;
   getter DOMString? item(unsigned long index);
   boolean contains(DOMString string);
 };
--- a/dom/webidl/IDBCursor.webidl
+++ b/dom/webidl/IDBCursor.webidl
@@ -9,16 +9,17 @@
 
 enum IDBCursorDirection {
     "next",
     "nextunique",
     "prev",
     "prevunique"
 };
 
+[Exposed=(Window,Worker)]
 interface IDBCursor {
     readonly    attribute (IDBObjectStore or IDBIndex) source;
 
     readonly    attribute IDBCursorDirection           direction;
 
     [Throws]
     readonly    attribute any                          key;
 
--- a/dom/webidl/IDBDatabase.webidl
+++ b/dom/webidl/IDBDatabase.webidl
@@ -5,16 +5,17 @@
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBObjectStoreParameters
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+[Exposed=(Window,Worker)]
 interface IDBDatabase : EventTarget {
     readonly    attribute DOMString          name;
     readonly    attribute unsigned long long version;
 
     readonly    attribute DOMStringList      objectStoreNames;
 
     [Throws]
     IDBObjectStore createObjectStore (DOMString name, optional IDBObjectStoreParameters optionalParameters);
@@ -35,18 +36,18 @@ interface IDBDatabase : EventTarget {
     void           close ();
 
                 attribute EventHandler       onabort;
                 attribute EventHandler       onerror;
                 attribute EventHandler       onversionchange;
 };
 
 partial interface IDBDatabase {
-    [Pref="dom.indexedDB.experimental"]
+    [Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     readonly    attribute StorageType        storage;
 
-    [Throws]
+    [Exposed=Window, Throws]
     IDBRequest createMutableFile (DOMString name, optional DOMString type);
 
     // this is deprecated due to renaming in the spec
-    [Throws]
+    [Exposed=Window, Throws]
     IDBRequest mozCreateFileHandle (DOMString name, optional DOMString type); // now createMutableFile
 };
--- a/dom/webidl/IDBEnvironment.webidl
+++ b/dom/webidl/IDBEnvironment.webidl
@@ -2,18 +2,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is:
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html
  */
 
-[NoInterfaceObject]
+[Exposed=(Window,Worker), NoInterfaceObject]
 interface IDBEnvironment {
     //[Throws] readonly    attribute IDBFactory indexedDB;
     [Throws] readonly    attribute IDBFactory? indexedDB;
 };
 
 // Mozilla-specific stuff
 partial interface IDBEnvironment {
-    [Throws] readonly    attribute IDBFactory? mozIndexedDB;
+    [Exposed=Window, Throws]
+    readonly    attribute IDBFactory? mozIndexedDB;
 };
--- a/dom/webidl/IDBFactory.webidl
+++ b/dom/webidl/IDBFactory.webidl
@@ -18,16 +18,17 @@ dictionary IDBOpenDBOptions
   StorageType storage;
 };
 
 /**
  * Interface that defines the indexedDB property on a window.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBFactory
  * for more information.
  */
+[Exposed=(Window,Worker)]
 interface IDBFactory {
   [Throws]
   IDBOpenDBRequest
   open(DOMString name,
        [EnforceRange] unsigned long long version);
 
   [Throws]
   IDBOpenDBRequest
--- a/dom/webidl/IDBIndex.webidl
+++ b/dom/webidl/IDBIndex.webidl
@@ -7,16 +7,17 @@
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBIndexParameters
  */
 
 dictionary IDBIndexParameters {
     boolean unique = false;
     boolean multiEntry = false;
 };
 
+[Exposed=(Window,Worker)]
 interface IDBIndex {
     readonly    attribute DOMString      name;
     readonly    attribute IDBObjectStore objectStore;
 
     [Throws]
     readonly    attribute any            keyPath;
 
     readonly    attribute boolean        multiEntry;
@@ -40,14 +41,16 @@ interface IDBIndex {
 
 partial interface IDBIndex {
     [Throws]
     IDBRequest mozGetAll (optional any key, optional unsigned long limit);
 
     [Throws]
     IDBRequest mozGetAllKeys (optional any key, optional unsigned long limit);
 
-    [Pref="dom.indexedDB.experimental", Throws]
+    [Throws,
+     Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     IDBRequest getAll (optional any key, optional unsigned long limit);
 
-    [Pref="dom.indexedDB.experimental", Throws]
+    [Throws,
+     Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     IDBRequest getAllKeys (optional any key, optional unsigned long limit);
 };
--- a/dom/webidl/IDBKeyRange.webidl
+++ b/dom/webidl/IDBKeyRange.webidl
@@ -4,16 +4,17 @@
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+[Exposed=(Window,Worker)]
 interface IDBKeyRange {
   [Throws]
   readonly attribute any     lower;
   [Throws]
   readonly attribute any     upper;
   [Constant]
   readonly attribute boolean lowerOpen;
   [Constant]
--- a/dom/webidl/IDBObjectStore.webidl
+++ b/dom/webidl/IDBObjectStore.webidl
@@ -7,16 +7,17 @@
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBObjectStore
  */
 
 dictionary IDBObjectStoreParameters {
     (DOMString or sequence<DOMString>)? keyPath = null;
     boolean                             autoIncrement = false;
 };
 
+[Exposed=(Window,Worker)]
 interface IDBObjectStore {
     readonly    attribute DOMString      name;
 
     [Throws]
     readonly    attribute any            keyPath;
 
     readonly    attribute DOMStringList  indexNames;
     readonly    attribute IDBTransaction transaction;
@@ -59,17 +60,20 @@ interface IDBObjectStore {
     IDBRequest count (optional any key);
 };
 
 partial interface IDBObjectStore {
     // Success fires IDBTransactionEvent, result == array of values for given keys
     [Throws]
     IDBRequest mozGetAll (optional any key, optional unsigned long limit);
 
-    [Pref="dom.indexedDB.experimental", Throws]
+    [Throws,
+     Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     IDBRequest getAll (optional any key, optional unsigned long limit);
 
-    [Pref="dom.indexedDB.experimental", Throws]
+    [Throws,
+     Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     IDBRequest getAllKeys (optional any key, optional unsigned long limit);
 
-    [Pref="dom.indexedDB.experimental", Throws]
+    [Throws,
+     Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     IDBRequest openKeyCursor (optional any range, optional IDBCursorDirection direction = "next");
 };
--- a/dom/webidl/IDBOpenDBRequest.webidl
+++ b/dom/webidl/IDBOpenDBRequest.webidl
@@ -2,13 +2,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBOpenDBRequest
  */
 
+[Exposed=(Window,Worker)]
 interface IDBOpenDBRequest : IDBRequest {
                 attribute EventHandler onblocked;
 
                 attribute EventHandler onupgradeneeded;
 };
--- a/dom/webidl/IDBRequest.webidl
+++ b/dom/webidl/IDBRequest.webidl
@@ -8,16 +8,17 @@
  * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBRequestReadyState
  */
 
 enum IDBRequestReadyState {
     "pending",
     "done"
 };
 
+[Exposed=(Window,Worker)]
 interface IDBRequest : EventTarget {
     [Throws]
     readonly    attribute any                  result;
 
     [Throws]
     readonly    attribute DOMError?            error;
 
     readonly    attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
--- a/dom/webidl/IDBTransaction.webidl
+++ b/dom/webidl/IDBTransaction.webidl
@@ -9,16 +9,17 @@
  */
 
 enum IDBTransactionMode {
     "readonly",
     "readwrite",
     "versionchange"
 };
 
+[Exposed=(Window,Worker)]
 interface IDBTransaction : EventTarget {
     [Throws]
     readonly    attribute IDBTransactionMode mode;
     readonly    attribute IDBDatabase        db;
 
     readonly    attribute DOMError?          error;
 
     [Throws]
--- a/dom/webidl/IDBVersionChangeEvent.webidl
+++ b/dom/webidl/IDBVersionChangeEvent.webidl
@@ -10,14 +10,15 @@
  * liability, trademark and document use rules apply.
  */
 
 dictionary IDBVersionChangeEventInit : EventInit {
     unsigned long long  oldVersion = 0;
     unsigned long long? newVersion = null;
 };
 
-[Constructor(DOMString type, optional IDBVersionChangeEventInit eventInitDict)]
+[Constructor(DOMString type, optional IDBVersionChangeEventInit eventInitDict),
+ Exposed=(Window,Worker)]
 interface IDBVersionChangeEvent : Event {
     readonly    attribute unsigned long long  oldVersion;
     readonly    attribute unsigned long long? newVersion;
 };
 
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -34,16 +34,17 @@ partial interface WorkerGlobalScope {
   void importScripts(DOMString... urls);
 
   readonly attribute WorkerNavigator navigator;
 };
 
 WorkerGlobalScope implements WindowTimers;
 WorkerGlobalScope implements WindowBase64;
 WorkerGlobalScope implements GlobalFetch;
+WorkerGlobalScope implements IDBEnvironment;
 
 // Not implemented yet: bug 1072107.
 // WorkerGlobalScope implements FontFaceSource;
 
 // Mozilla extensions
 partial interface WorkerGlobalScope {
   attribute EventHandler onclose;
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -30,16 +30,18 @@
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/AtomList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Navigator.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "nsDOMJSUtils.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsISupportsImpl.h"
@@ -66,22 +68,24 @@
 #ifdef ENABLE_TESTS
 #include "BackgroundChildImpl.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "prrng.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
 
 USING_WORKERS_NAMESPACE
 
 using mozilla::MutexAutoLock;
 using mozilla::MutexAutoUnlock;
 using mozilla::Preferences;
+using mozilla::dom::indexedDB::IndexedDatabaseManager;
 
 // The size of the worker runtime heaps in bytes. May be changed via pref.
 #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
 
 // The size of the generational GC nursery for workers, in bytes.
 #define WORKER_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
 
 // The size of the worker JS allocation threshold in MB. May be changed via pref.
@@ -1116,16 +1120,50 @@ PlatformOverrideChanged(const char* /* a
     mozilla::Preferences::GetString(PREF_GENERAL_PLATFORM_OVERRIDE);
 
   RuntimeService* runtime = RuntimeService::GetService();
   if (runtime) {
     runtime->UpdatePlatformOverridePreference(override);
   }
 }
 
+class BackgroundChildCallback MOZ_FINAL
+  : public nsIIPCBackgroundChildCreateCallback
+{
+public:
+  BackgroundChildCallback()
+  {
+    AssertIsOnMainThread();
+  }
+
+  NS_DECL_ISUPPORTS
+
+private:
+  ~BackgroundChildCallback()
+  {
+    AssertIsOnMainThread();
+  }
+
+  virtual void
+  ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aActor);
+  }
+
+  virtual void
+  ActorFailed() MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+    MOZ_CRASH("Unable to connect PBackground actor for the main thread!");
+  }
+};
+
+NS_IMPL_ISUPPORTS(BackgroundChildCallback, nsIIPCBackgroundChildCreateCallback)
+
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
 void
 CancelWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
@@ -1634,16 +1672,25 @@ RuntimeService::ShutdownIdleThreads(nsIT
 
 nsresult
 RuntimeService::Init()
 {
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
+  // Make sure PBackground actors are connected as soon as possible for the main
+  // thread in case workers clone remote blobs here.
+  if (!BackgroundChild::GetForCurrentThread()) {
+    nsRefPtr<BackgroundChildCallback> callback = new BackgroundChildCallback();
+    if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) {
+      MOZ_CRASH("Unable to connect PBackground actor for the main thread!");
+    }
+  }
+
   // Initialize JSSettings.
   if (!sDefaultJSSettings.gcSettings[0].IsSet()) {
     sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions();
     sDefaultJSSettings.chrome.maxScriptRuntime = -1;
     sDefaultJSSettings.chrome.compartmentOptions.setVersion(JSVERSION_LATEST);
     sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
 #ifdef JS_GC_ZEAL
     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
@@ -1771,16 +1818,20 @@ RuntimeService::Init()
                                              MAX_WORKERS_PER_DOMAIN);
   gMaxWorkersPerDomain = std::max(0, maxPerDomain);
 
   rv = InitOSFileConstants();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   return NS_OK;
 }
 
 void
 RuntimeService::Shutdown()
 {
   AssertIsOnMainThread();
 
@@ -2155,17 +2206,17 @@ RuntimeService::CreateServiceWorker(cons
   serviceWorker->mScope = NS_ConvertUTF8toUTF16(aScope);
 
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
 
 nsresult
 RuntimeService::CreateServiceWorkerFromLoadInfo(JSContext* aCx,
-                                               WorkerPrivate::LoadInfo aLoadInfo,
+                                               WorkerPrivate::LoadInfo* aLoadInfo,
                                                const nsAString& aScriptURL,
                                                const nsACString& aScope,
                                                ServiceWorker** aServiceWorker)
 {
 
   nsRefPtr<SharedWorker> sharedWorker;
   nsresult rv = CreateSharedWorkerFromLoadInfo(aCx, aLoadInfo, aScriptURL, aScope,
                                                WorkerTypeService,
@@ -2200,64 +2251,64 @@ RuntimeService::CreateSharedWorkerIntern
 
   JSContext* cx = aGlobal.Context();
 
   WorkerPrivate::LoadInfo loadInfo;
   nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
                                            false, &loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return CreateSharedWorkerFromLoadInfo(cx, loadInfo, aScriptURL, aName, aType,
+  return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName, aType,
                                         aSharedWorker);
 }
 
 nsresult
 RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
-                                               WorkerPrivate::LoadInfo aLoadInfo,
+                                               WorkerPrivate::LoadInfo* aLoadInfo,
                                                const nsAString& aScriptURL,
                                                const nsACString& aName,
                                                WorkerType aType,
                                                SharedWorker** aSharedWorker)
 {
   AssertIsOnMainThread();
-
-  MOZ_ASSERT(aLoadInfo.mResolvedScriptURI);
+  MOZ_ASSERT(aLoadInfo);
+  MOZ_ASSERT(aLoadInfo->mResolvedScriptURI);
 
   nsRefPtr<WorkerPrivate> workerPrivate;
   {
     MutexAutoLock lock(mMutex);
 
     WorkerDomainInfo* domainInfo;
     SharedWorkerInfo* sharedWorkerInfo;
 
     nsCString scriptSpec;
-    nsresult rv = aLoadInfo.mResolvedScriptURI->GetSpec(scriptSpec);
+    nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString key;
     GenerateSharedWorkerKey(scriptSpec, aName, key);
 
-    if (mDomainMap.Get(aLoadInfo.mDomain, &domainInfo) &&
+    if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) &&
         domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) {
       workerPrivate = sharedWorkerInfo->mWorkerPrivate;
     }
   }
 
   // Keep a reference to the window before spawning the worker. If the worker is
   // a Shared/Service worker and the worker script loads and executes before
   // the SharedWorker object itself is created before then WorkerScriptLoaded()
   // will reset the loadInfo's window.
-  nsCOMPtr<nsPIDOMWindow> window = aLoadInfo.mWindow;
+  nsCOMPtr<nsPIDOMWindow> window = aLoadInfo->mWindow;
 
   bool created = false;
   if (!workerPrivate) {
     ErrorResult rv;
     workerPrivate =
       WorkerPrivate::Constructor(aCx, aScriptURL, false,
-                                 aType, aName, &aLoadInfo, rv);
+                                 aType, aName, aLoadInfo, rv);
     NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode());
 
     created = true;
   }
 
   nsRefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate);
 
   if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -150,17 +150,17 @@ public:
   nsresult
   CreateServiceWorker(const GlobalObject& aGlobal,
                       const nsAString& aScriptURL,
                       const nsACString& aScope,
                       ServiceWorker** aServiceWorker);
 
   nsresult
   CreateServiceWorkerFromLoadInfo(JSContext* aCx,
-                                  WorkerPrivate::LoadInfo aLoadInfo,
+                                  WorkerPrivate::LoadInfo* aLoadInfo,
                                   const nsAString& aScriptURL,
                                   const nsACString& aScope,
                                   ServiceWorker** aServiceWorker);
 
   void
   ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
 
   const NavigatorProperties&
@@ -303,17 +303,17 @@ private:
   CreateSharedWorkerInternal(const GlobalObject& aGlobal,
                              const nsAString& aScriptURL,
                              const nsACString& aName,
                              WorkerType aType,
                              SharedWorker** aSharedWorker);
 
   nsresult
   CreateSharedWorkerFromLoadInfo(JSContext* aCx,
-                                 WorkerPrivate::LoadInfo aLoadInfo,
+                                 WorkerPrivate::LoadInfo* aLoadInfo,
                                  const nsAString& aScriptURL,
                                  const nsACString& aName,
                                  WorkerType aType,
                                  SharedWorker** aSharedWorker);
 };
 
 END_WORKERS_NAMESPACE
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2117,17 +2117,17 @@ ServiceWorkerManager::CreateServiceWorke
   AutoSafeJSContext cx;
 
   nsRefPtr<ServiceWorker> serviceWorker;
   RuntimeService* rs = RuntimeService::GetService();
   if (!rs) {
     return NS_ERROR_FAILURE;
   }
 
-  rv = rs->CreateServiceWorkerFromLoadInfo(cx, info, NS_ConvertUTF8toUTF16(aScriptSpec), aScope,
+  rv = rs->CreateServiceWorkerFromLoadInfo(cx, &info, NS_ConvertUTF8toUTF16(aScriptSpec), aScope,
                                            getter_AddRefs(serviceWorker));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -15,25 +15,25 @@
 #include "nsIDOMMessageEvent.h"
 #include "nsIDocument.h"
 #include "nsIDocShell.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
-#include "nsPerformance.h"
-#include "nsPIDOMWindow.h"
 #include "nsITextToSubURI.h"
 #include "nsIThreadInternal.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIWorkerDebugger.h"
 #include "nsIXPConnect.h"
+#include "nsPerformance.h"
+#include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "jsfriendapi.h"
 #include "js/MemoryMetrics.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
@@ -49,16 +49,22 @@
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredClone.h"
 #include "mozilla/dom/WorkerBinding.h"
+#include "mozilla/dom/indexedDB/IDBFactory.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Preferences.h"
 #include "nsAlgorithm.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsDOMJSUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
@@ -85,32 +91,38 @@
 #include "ServiceWorkerManager.h"
 #include "SharedWorker.h"
 #include "WorkerDebuggerManager.h"
 #include "WorkerFeature.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 #include "WorkerThread.h"
 
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
 // JS_MaybeGC will run once every second during normal execution.
 #define PERIODIC_GC_TIMER_DELAY_SEC 1
 
 // A shrinking GC will run five seconds after the last event is processed.
 #define IDLE_GC_TIMER_DELAY_SEC 5
 
 #define PREF_WORKERS_ENABLED "dom.workers.enabled"
 
 #ifdef WORKER_LOGGING
 #define LOG(_args) do { printf _args ; fflush(stdout); } while (0)
 #else
 #define LOG(_args) do { } while (0)
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
 USING_WORKERS_NAMESPACE
 
 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
 
 #ifdef DEBUG
 
 BEGIN_WORKERS_NAMESPACE
 
@@ -290,53 +302,131 @@ LogErrorToConsole(const nsAString& aMess
   __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
                       filename.get(), aLineNumber);
 #endif
 
   fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber);
   fflush(stderr);
 }
 
+void
+ReadBlobOrFile(JSContext* aCx,
+               JSStructuredCloneReader* aReader,
+               bool aIsMainThread,
+               JS::MutableHandle<JSObject*> aBlobOrFile)
+{
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aReader);
+  MOZ_ASSERT(!aBlobOrFile);
+
+  nsRefPtr<FileImpl> blobImpl;
+  {
+    FileImpl* rawBlobImpl;
+    MOZ_ALWAYS_TRUE(JS_ReadBytes(aReader, &rawBlobImpl, sizeof(rawBlobImpl)));
+
+    MOZ_ASSERT(rawBlobImpl);
+
+    blobImpl = rawBlobImpl;
+  }
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
+  if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
+    PBackgroundChild* backgroundManager =
+      BackgroundChild::GetForCurrentThread();
+    MOZ_ASSERT(backgroundManager);
+
+    // Always make sure we have a blob from an actor we can use on this thread.
+    BlobChild* blobChild = BlobChild::GetOrCreate(backgroundManager, blobImpl);
+    MOZ_ASSERT(blobChild);
+
+    blobImpl = blobChild->GetBlobImpl();
+    MOZ_ASSERT(blobImpl);
+  }
+
+  nsCOMPtr<nsISupports> parent;
+  if (aIsMainThread) {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
+      nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
+    parent = do_QueryInterface(scriptGlobal);
+  } else {
+    MOZ_ASSERT(!NS_IsMainThread());
+    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+    MOZ_ASSERT(workerPrivate);
+
+    WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
+    MOZ_ASSERT(globalScope);
+
+    parent = do_QueryObject(globalScope);
+  }
+
+  nsRefPtr<File> blob = new File(parent, blobImpl);
+  aBlobOrFile.set(blob->WrapObject(aCx));
+}
+
+bool
+WriteBlobOrFile(JSContext* aCx,
+                JSStructuredCloneWriter* aWriter,
+                FileImpl* aBlobOrFileImpl,
+                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+{
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aWriter);
+  MOZ_ASSERT(aBlobOrFileImpl);
+
+  nsRefPtr<FileImpl> newBlobOrFileImpl;
+  if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(aBlobOrFileImpl)) {
+    PBackgroundChild* backgroundManager =
+      BackgroundChild::GetForCurrentThread();
+    MOZ_ASSERT(backgroundManager);
+
+    // Always make sure we have a blob from an actor we can use on this thread.
+    BlobChild* blobChild =
+      BlobChild::GetOrCreate(backgroundManager, aBlobOrFileImpl);
+    MOZ_ASSERT(blobChild);
+
+    newBlobOrFileImpl = blobChild->GetBlobImpl();
+    MOZ_ASSERT(newBlobOrFileImpl);
+
+    aBlobOrFileImpl = newBlobOrFileImpl;
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aBlobOrFileImpl->SetMutable(false)));
+
+  if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0)) ||
+      NS_WARN_IF(!JS_WriteBytes(aWriter,
+                                &aBlobOrFileImpl,
+                                sizeof(aBlobOrFileImpl)))) {
+    return false;
+  }
+
+  aClonedObjects.AppendElement(aBlobOrFileImpl);
+  return true;
+}
+
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
     JS::Rooted<JSObject*> result(aCx);
 
     // See if object is a nsIDOMBlob pointer.
     if (aTag == DOMWORKER_SCTAG_BLOB) {
       MOZ_ASSERT(!aData);
 
-      FileImpl* blobImpl;
-      if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
-        MOZ_ASSERT(blobImpl);
-
-#ifdef DEBUG
-        {
-          // Blob should not be mutable.
-          bool isMutable;
-          NS_ASSERTION(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)) &&
-                       !isMutable,
-                       "Only immutable blob should be passed to worker");
-        }
-#endif
-
-        {
-          // New scope to protect |result| from a moving GC during ~nsRefPtr.
-          nsRefPtr<File> blob = new File(nullptr, blobImpl);
-          JS::Rooted<JS::Value> val(aCx);
-          if (GetOrCreateDOMReflector(aCx, blob, &val)) {
-            result = val.toObjectOrNull();
-          }
-        }
-
-        return result;
-      }
+      JS::Rooted<JSObject*> blobOrFile(aCx);
+      ReadBlobOrFile(aCx, aReader, /* aIsMainThread */ false, &blobOrFile);
+
+      return blobOrFile;
     }
     // See if the object is an ImageData.
     else if (aTag == SCTAG_DOM_IMAGEDATA) {
       MOZ_ASSERT(!aData);
       return ReadStructuredCloneImageData(aCx, aReader);
     }
 
     Error(aCx, 0);
@@ -345,28 +435,27 @@ struct WorkerStructuredCloneCallbacks
 
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
         JS::Handle<JSObject*> aObj, void* aClosure)
   {
     NS_ASSERTION(aClosure, "Null pointer!");
 
     // We'll stash any nsISupports pointers that need to be AddRef'd here.
-    nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
-      static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+    auto* clonedObjects =
+      static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
 
     // See if this is a Blob/File object.
     {
-      File* blob = nullptr;
+      nsRefPtr<File> blob;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
         FileImpl* blobImpl = blob->Impl();
-        if (blobImpl && NS_SUCCEEDED(blobImpl->SetMutable(false)) &&
-            JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
-            JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl))) {
-          clonedObjects->AppendElement(blobImpl);
+        MOZ_ASSERT(blobImpl);
+
+        if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
           return true;
         }
       }
     }
 
     // See if this is an ImageData object.
     {
       ImageData* imageData = nullptr;
@@ -402,74 +491,48 @@ struct MainThreadWorkerStructuredCloneCa
        uint32_t aData, void* aClosure)
   {
     AssertIsOnMainThread();
 
     // See if object is a Blob/File pointer.
     if (aTag == DOMWORKER_SCTAG_BLOB) {
       MOZ_ASSERT(!aData);
 
-      FileImpl* blobImpl;
-      if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
-        MOZ_ASSERT(blobImpl);
-
-#ifdef DEBUG
-        {
-          // Blob should not be mutable.
-          bool isMutable;
-          NS_ASSERTION(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)) &&
-                       !isMutable,
-                       "Only immutable blob should be passed to worker");
-        }
-#endif
-
-        // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
-        // called because the static analysis thinks dereferencing XPCOM objects
-        // can GC (because in some cases it can!), and a return statement with a
-        // JSObject* type means that JSObject* is on the stack as a raw pointer
-        // while destructors are running.
-        JS::Rooted<JS::Value> val(aCx);
-        {
-          nsRefPtr<File> blob = new File(nullptr, blobImpl);
-          if (!GetOrCreateDOMReflector(aCx, blob, &val)) {
-            return nullptr;
-          }
-        }
-
-        return &val.toObject();
-      }
+      JS::Rooted<JSObject*> blobOrFile(aCx);
+      ReadBlobOrFile(aCx, aReader, /* aIsMainThread */ true, &blobOrFile);
+
+      return blobOrFile;
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
   }
 
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
         JS::Handle<JSObject*> aObj, void* aClosure)
   {
     AssertIsOnMainThread();
 
     NS_ASSERTION(aClosure, "Null pointer!");
 
     // We'll stash any nsISupports pointers that need to be AddRef'd here.
-    nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
-      static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+    auto* clonedObjects =
+      static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
 
     // See if this is a Blob/File object.
     {
-      File* blob = nullptr;
+      nsRefPtr<File> blob;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
         FileImpl* blobImpl = blob->Impl();
+        MOZ_ASSERT(blobImpl);
+
         if (blobImpl->IsCCed()) {
           NS_WARNING("Cycle collected blob objects are not supported!");
-        } else if (NS_SUCCEEDED(blobImpl->SetMutable(false)) &&
-                   JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
-                   JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl))) {
-          clonedObjects->AppendElement(blobImpl);
+        } else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
           return true;
         }
       }
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
   }
@@ -1219,17 +1282,17 @@ private:
         return true;
       }
 
       // The innerWindowId is only required if we are going to ReportError
       // below, which is gated on this condition. The inner window correctness
       // check is only going to succeed when the worker is accepting events.
       if (workerIsAcceptingEvents) {
         aWorkerPrivate->AssertInnerWindowIsCorrect();
-        innerWindowId = aWorkerPrivate->GetInnerWindowId();
+        innerWindowId = aWorkerPrivate->WindowID();
       }
     }
 
     // Don't fire this event if the JS object has been disconnected from the
     // private object.
     if (!workerIsAcceptingEvents) {
       return true;
     }
@@ -1705,16 +1768,56 @@ private:
       return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial);
     }
 
     aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial);
     return true;
   }
 };
 
+class DummyRunnable MOZ_FINAL
+  : public WorkerRunnable
+{
+public:
+  explicit
+  DummyRunnable(WorkerPrivate* aWorkerPrivate)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+private:
+  ~DummyRunnable()
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  virtual bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT_UNREACHABLE("Should never call Dispatch on this!");
+    return true;
+  }
+
+  virtual void
+  PostDispatch(JSContext* aCx,
+               WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT_UNREACHABLE("Should never call Dispatch on this!");
+  }
+
+  virtual bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    // Do nothing.
+    return true;
+  }
+};
+
 PRThread*
 PRThreadFromThread(nsIThread* aThread)
 {
   MOZ_ASSERT(aThread);
 
   PRThread* result;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->GetPRThread(&result)));
   MOZ_ASSERT(result);
@@ -1726,16 +1829,39 @@ PRThreadFromThread(nsIThread* aThread)
 
 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
 
 template <class Derived>
+WorkerPrivateParent<Derived>::
+LoadInfo::LoadInfo()
+  : mWindowID(UINT64_MAX)
+  , mFromWindow(false)
+  , mEvalAllowed(false)
+  , mReportCSPViolations(false)
+  , mXHRParamsAllowed(false)
+  , mPrincipalIsSystem(false)
+  , mIsInPrivilegedApp(false)
+  , mIsInCertifiedApp(false)
+  , mIndexedDBAllowed(false)
+{
+  MOZ_COUNT_CTOR(WorkerPrivateParent<Derived>::LoadInfo);
+}
+
+template <class Derived>
+WorkerPrivateParent<Derived>::
+LoadInfo::~LoadInfo()
+{
+  MOZ_COUNT_DTOR(WorkerPrivateParent<Derived>::LoadInfo);
+}
+
+template <class Derived>
 class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL
   : public nsRunnable
 {
   friend class nsRevocableEventPtr<SynchronizeAndResumeRunnable>;
 
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsPIDOMWindow> mWindow;
 
@@ -2004,16 +2130,32 @@ NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryR
 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
 : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
 #ifdef DEBUG
   , mHasRun(false)
 #endif
 {
 }
 
+struct WorkerPrivate::PreemptingRunnableInfo MOZ_FINAL
+{
+  nsCOMPtr<nsIRunnable> mRunnable;
+  uint32_t mRecursionDepth;
+
+  PreemptingRunnableInfo()
+  {
+    MOZ_COUNT_CTOR(WorkerPrivate::PreemptingRunnableInfo);
+  }
+
+  ~PreemptingRunnableInfo()
+  {
+    MOZ_COUNT_DTOR(WorkerPrivate::PreemptingRunnableInfo);
+  }
+};
+
 // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
 // templates.
 template <class Derived>
 typename WorkerPrivateParent<Derived>::cycleCollection
   WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
     WorkerPrivateParent<Derived>::cycleCollection();
 
 template <class Derived>
@@ -2802,26 +2944,16 @@ WorkerPrivateParent<Derived>::DispatchMe
     xpc::Throw(cx, rv);
     return false;
   }
 
   return true;
 }
 
 template <class Derived>
-uint64_t
-WorkerPrivateParent<Derived>::GetInnerWindowId()
-{
-  AssertIsOnMainThread();
-  NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(),
-               "Outer window?");
-  return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0;
-}
-
-template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateRuntimeOptions(
                                     JSContext* aCx,
                                     const JS::RuntimeOptions& aRuntimeOptions)
 {
   AssertIsOnParentThread();
 
   {
@@ -3362,26 +3494,32 @@ WorkerPrivateParent<Derived>::SetBaseURI
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal,
                                            nsILoadGroup* aLoadGroup)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
+  MOZ_ASSERT(!mLoadInfo.mPrincipalInfo);
 
   mLoadInfo.mPrincipal = aPrincipal;
   mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
   uint16_t appStatus = aPrincipal->GetAppStatus();
   mLoadInfo.mIsInPrivilegedApp =
     (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
      appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
   mLoadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
 
   mLoadInfo.mLoadGroup = aLoadGroup;
+
+  mLoadInfo.mPrincipalInfo = new PrincipalInfo();
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo)));
 }
 
 template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
 {
   AssertIsOnParentThread();
 
@@ -3903,16 +4041,17 @@ WorkerPrivate::Constructor(JSContext* aC
 
 // static
 nsresult
 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
                            WorkerPrivate* aParent, const nsAString& aScriptURL,
                            bool aIsChromeWorker, LoadInfo* aLoadInfo)
 {
   using namespace mozilla::dom::workers::scriptloader;
+  using mozilla::dom::indexedDB::IDBFactory;
 
   MOZ_ASSERT(aCx);
   MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
 
   if (aWindow) {
     AssertIsOnMainThread();
   }
 
@@ -3953,16 +4092,19 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
       if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
           NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) {
         NS_WARNING("Failed to proxy release of channel, leaking instead!");
       }
       return NS_ERROR_FAILURE;
     }
 
     loadInfo.mDomain = aParent->Domain();
+    loadInfo.mFromWindow = aParent->IsFromWindow();
+    loadInfo.mWindowID = aParent->WindowID();
+    loadInfo.mIndexedDBAllowed = aParent->IsIndexedDBAllowed();
   } else {
     AssertIsOnMainThread();
 
     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
     MOZ_ASSERT(ssm);
 
     bool isChrome = nsContentUtils::IsCallerChrome();
 
@@ -4066,16 +4208,19 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
 
       loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
 
       uint16_t appStatus = loadInfo.mPrincipal->GetAppStatus();
       loadInfo.mIsInPrivilegedApp =
         (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
          appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
       loadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
+      loadInfo.mFromWindow = true;
+      loadInfo.mWindowID = globalWindow->WindowID();
+      loadInfo.mIndexedDBAllowed = IDBFactory::AllowedForWindow(globalWindow);
     } else {
       // Not a window
       MOZ_ASSERT(isChrome);
 
       // We're being created outside of a window. Need to figure out the script
       // that is creating us in order for us to use relative URIs later on.
       JS::AutoFilename fileName;
       if (JS::DescribeScriptedCaller(aCx, &fileName)) {
@@ -4104,16 +4249,19 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
           rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
                          fileName.get());
         }
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
       loadInfo.mXHRParamsAllowed = true;
+      loadInfo.mFromWindow = false;
+      loadInfo.mWindowID = UINT64_MAX;
+      loadInfo.mIndexedDBAllowed = true;
     }
 
     MOZ_ASSERT(loadInfo.mPrincipal);
     MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
 
     if (!nsContentUtils::GetContentSecurityPolicy(getter_AddRefs(loadInfo.mCSP))) {
       NS_WARNING("Failed to get CSP!");
       return NS_ERROR_FAILURE;
@@ -4288,25 +4436,84 @@ WorkerPrivate::OnProcessNextEvent(uint32
   // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
   // However, it's possible that non-worker C++ could spin its own nested event
   // loop, and in that case we must ensure that we continue to process control
   // runnables here.
   if (aRecursionDepth > 1 &&
       mSyncLoopStack.Length() < aRecursionDepth - 1) {
     ProcessAllControlRunnables();
   }
+
+  // Run any preempting runnables that match this depth.
+  if (!mPreemptingRunnableInfos.IsEmpty()) {
+    nsTArray<PreemptingRunnableInfo> pendingRunnableInfos;
+
+    for (uint32_t index = 0;
+         index < mPreemptingRunnableInfos.Length();
+         index++) {
+      PreemptingRunnableInfo& preemptingRunnableInfo =
+        mPreemptingRunnableInfos[index];
+
+      if (preemptingRunnableInfo.mRecursionDepth == aRecursionDepth) {
+        preemptingRunnableInfo.mRunnable->Run();
+        preemptingRunnableInfo.mRunnable = nullptr;
+      } else {
+        PreemptingRunnableInfo* pending = pendingRunnableInfos.AppendElement();
+        pending->mRunnable.swap(preemptingRunnableInfo.mRunnable);
+        pending->mRecursionDepth = preemptingRunnableInfo.mRecursionDepth;
+      }
+    }
+
+    mPreemptingRunnableInfos.SwapElements(pendingRunnableInfos);
+  }
 }
 
 void
 WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth)
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(aRecursionDepth);
 }
 
+bool
+WorkerPrivate::RunBeforeNextEvent(nsIRunnable* aRunnable)
+{
+  AssertIsOnWorkerThread();
+  MOZ_ASSERT(aRunnable);
+  MOZ_ASSERT_IF(!mPreemptingRunnableInfos.IsEmpty(),
+                NS_HasPendingEvents(mThread));
+
+  const uint32_t recursionDepth =
+    mThread->RecursionDepth(WorkerThreadFriendKey());
+
+  PreemptingRunnableInfo* preemptingRunnableInfo =
+    mPreemptingRunnableInfos.AppendElement();
+
+  preemptingRunnableInfo->mRunnable = aRunnable;
+
+  // Due to the weird way that the thread recursion counter is implemented we
+  // subtract one from the recursion level if we have one.
+  preemptingRunnableInfo->mRecursionDepth =
+    recursionDepth ? recursionDepth - 1 : 0;
+
+  // Ensure that we have a pending event so that the runnable will be guaranteed
+  // to run.
+  if (mPreemptingRunnableInfos.Length() == 1 && !NS_HasPendingEvents(mThread)) {
+    nsRefPtr<DummyRunnable> dummyRunnable = new DummyRunnable(this);
+    if (NS_FAILED(Dispatch(dummyRunnable))) {
+      NS_WARNING("RunBeforeNextEvent called after the thread is shutting "
+                 "down!");
+      mPreemptingRunnableInfos.Clear();
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void
 WorkerPrivate::InitializeGCTimers()
 {
   AssertIsOnWorkerThread();
 
   // We need a timer for GC. The basic plan is to run a non-shrinking GC
   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -12,16 +12,17 @@
 #include "nsILoadGroup.h"
 #include "nsIWorkerDebugger.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/CondVar.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "mozilla/dom/StructuredCloneTags.h"
@@ -43,16 +44,19 @@ class nsIURI;
 namespace JS {
 struct RuntimeStats;
 }
 
 namespace mozilla {
 namespace dom {
 class Function;
 }
+namespace ipc {
+class PrincipalInfo;
+}
 }
 
 struct PRThread;
 
 BEGIN_WORKERS_NAMESPACE
 
 class AutoSyncLoopHolder;
 class MessagePort;
@@ -126,16 +130,18 @@ template <class Derived>
 class WorkerPrivateParent : public DOMEventTargetHelper
 {
   class SynchronizeAndResumeRunnable;
 
 protected:
   class EventTarget;
   friend class EventTarget;
 
+  typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
+
 public:
   struct LocationInfo
   {
     nsCString mHref;
     nsCString mProtocol;
     nsCString mHost;
     nsCString mHostname;
     nsCString mPort;
@@ -152,30 +158,32 @@ public:
     nsCOMPtr<nsIURI> mResolvedScriptURI;
     nsCOMPtr<nsIPrincipal> mPrincipal;
     nsCOMPtr<nsIScriptContext> mScriptContext;
     nsCOMPtr<nsPIDOMWindow> mWindow;
     nsCOMPtr<nsIContentSecurityPolicy> mCSP;
     nsCOMPtr<nsIChannel> mChannel;
     nsCOMPtr<nsILoadGroup> mLoadGroup;
 
+    nsAutoPtr<PrincipalInfo> mPrincipalInfo;
     nsCString mDomain;
 
+    uint64_t mWindowID;
+
+    bool mFromWindow;
     bool mEvalAllowed;
     bool mReportCSPViolations;
     bool mXHRParamsAllowed;
     bool mPrincipalIsSystem;
     bool mIsInPrivilegedApp;
     bool mIsInCertifiedApp;
+    bool mIndexedDBAllowed;
 
-    LoadInfo()
-    : mEvalAllowed(false), mReportCSPViolations(false),
-      mXHRParamsAllowed(false), mPrincipalIsSystem(false),
-      mIsInPrivilegedApp(false), mIsInCertifiedApp(false)
-    { }
+    LoadInfo();
+    ~LoadInfo();
 
     void
     StealFrom(LoadInfo& aOther)
     {
       MOZ_ASSERT(!mBaseURI);
       aOther.mBaseURI.swap(mBaseURI);
 
       MOZ_ASSERT(!mResolvedScriptURI);
@@ -194,23 +202,29 @@ public:
       aOther.mCSP.swap(mCSP);
 
       MOZ_ASSERT(!mChannel);
       aOther.mChannel.swap(mChannel);
 
       MOZ_ASSERT(!mLoadGroup);
       aOther.mLoadGroup.swap(mLoadGroup);
 
+      MOZ_ASSERT(!mPrincipalInfo);
+      mPrincipalInfo = aOther.mPrincipalInfo.forget();
+
       mDomain = aOther.mDomain;
+      mWindowID = aOther.mWindowID;
+      mFromWindow = aOther.mFromWindow;
       mEvalAllowed = aOther.mEvalAllowed;
       mReportCSPViolations = aOther.mReportCSPViolations;
       mXHRParamsAllowed = aOther.mXHRParamsAllowed;
       mPrincipalIsSystem = aOther.mPrincipalIsSystem;
       mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
       mIsInCertifiedApp = aOther.mIsInCertifiedApp;
+      mIndexedDBAllowed = aOther.mIndexedDBAllowed;
     }
   };
 
 protected:
   typedef mozilla::ErrorResult ErrorResult;
 
   SharedMutex mMutex;
   mozilla::CondVar mCondVar;
@@ -399,19 +413,16 @@ public:
 
   bool
   DispatchMessageEventToMessagePort(
                                JSContext* aCx,
                                uint64_t aMessagePortSerial,
                                JSAutoStructuredCloneBuffer&& aBuffer,
                                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
 
-  uint64_t
-  GetInnerWindowId();
-
   void
   UpdateRuntimeOptions(JSContext* aCx,
                        const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdateLanguages(JSContext* aCx, const nsTArray<nsString>& aLanguages);
 
   void
@@ -506,16 +517,28 @@ public:
   }
 
   const nsCString&
   Domain() const
   {
     return mLoadInfo.mDomain;
   }
 
+  bool
+  IsFromWindow() const
+  {
+    return mLoadInfo.mFromWindow;
+  }
+
+  uint64_t
+  WindowID() const
+  {
+    return mLoadInfo.mWindowID;
+  }
+
   nsIURI*
   GetBaseURI() const
   {
     AssertIsOnMainThread();
     return mLoadInfo.mBaseURI;
   }
 
   void
@@ -577,16 +600,22 @@ public:
   }
 
   bool
   IsInCertifiedApp() const
   {
     return mLoadInfo.mIsInCertifiedApp;
   }
 
+  const PrincipalInfo&
+  GetPrincipalInfo() const
+  {
+    return *mLoadInfo.mPrincipalInfo;
+  }
+
   already_AddRefed<nsIChannel>
   ForgetWorkerChannel()
   {
     AssertIsOnMainThread();
     return mLoadInfo.mChannel.forget();
   }
 
   nsIDocument*
@@ -708,16 +737,22 @@ public:
 
   uint64_t
   NextMessagePortSerial()
   {
     AssertIsOnMainThread();
     return mMessagePortSerial++;
   }
 
+  bool
+  IsIndexedDBAllowed() const
+  {
+    return mLoadInfo.mIndexedDBAllowed;
+  }
+
   void
   GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers);
 
   void
   CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow);
 
   void
   RegisterHostObjectURI(const nsACString& aURI);
@@ -828,16 +863,19 @@ class WorkerPrivate : public WorkerPriva
 #endif
   };
 
   // This is only modified on the worker thread, but in DEBUG builds
   // AssertValidSyncLoop function iterates it on other threads. Therefore
   // modifications are done with mMutex held *only* in DEBUG builds.
   nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
 
+  struct PreemptingRunnableInfo;
+  nsTArray<PreemptingRunnableInfo> mPreemptingRunnableInfos;
+
   nsCOMPtr<nsITimer> mTimer;
 
   nsCOMPtr<nsITimer> mGCTimer;
   nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
   nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
 
   nsRefPtr<MemoryReporter> mMemoryReporter;
 
@@ -1162,16 +1200,21 @@ public:
   // Only valid after CompileScriptRunnable has finished running!
   bool
   WorkerScriptExecutedSuccessfully() const
   {
     AssertIsOnWorkerThread();
     return mWorkerScriptExecutedSuccessfully;
   }
 
+  // Just like nsIAppShell::RunBeforeNextEvent. May only be called on the worker
+  // thread.
+  bool
+  RunBeforeNextEvent(nsIRunnable* aRunnable);
+
 private:
   WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsACString& aSharedWorkerName,
                 LoadInfo& aLoadInfo);
 
   void
   ClearMainEventQueue(WorkerRanOrNot aRanOrNot);
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -3,23 +3,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerScope.h"
 
 #include "jsapi.h"
 #include "mozilla/EventListenerManager.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/Services.h"
 #include "nsServiceManagerUtils.h"
 
 #include "nsIDocument.h"
 #include "nsIServiceWorkerManager.h"
 
 #ifdef ANDROID
 #include <android/log.h>
@@ -38,16 +40,19 @@
 #define UNWRAP_WORKER_OBJECT(Interface, obj, value)                           \
   UnwrapObject<prototypes::id::Interface##_workers,                           \
     mozilla::dom::Interface##Binding_workers::NativeType>(obj, value)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
+using mozilla::dom::indexedDB::IDBFactory;
+using mozilla::ipc::PrincipalInfo;
+
 BEGIN_WORKERS_NAMESPACE
 
 WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
 : mWorkerPrivate(aWorkerPrivate)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
@@ -60,25 +65,27 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlo
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope,
                                                   DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope,
                                                 DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScope,
                                                DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
 
   tmp->mWorkerPrivate->TraceTimeouts(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -307,16 +314,54 @@ WorkerGlobalScope::GetPerformance()
 
 already_AddRefed<Promise>
 WorkerGlobalScope::Fetch(const RequestOrUSVString& aInput,
                          const RequestInit& aInit, ErrorResult& aRv)
 {
   return FetchRequest(this, aInput, aInit, aRv);
 }
 
+already_AddRefed<IDBFactory>
+WorkerGlobalScope::GetIndexedDB(ErrorResult& aErrorResult)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  nsRefPtr<IDBFactory> indexedDB = mIndexedDB;
+
+  if (!indexedDB) {
+    if (!mWorkerPrivate->IsIndexedDBAllowed()) {
+      NS_WARNING("IndexedDB is not allowed in this worker!");
+      return nullptr;
+    }
+
+    JSContext* cx = mWorkerPrivate->GetJSContext();
+    MOZ_ASSERT(cx);
+
+    JS::Rooted<JSObject*> owningObject(cx, GetGlobalJSObject());
+    MOZ_ASSERT(owningObject);
+
+    const PrincipalInfo& principalInfo = mWorkerPrivate->GetPrincipalInfo();
+
+    nsresult rv =
+      IDBFactory::CreateForWorker(cx,
+                                  owningObject,
+                                  principalInfo,
+                                  mWorkerPrivate->WindowID(),
+                                  getter_AddRefs(indexedDB));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aErrorResult = rv;
+      return nullptr;
+    }
+
+    mIndexedDB = indexedDB;
+  }
+
+  return indexedDB.forget();
+}
+
 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
 : WorkerGlobalScope(aWorkerPrivate)
 {
 }
 
 JSObject*
 DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx)
 {
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -14,34 +14,43 @@
 namespace mozilla {
 namespace dom {
 
 class Console;
 class Function;
 class Promise;
 class RequestOrUSVString;
 
+namespace indexedDB {
+
+class IDBFactory;
+
+} // namespace indexedDB
+
 } // namespace dom
 } // namespace mozilla
 
 BEGIN_WORKERS_NAMESPACE
 
 class ServiceWorkerClients;
 class WorkerPrivate;
 class WorkerLocation;
 class WorkerNavigator;
 class Performance;
 
 class WorkerGlobalScope : public DOMEventTargetHelper,
                           public nsIGlobalObject
 {
+  typedef mozilla::dom::indexedDB::IDBFactory IDBFactory;
+
   nsRefPtr<Console> mConsole;
   nsRefPtr<WorkerLocation> mLocation;
   nsRefPtr<WorkerNavigator> mNavigator;
   nsRefPtr<Performance> mPerformance;
+  nsRefPtr<IDBFactory> mIndexedDB;
 
 protected:
   WorkerPrivate* mWorkerPrivate;
 
   explicit WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
   virtual ~WorkerGlobalScope();
 
 public:
@@ -122,16 +131,19 @@ public:
 
   void
   Dump(const Optional<nsAString>& aString) const;
 
   Performance* GetPerformance();
 
   already_AddRefed<Promise>
   Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit, ErrorResult& aRv);
+
+  already_AddRefed<IDBFactory>
+  GetIndexedDB(ErrorResult& aErrorResult);
 };
 
 class DedicatedWorkerGlobalScope MOZ_FINAL : public WorkerGlobalScope
 {
   ~DedicatedWorkerGlobalScope() { }
 
 public:
   explicit DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
--- a/dom/workers/WorkerThread.cpp
+++ b/dom/workers/WorkerThread.cpp
@@ -279,16 +279,24 @@ WorkerThread::Dispatch(nsIRunnable* aRun
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+uint32_t
+WorkerThread::RecursionDepth(const WorkerThreadFriendKey& /* aKey */) const
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == mThread);
+
+  return mRunningEvent;
+}
+
 NS_IMPL_ISUPPORTS(WorkerThread::Observer, nsIThreadObserver)
 
 NS_IMETHODIMP
 WorkerThread::Observer::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
 {
   MOZ_CRASH("OnDispatchedEvent() should never be called!");
 }
 
--- a/dom/workers/WorkerThread.h
+++ b/dom/workers/WorkerThread.h
@@ -67,16 +67,19 @@ public:
   nsresult
   DispatchPrimaryRunnable(const WorkerThreadFriendKey& aKey,
                           nsIRunnable* aRunnable);
 
   nsresult
   Dispatch(const WorkerThreadFriendKey& aKey,
            WorkerRunnable* aWorkerRunnable);
 
+  uint32_t
+  RecursionDepth(const WorkerThreadFriendKey& aKey) const;
+
   NS_DECL_ISUPPORTS_INHERITED
 
 private:
   WorkerThread();
   ~WorkerThread();
 
   // This should only be called by consumers that have an
   // nsIEventTarget/nsIThread pointer.
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -89,28 +89,52 @@ var interfaceNamesInGlobalScope =
     "Blob",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DedicatedWorkerGlobalScope",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "DataStore", b2g: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "DataStoreCursor", b2g: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMError",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMException",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMStringList",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "Event",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "File",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileReaderSync",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "Headers", pref: "dom.fetch.enabled" },
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursor",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBDatabase",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBFactory",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBIndex",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBKeyRange",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBObjectStore",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBOpenDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBTransaction",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBVersionChangeEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessagePort",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Performance",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -134,22 +134,32 @@ PrincipalToPrincipalInfo(nsIPrincipal* a
   }
 
   nsCString spec;
   rv = uri->GetSpec(spec);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  uint32_t appId;
-  rv = aPrincipal->GetAppId(&appId);
+  bool isUnknownAppId;
+  rv = aPrincipal->GetUnknownAppId(&isUnknownAppId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  uint32_t appId;
+  if (isUnknownAppId) {
+    appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
+  } else {
+    rv = aPrincipal->GetAppId(&appId);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
   bool isInBrowserElement;
   rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   *aPrincipalInfo = ContentPrincipalInfo(appId, isInBrowserElement, spec);
   return NS_OK;
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -84,13 +84,14 @@ struct MIMEInputStreamParams
   nsCString contentLength;
   bool startedReading;
   bool addContentLength;
 };
 
 union OptionalFileDescriptorSet
 {
   PFileDescriptorSet;
+  FileDescriptor[];
   void_t;
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -29,22 +29,38 @@
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/net/DNS.h"
 #include "SerializedLoadContext.h"
 #include "nsInputStreamPump.h"
 #include "InterceptedChannel.h"
 #include "nsPerformance.h"
 #include "mozIThirdPartyUtil.h"
 
+#ifdef OS_POSIX
+#include "chrome/common/file_descriptor_set_posix.h"
+#endif
+
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
+namespace {
+
+const uint32_t kMaxFileDescriptorsPerMessage = 250;
+
+#ifdef OS_POSIX
+// Keep this in sync with other platforms.
+static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250,
+              "MAX_DESCRIPTORS_PER_MESSAGE mismatch!");
+#endif
+
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelChild
 //-----------------------------------------------------------------------------
 
 HttpChannelChild::HttpChannelChild()
   : HttpAsyncAborter<HttpChannelChild>(MOZ_THIS_IN_INITIALIZER_LIST())
   , mIsFromCache(false)
   , mCacheEntryAvailable(false)
@@ -1478,31 +1494,33 @@ HttpChannelChild::ContinueAsyncOpen()
   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
   openArgs.loadFlags() = mLoadFlags;
   openArgs.requestHeaders() = mClientSetRequestHeaders;
   openArgs.requestMethod() = mRequestHead.Method();
 
   nsTArray<mozilla::ipc::FileDescriptor> fds;
   SerializeInputStream(mUploadStream, openArgs.uploadStream(), fds);
 
-  PFileDescriptorSetChild* fdSet = nullptr;
-  if (!fds.IsEmpty()) {
+  OptionalFileDescriptorSet optionalFDs;
+
+  if (fds.IsEmpty()) {
+    optionalFDs = mozilla::void_t();
+  } else if (fds.Length() <= kMaxFileDescriptorsPerMessage) {
+    optionalFDs = nsTArray<mozilla::ipc::FileDescriptor>();
+    optionalFDs.get_ArrayOfFileDescriptor().SwapElements(fds);
+  } else {
     MOZ_ASSERT(gNeckoChild->Manager());
 
-    fdSet = gNeckoChild->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
+    PFileDescriptorSetChild* fdSet =
+      gNeckoChild->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
     for (uint32_t i = 1; i < fds.Length(); ++i) {
       unused << fdSet->SendAddFileDescriptor(fds[i]);
     }
-  }
 
-  OptionalFileDescriptorSet optionalFDs;
-  if (fdSet) {
     optionalFDs = fdSet;
-  } else {
-    optionalFDs = mozilla::void_t();
   }
 
   nsCOMPtr<mozIThirdPartyUtil> util(do_GetService(THIRDPARTYUTIL_CONTRACTID));
   if (util) {
     bool thirdParty;
     nsresult rv = util->IsThirdPartyChannel(this, nullptr, &thirdParty);
     if (NS_FAILED(rv)) {
       // If we couldn't compute whether this is a third-party load, assume that
@@ -1541,19 +1559,21 @@ HttpChannelChild::ContinueAsyncOpen()
   AddIPDLReference();
 
   PBrowserOrId browser = static_cast<ContentChild*>(gNeckoChild->Manager())
                          ->GetBrowserOrId(tabChild);
   gNeckoChild->SendPHttpChannelConstructor(this, browser,
                                            IPC::SerializedLoadContext(this),
                                            openArgs);
 
-  if (fdSet) {
+  if (optionalFDs.type() ==
+        OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
     FileDescriptorSetChild* fdSetActor =
-      static_cast<FileDescriptorSetChild*>(fdSet);
+      static_cast<FileDescriptorSetChild*>(
+        optionalFDs.get_PFileDescriptorSetChild());
 
     fdSetActor->ForgetFileDescriptors(fds);
   }
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -299,16 +299,19 @@ HttpChannelParent::DoAsyncOpen(  const U
     FileDescriptorSetParent* fdSetActor =
       static_cast<FileDescriptorSetParent*>(aFds.get_PFileDescriptorSetParent());
     MOZ_ASSERT(fdSetActor);
 
     fdSetActor->ForgetFileDescriptors(fds);
     MOZ_ASSERT(!fds.IsEmpty());
 
     unused << fdSetActor->Send__delete__(fdSetActor);
+  } else if (aFds.type() == OptionalFileDescriptorSet::TArrayOfFileDescriptor) {
+    const_cast<OptionalFileDescriptorSet&>(aFds).
+      get_ArrayOfFileDescriptor().SwapElements(fds);
   }
 
   nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds);
   if (stream) {
     mChannel->InternalSetUploadStream(stream);
     mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
deleted file mode 100644
--- a/testing/web-platform/meta/IndexedDB/idb_webworkers.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[idb_webworkers.htm]
-  type: testharness
-  [IndexedDB inside of a WebWorker ]
-    expected: FAIL
-