Bug 701634 - Support IndexedDB in Workers, r=khuey+baku.
authorBen Turner <bent.mozilla@gmail.com>
Tue, 16 Dec 2014 22:26:15 -0800
changeset 220124 9d0ed89e7c5820309c55f12b8a81122298934666
parent 220123 aa82fdbf336298bbaa2a48ee8d0ab6245b635fcd
child 220125 f89cbdabd57cccb2c5b922a2d45a034145c5de4f
push id53013
push userbturner@mozilla.com
push dateWed, 17 Dec 2014 15:11:08 +0000
treeherdermozilla-inbound@9d0ed89e7c58 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs701634
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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
-