Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 17 Dec 2014 20:53:20 -0500
changeset 220234 0e441ff66c5ec4248614573e8a5fab9ad2d1e98a
parent 220179 4d9618982ff076331719f320c12a45a0fbe57803 (current diff)
parent 220233 24c139438218fc4290a54c67ac8c04287bd9a60b (diff)
child 220235 655536bc9ccda4318e311b0c211332b54e886c86
child 220252 b23e9e43fb8adc1c66668f5d5aedb128f71e6fb7
child 220301 ffb2b4550976f598c9e3af3bd910b8de5b87cd3d
push id27980
push userryanvm@gmail.com
push dateThu, 18 Dec 2014 01:53:32 +0000
treeherdermozilla-central@0e441ff66c5e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0a1
first release with
nightly linux32
0e441ff66c5e / 37.0a1 / 20141218030202 / files
nightly linux64
0e441ff66c5e / 37.0a1 / 20141218030202 / files
nightly mac
0e441ff66c5e / 37.0a1 / 20141218030202 / files
nightly win32
0e441ff66c5e / 37.0a1 / 20141218030202 / files
nightly win64
0e441ff66c5e / 37.0a1 / 20141218030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge CLOSED TREE
browser/base/content/content.js
dom/ipc/TabChild.cpp
dom/media/webrtc/MediaEngineTabVideoSource.cpp
ipc/chromium/src/chrome/common/ipc_logging.cc
ipc/chromium/src/chrome/common/ipc_logging.h
security/manager/boot/src/Makefile.in
security/manager/pki/src/Makefile.in
testing/web-platform/meta/IndexedDB/idb_webworkers.htm.ini
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5679,19 +5679,21 @@ function handleLinkClick(event, href, li
       var targetURI = makeURI(href);
       sm.checkSameOriginURI(referrerURI, targetURI, false);
       persistAllowMixedContentInChildTab = true;
     }
     catch (e) { }
   }
 
   urlSecurityCheck(href, doc.nodePrincipal);
-  openLinkIn(href, where, { referrerURI: referrerURI,
-                            charset: doc.characterSet,
-                            allowMixedContent: persistAllowMixedContentInChildTab });
+  let params = { charset: doc.characterSet,
+                 allowMixedContent: persistAllowMixedContentInChildTab };
+  if (!BrowserUtils.linkHasNoReferrer(linkNode))
+    params.referrerURI = referrerURI;
+  openLinkIn(href, where, params);
   event.preventDefault();
   return true;
 }
 
 function middleMousePaste(event) {
   let clipboard = readFromClipboard();
   if (!clipboard)
     return;
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -574,16 +574,17 @@ let ClickEventHandler = {
         json.title = node.getAttribute("title");
         if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
             !event.altKey && !event.metaKey) {
           json.bookmark = node.getAttribute("rel") == "sidebar";
           if (json.bookmark) {
             event.preventDefault(); // Need to prevent the pageload.
           }
         }
+        json.noReferrer = BrowserUtils.linkHasNoReferrer(node)
       }
 
       sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1,15 +1,16 @@
 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
 # 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/.
 
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
+Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 
 var gContextMenuContentData = null;
 
 function nsContextMenu(aXulMenu, aIsShift) {
   this.shouldDisplay = true;
   this.initMenu(aXulMenu, aIsShift);
 }
 
@@ -852,33 +853,38 @@ nsContextMenu.prototype = {
     }
     if (!editable) {
       return false;
     }
     // Otherwise make sure that nothing in the parent chain disables spellchecking
     return aNode.spellcheck;
   },
 
+  _openLinkInParameters : function (doc, extra) {
+    let params = { charset: doc.characterSet };
+    if (!BrowserUtils.linkHasNoReferrer(this.link))
+      params.referrerURI = document.documentURIObject;
+    for (let p in extra)
+      params[p] = extra[p];
+    return params;
+  },
+
   // Open linked-to URL in a new window.
   openLink : function () {
     var doc = this.target.ownerDocument;
     urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
-    openLinkIn(this.linkURL, "window",
-               { charset: doc.characterSet,
-                 referrerURI: doc.documentURIObject });
+    openLinkIn(this.linkURL, "window", this._openLinkInParameters(doc));
   },
 
   // Open linked-to URL in a new private window.
   openLinkInPrivateWindow : function () {
     var doc = this.target.ownerDocument;
     urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
     openLinkIn(this.linkURL, "window",
-               { charset: doc.characterSet,
-                 referrerURI: doc.documentURIObject,
-                 private: true });
+               this._openLinkInParameters(doc, { private: true }));
   },
 
   // Open linked-to URL in a new tab.
   openLinkInTab: function() {
     var doc = this.target.ownerDocument;
     urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
     var referrerURI = doc.documentURIObject;
 
@@ -892,29 +898,27 @@ nsContextMenu.prototype = {
       try {
         var targetURI = this.linkURI;
         sm.checkSameOriginURI(referrerURI, targetURI, false);
         persistAllowMixedContentInChildTab = true;
       }
       catch (e) { }
     }
 
-    openLinkIn(this.linkURL, "tab",
-               { charset: doc.characterSet,
-                 referrerURI: referrerURI,
-                 allowMixedContent: persistAllowMixedContentInChildTab });
+    let params = this._openLinkInParameters(doc, {
+      allowMixedContent: persistAllowMixedContentInChildTab,
+    });
+    openLinkIn(this.linkURL, "tab", params);
   },
 
   // open URL in current tab
   openLinkInCurrent: function() {
     var doc = this.target.ownerDocument;
     urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
-    openLinkIn(this.linkURL, "current",
-               { charset: doc.characterSet,
-                 referrerURI: doc.documentURIObject });
+    openLinkIn(this.linkURL, "current", this._openLinkInParameters(doc));
   },
 
   // Open frame in a new tab.
   openFrameInTab: function() {
     var doc = this.target.ownerDocument;
     var frameURL = doc.location.href;
     var referrer = doc.referrer;
     openLinkIn(frameURL, "tab",
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/code-coverage
@@ -0,0 +1,6 @@
+. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
+
+MOZ_CODE_COVERAGE=1
+export CFLAGS="-fprofile-arcs -ftest-coverage"
+export CXXFLAGS="-fprofile-arcs -ftest-coverage"
+export LDFLAGS="-fprofile-arcs -ftest-coverage -lgcov"
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -1,8 +1,9 @@
+/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
 "use strict";
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
@@ -62,18 +63,20 @@ let ContentClick = {
 
     // This part is based on handleLinkClick.
     var where = window.whereToOpenLink(json);
     if (where == "current")
       return false;
 
     // Todo(903022): code for where == save
 
-    window.openLinkIn(json.href, where, { referrerURI: browser.documentURI,
-                                          charset: browser.characterSet });
+    let params = { charset: browser.characterSet };
+    if (!json.noReferrer)
+      params.referrerURI = browser.documentURI;
+    window.openLinkIn(json.href, where, params);
 
     // Mark the page as a user followed link.  This is done so that history can
     // distinguish automatic embed visits from user activated ones.  For example
     // pages loaded in frames are embed visits and lost with the session, while
     // visits across frames should be preserved.
     try {
       if (!PrivateBrowsingUtils.isWindowPrivate(window))
         PlacesUIUtils.markPageAsFollowedLink(href);
--- a/build/gen_mach_buildprops.py
+++ b/build/gen_mach_buildprops.py
@@ -91,26 +91,31 @@ if __name__ == '__main__':
                         action="store", dest="complete_mar_file",
                         help="Path to the complete MAR file, relative to the objdir.")
     parser.add_argument("--partial-mar-file", required=False,
                         action="store", dest="partial_mar_file",
                         help="Path to the partial MAR file, relative to the objdir.")
     parser.add_argument("--upload-output", required=True,
                         action="store", dest="upload_output",
                         help="Path to the text output of 'make upload'")
+    parser.add_argument("--upload-files", required=True, nargs="+",
+                        action="store", dest="upload_files",
+                        help="List of files to be uploaded.")
     args = parser.parse_args()
 
     json_data = getMarProperties(args.complete_mar_file)
     json_data.update(getUrlProperties(args.upload_output))
     if args.partial_mar_file:
         json_data.update(getMarProperties(args.partial_mar_file, partial=True))
 
         # Pull the previous buildid from the partial mar filename.
         res = re.match(r'.*\.([0-9]+)-[0-9]+.mar', args.partial_mar_file)
         if res:
             json_data['previous_buildid'] = res.group(1)
 
             # Set partialInfo to be a collection of the partial mar properties
             # useful for balrog.
             json_data['partialInfo'] = getPartialInfo(json_data)
 
+    json_data['uploadFiles'] = args.upload_files
+
     with open('mach_build_properties.json', 'w') as outfile:
         json.dump(json_data, outfile, indent=4)
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -7,16 +7,20 @@ ifneq (,$(filter automation/%,$(MAKECMDG
 ifeq (4.0,$(firstword $(sort 4.0 $(MAKE_VERSION))))
 MAKEFLAGS += --output-sync=target
 else
 .NOTPARALLEL:
 endif
 endif
 
 include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
+include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk
+
+# Clear out DIST_FILES if it was set by upload-files.mk (for Android builds)
+DIST_FILES =
 
 # Log file from the 'make upload' step. We need this to parse out the URLs of
 # the uploaded files.
 AUTOMATION_UPLOAD_OUTPUT = $(DIST)/automation-upload.txt
 
 # Helper variables to convert from MOZ_AUTOMATION_* variables to the
 # corresponding the make target
 tier_BUILD_SYMBOLS = buildsymbols
@@ -81,17 +85,17 @@ automation/pretty-package: automation/bu
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
 automation/build: $(addprefix automation/,$(MOZ_AUTOMATION_TIERS))
-	$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) --upload-output $(AUTOMATION_UPLOAD_OUTPUT)
+	$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) --upload-output $(AUTOMATION_UPLOAD_OUTPUT) --upload-files $(abspath $(UPLOAD_FILES))
 
 # We need the log from make upload to grep it for urls in order to set
 # properties.
 AUTOMATION_EXTRA_CMDLINE-upload = 2>&1 | tee $(AUTOMATION_UPLOAD_OUTPUT)
 
 # Note: We have to force -j1 here, at least until bug 1036563 is fixed.
 AUTOMATION_EXTRA_CMDLINE-l10n-check = -j1
 AUTOMATION_EXTRA_CMDLINE-pretty-l10n-check = -j1
--- a/configure.in
+++ b/configure.in
@@ -489,22 +489,17 @@ case "$target" in
 
         if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
 
         AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
         AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
 
-        if test "$_CC_MAJOR_VERSION" = "16"; then
-            _CC_SUITE=10
-            MSVS_VERSION=2010
-            MSVC_C_RUNTIME_DLL=msvcr100.dll
-            MSVC_CXX_RUNTIME_DLL=msvcp100.dll
-        elif test "$_CC_MAJOR_VERSION" = "17"; then
+        if test "$_CC_MAJOR_VERSION" = "17"; then
             _CC_SUITE=11
             MSVS_VERSION=2012
             MSVC_C_RUNTIME_DLL=msvcr110.dll
             MSVC_CXX_RUNTIME_DLL=msvcp110.dll
         elif test "$_CC_MAJOR_VERSION" = "18"; then
             _CC_SUITE=12
             MSVS_VERSION=2013
             MSVC_C_RUNTIME_DLL=msvcr120.dll
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -299,25 +299,25 @@ FindJSContext(nsIGlobalObject* aGlobalOb
     cx = nsContentUtils::GetSafeJSContext();
   }
   return cx;
 }
 
 AutoJSAPI::AutoJSAPI()
   : mCx(nullptr)
   , mOwnErrorReporting(false)
-  , mOldDontReportUncaught(false)
+  , mOldAutoJSAPIOwnsErrorReporting(false)
 {
 }
 
 AutoJSAPI::~AutoJSAPI()
 {
   if (mOwnErrorReporting) {
     MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()");
-    JS::ContextOptionsRef(cx()).setDontReportUncaught(mOldDontReportUncaught);
+    JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(mOldAutoJSAPIOwnsErrorReporting);
 
     if (HasException()) {
 
       // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null
       // compartment when the destructor is called. However, the JS engine
       // requires us to be in a compartment when we fetch the pending exception.
       // In this case, we enter the privileged junk scope and don't dispatch any
       // error events.
@@ -372,17 +372,17 @@ AutoJSAPI::InitInternal(JSObject* aGloba
     JS_SetErrorReporter(rt, xpc::SystemErrorReporter);
   }
 }
 
 AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
                      bool aIsMainThread,
                      JSContext* aCx)
   : mOwnErrorReporting(false)
-  , mOldDontReportUncaught(false)
+  , mOldAutoJSAPIOwnsErrorReporting(false)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
   MOZ_ASSERT(aCx);
   MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread());
 
   InitInternal(aGlobalObject->GetGlobalJSObject(), aCx, aIsMainThread);
 }
@@ -467,21 +467,22 @@ AutoJSAPI::InitWithLegacyErrorReporting(
 }
 
 bool
 AutoJSAPI::InitWithLegacyErrorReporting(nsGlobalWindow* aWindow)
 {
   return InitWithLegacyErrorReporting(static_cast<nsIGlobalObject*>(aWindow));
 }
 
-// Even with dontReportUncaught, the JS engine still sends warning reports
-// to the JSErrorReporter as soon as they are generated. These go directly to
-// the console, so we can handle them easily here.
+// Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
+// reports to the JSErrorReporter as soon as they are generated. These go
+// directly to the console, so we can handle them easily here.
 //
-// Eventually, SpiderMonkey will have a special-purpose callback for warnings only.
+// Eventually, SpiderMonkey will have a special-purpose callback for warnings
+// only.
 void
 WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep)
 {
   MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags));
   nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
   nsPIDOMWindow* win = xpc::WindowGlobalOrNull(JS::CurrentGlobalOrNull(aCx));
   xpcReport->Init(aRep, aMessage, nsContentUtils::IsCallerChrome(),
                   win ? win->WindowID() : 0);
@@ -491,18 +492,18 @@ WarningOnlyErrorReporter(JSContext* aCx,
 void
 AutoJSAPI::TakeOwnershipOfErrorReporting()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Can't own error reporting off-main-thread yet");
   MOZ_ASSERT(!mOwnErrorReporting);
   mOwnErrorReporting = true;
 
   JSRuntime *rt = JS_GetRuntime(cx());
-  mOldDontReportUncaught = JS::ContextOptionsRef(cx()).dontReportUncaught();
-  JS::ContextOptionsRef(cx()).setDontReportUncaught(true);
+  mOldAutoJSAPIOwnsErrorReporting = JS::ContextOptionsRef(cx()).autoJSAPIOwnsErrorReporting();
+  JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(true);
   JS_SetErrorReporter(rt, WarningOnlyErrorReporter);
 }
 
 bool
 AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
 {
     MOZ_ASSERT(CxPusherIsStackTop());
     MOZ_ASSERT(HasException());
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -302,17 +302,17 @@ protected:
 
 private:
   mozilla::Maybe<danger::AutoCxPusher> mCxPusher;
   mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
   JSContext *mCx;
 
   // Track state between the old and new error reporting modes.
   bool mOwnErrorReporting;
-  bool mOldDontReportUncaught;
+  bool mOldAutoJSAPIOwnsErrorReporting;
   Maybe<JSErrorReporter> mOldErrorReporter;
 
   void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread);
 
   AutoJSAPI(const AutoJSAPI&) MOZ_DELETE;
   AutoJSAPI& operator= (const AutoJSAPI&) MOZ_DELETE;
 };
 
--- 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();
@@ -2980,16 +2982,43 @@ nsDocument::InitCSP(nsIChannel* aChannel
       PR_LOG(gCspPRLog, PR_LOG_DEBUG,
               ("CSP doesn't like frame's ancestry, not loading."));
 #endif
       // stop!  ERROR page!
       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
     }
   }
 
+  // ----- Set up any Referrer Policy specified by CSP
+  bool hasReferrerPolicy = false;
+  uint32_t referrerPolicy = mozilla::net::RP_Default;
+  rv = csp->GetReferrerPolicy(&referrerPolicy, &hasReferrerPolicy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (hasReferrerPolicy) {
+    // Referrer policy spec (section 6.1) says that once the referrer policy
+    // is set, any future attempts to change it result in No-Referrer.
+    if (!mReferrerPolicySet) {
+      mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
+      mReferrerPolicySet = true;
+    } else if (mReferrerPolicy != referrerPolicy) {
+      mReferrerPolicy = mozilla::net::RP_No_Referrer;
+#ifdef PR_LOGGING
+      {
+        PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s",
+                "CSP wants to set referrer, but nsDocument"
+                "already has it set. No referrers will be sent"));
+      }
+#endif
+    }
+
+    // Referrer Policy is set separately for the speculative parser in
+    // nsHTMLDocument::StartDocumentLoad() so there's nothing to do here for
+    // speculative loads.
+  }
+
   rv = principal->SetCsp(csp);
   NS_ENSURE_SUCCESS(rv, rv);
 #ifdef PR_LOGGING
   PR_LOG(gCspPRLog, PR_LOG_DEBUG,
          ("Inserted CSP into principal %p", principal));
 #endif
 
   return NS_OK;
--- 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)
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_csp_referrerdirective.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Subframe test for bug 965727</title>
+
+<script type="text/javascript">
+// we can get the ID out of the querystring.
+var args = document.location.search.substring(1).split('&');
+var id = "unknown";
+for (var i=0; i < args.length; i++) {
+  var arg = unescape(args[i]);
+  if (arg.indexOf('=') > 0 && arg.indexOf('id') == 0) {
+    id = arg.split('=')[1].trim();
+  }
+}
+
+var results = {
+  'id': id,
+  'referrer': document.location.href,
+  'results': {
+    'sameorigin': false,
+    'crossorigin': false,
+    'downgrade': false
+  }
+};
+
+// this is called back by each script load.
+var postResult = function(loadType, referrerLevel, referrer) {
+  results.results[loadType] = referrerLevel;
+
+  // and then check if all three have loaded.
+  for (var id in results.results) {
+    if (!results.results[id]) {
+      return;
+    }
+  }
+  //finished if we don't return early
+  window.parent.postMessage(JSON.stringify(results), "*");
+  console.log(JSON.stringify(results));
+}
+
+</script>
+</head>
+<body>
+Testing ...
+
+<script src="https://example.com/tests/dom/base/test/csp/referrerdirective.sjs?type=sameorigin&"
+        onerror="postResult('sameorigin', 'error');"></script>
+<script src="https://test2.example.com/tests/dom/base/test/csp/referrerdirective.sjs?type=crossorigin&"
+        onerror="postResult('crossorigin', 'error');"></script>
+<script src="http://example.com/tests/dom/base/test/csp/referrerdirective.sjs?type=downgrade&"
+        onerror="postResult('downgrade', 'error');"></script>
+
+</body>
+</html>
--- a/dom/base/test/csp/mochitest.ini
+++ b/dom/base/test/csp/mochitest.ini
@@ -98,16 +98,18 @@ support-files =
   file_leading_wildcard.html
   file_multi_policy_injection_bypass.html
   file_multi_policy_injection_bypass.html^headers^
   file_multi_policy_injection_bypass_2.html
   file_multi_policy_injection_bypass_2.html^headers^
   file_form-action.html
   file_worker_redirect.html
   file_worker_redirect.sjs
+  file_csp_referrerdirective.html
+  referrerdirective.sjs
 
 [test_base-uri.html]
 [test_connect-src.html]
 [test_CSP.html]
 [test_csp_allow_https_schemes.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_CSP_bug663567.html]
 [test_CSP_bug802872.html]
@@ -144,9 +146,11 @@ skip-if = buildapp == 'b2g' # intermitte
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_303_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_307_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_subframe_run_js_if_allowed.html]
 [test_leading_wildcard.html]
 [test_multi_policy_injection_bypass.html]
+[test_CSP_referrerdirective.html]
+skip-if = buildapp == 'b2g' #no ssl support
 [test_worker_redirect.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/referrerdirective.sjs
@@ -0,0 +1,36 @@
+// Used for bug 965727 to serve up really simple scripts reflecting the
+// referrer sent to load this back to the loader.
+
+
+function handleRequest(request, response) {
+  // skip speculative loads.
+
+  var splits = request.queryString.split('&');
+  var params = {};
+  splits.forEach(function(v) {
+    let parts = v.split('=');
+    params[parts[0]] = unescape(parts[1]);
+  });
+
+  var loadType = params['type'];
+  var referrerLevel = 'error';
+
+  if (request.hasHeader('Referer')) {
+    var referrer = request.getHeader('Referer');
+    if (referrer.indexOf("file_csp_testserver.sjs") > -1) {
+      referrerLevel = "full";
+    } else {
+      referrerLevel = "origin";
+    }
+  } else {
+    referrerLevel = 'none';
+  }
+
+  var theScript = 'window.postResult("' + loadType + '", "' + referrerLevel + '");';
+  response.setHeader('Content-Type', 'application/javascript; charset=utf-8', false);
+  response.setHeader('Cache-Control', 'no-cache', false);
+
+  if (request.method != "OPTIONS") {
+  response.write(theScript);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/test_CSP_referrerdirective.html
@@ -0,0 +1,125 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=965727
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Content Security Policy referrer Directive (Bug 965727)</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/*
+ * This tests various referrer policies and the referrer-sending behavior when
+ * requesting scripts in different ways:
+ *  - cross-origin (https://example.com -> https://test2.example.com)
+ *  - same-origin (https://example.com -> https://example.com)
+ *  - downgrade (https://example.com -> http://example.com)
+ *
+ * Each test creates an iframe that loads scripts for each of the checks.  If
+ * the scripts are blocked, the test fails (they should run).  When loaded,
+ * each script updates a results object in the test page, and then when the
+ * test page has finished loading all the scripts, it postMessages back to this
+ * page.  Once all tests are done, the results are checked.
+ */
+
+var testData = {
+  'default': { 'csp': "script-src * 'unsafe-inline'; referrer default",
+          'expected': {  'sameorigin': 'full',
+                        'crossorigin': 'full',
+                          'downgrade': 'none' }},
+
+  'origin':  { 'csp': "script-src * 'unsafe-inline'; referrer origin",
+          'expected': {  'sameorigin': 'origin',
+                        'crossorigin': 'origin',
+                          'downgrade': 'origin' }},
+
+  'origin-when-crossorigin':  { 'csp': "script-src * 'unsafe-inline'; referrer origin-when-crossorigin",
+          'expected': {  'sameorigin': 'full',
+                        'crossorigin': 'origin',
+                          'downgrade': 'none' }},
+
+  'unsafe-url':  { 'csp': "script-src * 'unsafe-inline'; referrer unsafe-url",
+          'expected': {  'sameorigin': 'full',
+                        'crossorigin': 'full',
+                          'downgrade': 'full' }},
+
+  'none':   { 'csp': "script-src * 'unsafe-inline'; referrer no-referrer",
+          'expected': {  'sameorigin': 'none',
+                        'crossorigin': 'none',
+                          'downgrade': 'none' }},
+  };
+
+
+var referrerDirectiveTests = {
+  // called via postMessage when one of the iframes is done running.
+  onIframeComplete: function(event) {
+    try {
+      var results = JSON.parse(event.data);
+      ok(results.hasOwnProperty('id'), "'id' property required in posted message " + event.data);
+
+      ok(testData.hasOwnProperty(results['id']), "Test " + results['id'] + " must be expected.");
+
+      // check all the various load types' referrers.
+      var expected = testData[results['id']].expected;
+      for (var t in expected) {
+        is(results.results[t], expected[t],
+          " referrer must match expected for " + t + " in " + results['id']);
+      }
+      testData[results['id']]['complete'] = true;
+
+    } catch(e) {
+      // fail -- should always be JSON
+      ok(false, "failed to parse posted message + " + event.data);
+      // have to end as well since not all messages were valid.
+      SimpleTest.finish();
+    }
+
+    referrerDirectiveTests.checkForCompletion();
+  },
+
+  // checks to see if all the parallel tests are done and validates results.
+  checkForCompletion: function() {
+    for (var id in testData) {
+      if (!testData[id].hasOwnProperty('complete')) {
+        return;
+      }
+    }
+    SimpleTest.finish();
+  }
+};
+
+SimpleTest.waitForExplicitFinish();
+// have to disable mixed content blocking to test https->http referrers.
+SpecialPowers.pushPrefEnv({
+    'set': [['security.mixed_content.block_active_content',   false],
+            ['security.mixed_content.block_display_content',  false]]
+    },
+    function() {
+      // each of the iframes we create will call us back when its contents are loaded.
+      window.addEventListener("message", referrerDirectiveTests.onIframeComplete.bind(window), false);
+
+      // one iframe created for each test case
+      for (var id in testData) {
+        var elt = document.createElement("iframe");
+        elt.src = "https://example.com/tests/dom/base/test/csp/file_csp_testserver.sjs?" +
+                  "id=" + id +
+                  "&csp=" + escape(testData[id]['csp']) +
+                  "&file=tests/dom/base/test/csp/file_csp_referrerdirective.html";
+        document.getElementById("content").appendChild(elt);
+      }
+    });
+</script>
+</pre>
+</body>
+</html>
copy from dom/datastore/tests/file_basic_worker.html
copy to dom/datastore/tests/file_worker_close.html
--- a/dom/datastore/tests/file_basic_worker.html
+++ b/dom/datastore/tests/file_worker_close.html
@@ -1,26 +1,25 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Test for DataStore - basic operation on a readonly db</title>
+  <title>Test for DataStore</title>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
   var messages = [];
-  var worker = new Worker("file_basic_worker.js");
+  var worker = new Worker("file_worker_close.js");
 
   worker.onmessage = function(event) {
     messages.push(event.data)
 
     if (event.data == 'DONE') {
-      // Free the worker when all the tests are done.
       worker.terminate();
 
       // Fire message to the test_basic_worker.html.
       for (var i = 0; i < messages.length; i++) {
         alert(messages[i]);
       }
     }
   }
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_worker_close.js
@@ -0,0 +1,18 @@
+function is(a, b, msg) {
+  postMessage((a == b ? 'OK' : 'KO')+ ' ' + msg)
+}
+
+var store;
+navigator.getDataStores('foo').then(function(stores) {
+  is(stores.length, 1, "getDataStores('foo') returns 1 element");
+  is(stores[0].name, 'foo', 'The dataStore.name is foo');
+  is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
+  store = stores[0];
+  postMessage('DONE');
+});
+
+onclose = function() {
+  for (var i = 0; i < 100; ++i) {
+    store.get(123);
+  }
+}
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -27,16 +27,18 @@ support-files =
   file_event_maker.html
   file_event_receiver.html
   file_transactions.html
   file_basic_common.js
   file_sync_common.js
   file_bug1008044.html
   file_bug957086.html
   file_notify_system_message.html
+  file_worker_close.html
+  file_worker_close.js
 
 [test_app_install.html]
 skip-if = toolkit == 'gonk' # embed-apps doesn't work in the mochitest app
 [test_readonly.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 [test_basic.html]
 [test_basic_worker.html]
 [test_changes.html]
@@ -54,8 +56,9 @@ skip-if = (toolkit == 'android' && proce
 [test_bug986056.html]
 [test_oop_events.html]
 [test_transactions.html]
 [test_bug1008044.html]
 [test_bug957086.html]
 [test_bug1058108.html]
 [test_notify_system_message.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || toolkit == 'win' #bug 1053662 - Timeout prone
+[test_worker_close.html]
copy from dom/datastore/tests/test_basic_worker.html
copy to dom/datastore/tests/test_worker_close.html
--- a/dom/datastore/tests/test_basic_worker.html
+++ b/dom/datastore/tests/test_worker_close.html
@@ -1,21 +1,21 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Test for DataStore - basic operation on a readonly db</title>
+  <title>Test for DataStore - Worker.onclose</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
-  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_basic_worker.html';
+  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_worker_close.html';
   var gApp;
 
   function cbError() {
     ok(false, "Error callback invoked");
     finish();
   }
 
   function installApp() {
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -209,18 +209,18 @@ public:
     promise->MaybeResolve(response);
 
     mResolver->mPromiseProxy->CleanUp(aCx);
     return true;
   }
 };
 
 WorkerFetchResolver::WorkerFetchResolver(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
-  : mPromiseProxy(new PromiseWorkerProxy(aWorkerPrivate, aPromise))
 {
+  mPromiseProxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
 }
 
 WorkerFetchResolver::~WorkerFetchResolver()
 {
 }
 
 void
 WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
--- 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/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -668,20 +668,25 @@ nsHTMLDocument::StartDocumentLoad(const 
 
   // These are the charset source and charset for the parser.  This can differ
   // from that for the document if the channel is a wyciwyg channel.
   int32_t parserCharsetSource;
   nsAutoCString parserCharset;
 
   nsCOMPtr<nsIWyciwygChannel> wyciwygChannel;
   
-  // For error reporting
+  // For error reporting and referrer policy setting
   nsHtml5TreeOpExecutor* executor = nullptr;
   if (loadAsHtml5) {
     executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
+    if (mReferrerPolicySet) {
+      // CSP may have set the referrer policy, so a speculative parser should
+      // start with the new referrer policy.
+      executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
+    }
   }
 
   if (!IsHTML() || !docShell) { // no docshell for text/html XHR
     charsetSource = IsHTML() ? kCharsetFromFallback
                              : kCharsetFromDocTypeDefault;
     charset.AssignLiteral("UTF-8");
     TryChannelCharset(aChannel, charsetSource, charset, executor);
     parserCharsetSource = charsetSource;
@@ -1636,16 +1641,25 @@ nsHTMLDocument::Open(JSContext* cx,
 
   // Store the security info of the caller now that we're done
   // resetting the document.
   mSecurityInfo = securityInfo;
 
   mParserAborted = false;
   mParser = nsHtml5Module::NewHtml5Parser();
   nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
+  if (mReferrerPolicySet) {
+    // CSP may have set the referrer policy, so a speculative parser should
+    // start with the new referrer policy.
+    nsHtml5TreeOpExecutor* executor = nullptr;
+    executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
+    if (executor && mReferrerPolicySet) {
+      executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
+    }
+  }
 
   // This will be propagated to the parser when someone actually calls write()
   SetContentTypeInternal(contentType);
 
   // Prepare the docshell and the document viewer for the impending
   // out of band document.write()
   shell->PrepareForNewContentModel();
 
--- 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_remove_index.js
+++ b/dom/indexedDB/test/unit/test_remove_index.js
@@ -45,11 +45,14 @@ function testSteps()
 
   let index2 = objectStore.createIndex(indexName, "foo");
   isnot(index, index2, "New instance should be created");
 
   is(objectStore.indexNames.length, 1, "Correct recreacted indexNames list");
   is(objectStore.indexNames.item(0), indexName, "Correct recreacted name");
   is(objectStore.index(indexName), index2, "Correct instance");
 
+  event.target.transaction.oncomplete = grabEventAndContinueHandler;
+  event = yield undefined;
+
   finishTest();
   yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_remove_objectStore.js
+++ b/dom/indexedDB/test/unit/test_remove_objectStore.js
@@ -32,16 +32,19 @@ function testSteps()
       }
     }
   }
   yield undefined;
 
   is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
   is(db.objectStoreNames.item(0), objectStoreName, "Correct name");
 
+  event.target.transaction.oncomplete = grabEventAndContinueHandler;
+  event = yield undefined;
+
   db.close();
 
   request = indexedDB.open(name, 2);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   event = yield undefined;
 
   db = event.target.result;
@@ -76,30 +79,37 @@ function testSteps()
   event = yield undefined;
 
   db.deleteObjectStore(objectStore.name);
   is(db.objectStoreNames.length, 0, "Correct objectStores list");
 
   continueToNextStep();
   yield undefined;
 
+  trans.oncomplete = grabEventAndContinueHandler;
+  event = yield undefined;
+
   db.close();
 
   request = indexedDB.open(name, 3);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   event = yield undefined;
 
   db = event.target.result;
+  trans = event.target.transaction;
 
   objectStore = db.createObjectStore(objectStoreName, { keyPath: "foo" });
 
   request = objectStore.add({foo:"bar"});
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   db.deleteObjectStore(objectStoreName);
 
   event = yield undefined;
 
+  trans.oncomplete = grabEventAndContinueHandler;
+  event = yield undefined;
+
   finishTest();
   yield undefined;
 }
--- 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/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -15,17 +15,17 @@ interface nsIURI;
  * nsIContentSecurityPolicy
  * Describes an XPCOM component used to model and enforce CSPs.  Instances of
  * this class may have multiple policies within them, but there should only be
  * one of these per document/principal.
  */
 
 typedef unsigned short CSPDirective;
 
-[scriptable, uuid(69b7663e-117a-4a3b-81bd-d86420b7c79e)]
+[scriptable, uuid(68434447-b816-4473-a731-efc4f6d59902)]
 interface nsIContentSecurityPolicy : nsISerializable
 {
   /**
    * Directives supported by Content Security Policy.  These are enums for
    * the CSPDirective type.
    * The NO_DIRECTIVE entry is  used for checking default permissions and
    * returning failure when asking CSP which directive to check.
    *
@@ -42,30 +42,46 @@ interface nsIContentSecurityPolicy : nsI
   const unsigned short FRAME_SRC_DIRECTIVE        = 7;
   const unsigned short FONT_SRC_DIRECTIVE         = 8;
   const unsigned short CONNECT_SRC_DIRECTIVE      = 9;
   const unsigned short REPORT_URI_DIRECTIVE       = 10;
   const unsigned short FRAME_ANCESTORS_DIRECTIVE  = 11;
   const unsigned short REFLECTED_XSS_DIRECTIVE    = 12;
   const unsigned short BASE_URI_DIRECTIVE         = 13;
   const unsigned short FORM_ACTION_DIRECTIVE      = 14;
+  const unsigned short REFERRER_DIRECTIVE         = 15;
 
   /**
    * Accessor method for a read-only string version of the policy at a given
    * index.
    */
   AString getPolicy(in unsigned long index);
 
   /**
    * Returns the number of policies attached to this CSP instance.  Useful with
    * getPolicy().
    */
   readonly attribute unsigned long policyCount;
 
   /**
+   * Obtains the referrer policy (as integer) for this browsing context as
+   * specified in CSP.  If there are multiple policies and...
+   *  - only one sets a referrer policy: that policy is returned
+   *  - more than one sets different referrer policies: no-referrer is returned
+   *  - more than one set equivalent policies: that policy is returned
+   * For the enumeration of policies see ReferrerPolicy.h and nsIHttpChannel.
+   *
+   * @param aPolicy
+   *        The referrer policy to use for the protected resource.
+   * @return
+   *        true if a referrer policy is specified, false if it's unspecified.
+   */
+  bool getReferrerPolicy(out unsigned long policy);
+
+  /**
    * Remove a policy associated with this CSP context.
    * @throws NS_ERROR_FAILURE if the index is out of bounds or invalid.
    */
   void removePolicy(in unsigned long index);
 
   /**
    * Parse and install a CSP policy.
    * @param aPolicy
--- 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());
@@ -190,16 +204,84 @@ EventTargetIsOnCurrentThread(nsIEventTar
   }
 
   bool current;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aEventTarget->IsOnCurrentThread(&current)));
 
   return current;
 }
 
+class CancelableRunnableWrapper MOZ_FINAL
+  : public nsCancelableRunnable
+{
+  nsCOMPtr<nsIRunnable> mRunnable;
+#ifdef DEBUG
+  nsCOMPtr<nsIEventTarget> mDEBUGEventTarget;
+#endif
+
+public:
+  CancelableRunnableWrapper(nsIRunnable* aRunnable,
+                            nsIEventTarget* aEventTarget)
+    : mRunnable(aRunnable)
+#ifdef DEBUG
+    , mDEBUGEventTarget(aEventTarget)
+#endif
+  {
+    MOZ_ASSERT(aRunnable);
+    MOZ_ASSERT(aEventTarget);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~CancelableRunnableWrapper()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSICANCELABLERUNNABLE
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(CancelableRunnableWrapper, nsCancelableRunnable)
+
+NS_IMETHODIMP
+CancelableRunnableWrapper::Run()
+{
+  DebugOnly<bool> onTarget;
+  MOZ_ASSERT(mDEBUGEventTarget);
+  MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
+  MOZ_ASSERT(onTarget);
+
+  nsCOMPtr<nsIRunnable> runnable;
+  mRunnable.swap(runnable);
+
+  if (runnable) {
+    return runnable->Run();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CancelableRunnableWrapper::Cancel()
+{
+  DebugOnly<bool> onTarget;
+  MOZ_ASSERT(mDEBUGEventTarget);
+  MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
+  MOZ_ASSERT(onTarget);
+
+  if (NS_WARN_IF(!mRunnable)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  unused << Run();
+  MOZ_ASSERT(!mRunnable);
+
+  return NS_OK;
+}
+
 // Ensure that a nsCOMPtr/nsRefPtr is released on the target thread.
 template <template <class> class SmartPtr, class T>
 void
 ReleaseOnTarget(SmartPtr<T>& aDoomed, nsIEventTarget* aTarget)
 {
   MOZ_ASSERT(aDoomed);
   MOZ_ASSERT(!EventTargetIsOnCurrentThread(aTarget));
 
@@ -208,52 +290,100 @@ ReleaseOnTarget(SmartPtr<T>& aDoomed, ns
 
   auto* doomedSupports = static_cast<nsISupports*>(doomedRaw);
 
   nsCOMPtr<nsIRunnable> releaseRunnable =
     NS_NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release);
   MOZ_ASSERT(releaseRunnable);
 
   if (aTarget) {
+    // If we're targeting a non-main thread then make sure the runnable is
+    // cancelable.
+    releaseRunnable = new CancelableRunnableWrapper(releaseRunnable, aTarget);
+
     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 +467,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 +557,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 +1110,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 +1592,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 +1761,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 +1872,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;
   }
 
@@ -1981,16 +2262,19 @@ RemoteBlobImpl::Destroy()
     delete this;
     return;
   }
 
   nsCOMPtr<nsIRunnable> destroyRunnable =
     NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
 
   if (mActorTarget) {
+    destroyRunnable =
+      new CancelableRunnableWrapper(destroyRunnable, mActorTarget);
+
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
                                                         NS_DISPATCH_NORMAL)));
   } else {
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
   }
 }
 
 NS_IMPL_ADDREF(BlobChild::RemoteBlobImpl)
@@ -2167,19 +2451,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 +2486,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 +2525,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 +2575,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)));
@@ -2355,16 +2658,19 @@ RemoteBlobImpl::Destroy()
     delete this;
     return;
   }
 
   nsCOMPtr<nsIRunnable> destroyRunnable =
     NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
 
   if (mActorTarget) {
+    destroyRunnable =
+      new CancelableRunnableWrapper(destroyRunnable, mActorTarget);
+
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
                                                         NS_DISPATCH_NORMAL)));
   } else {
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
   }
 }
 
 NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl)
@@ -2576,47 +2882,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 +2937,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 +2966,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 +2999,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 +3233,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 +3373,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 +3404,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()
 {
@@ -3198,16 +3515,18 @@ BlobChild::NoteDyingRemoteBlobImpl()
   // This may be called on any thread due to the fact that RemoteBlobImpl is
   // designed to be passed between threads. We must start the shutdown process
   // on the owning thread, so we proxy here if necessary.
   if (!IsOnOwningThread()) {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlobImpl);
 
     if (mEventTarget) {
+      runnable = new CancelableRunnableWrapper(runnable, mEventTarget);
+
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mEventTarget->Dispatch(runnable,
                                                           NS_DISPATCH_NORMAL)));
     } else {
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
     }
 
     return;
   }
@@ -3645,20 +3964,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;
       }
 
@@ -3774,16 +4090,18 @@ BlobParent::NoteDyingRemoteBlobImpl()
   // This may be called on any thread due to the fact that RemoteBlobImpl is
   // designed to be passed between threads. We must start the shutdown process
   // on the main thread, so we proxy here if necessary.
   if (!IsOnOwningThread()) {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlobImpl);
 
     if (mEventTarget) {
+      runnable = new CancelableRunnableWrapper(runnable, mEventTarget);
+
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mEventTarget->Dispatch(runnable,
                                                           NS_DISPATCH_NORMAL)));
     } else {
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
     }
 
     return;
   }
@@ -3866,16 +4184,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 +4231,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 +4268,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 +4351,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 +4558,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/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -240,21 +240,21 @@ TabChildBase::InitializeRootMetrics()
   // Calculate a really simple resolution that we probably won't
   // be keeping, as well as putting the scroll offset back to
   // the top-left of the page.
   mLastRootMetrics.SetViewport(CSSRect(CSSPoint(), kDefaultViewportSize));
   mLastRootMetrics.mCompositionBounds = ParentLayerRect(
       ParentLayerPoint(),
       ParentLayerSize(ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenIsParentLayerForRoot)));
   mLastRootMetrics.SetZoom(mLastRootMetrics.CalculateIntrinsicScale());
-  mLastRootMetrics.mDevPixelsPerCSSPixel = WebWidget()->GetDefaultScale();
+  mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale());
   // We use ParentLayerToLayerScale(1) below in order to turn the
   // async zoom amount into the gecko zoom amount.
   mLastRootMetrics.mCumulativeResolution =
-    mLastRootMetrics.GetZoom() / mLastRootMetrics.mDevPixelsPerCSSPixel * ParentLayerToLayerScale(1);
+    mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1);
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   mLastRootMetrics.mPresShellResolution = mLastRootMetrics.mCumulativeResolution.scale;
   mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0));
 
   TABC_LOG("After InitializeRootMetrics, mLastRootMetrics is %s\n",
     Stringify(mLastRootMetrics).c_str());
 }
@@ -379,17 +379,17 @@ TabChildBase::HandlePossibleViewportChan
   }
 
   FrameMetrics metrics(mLastRootMetrics);
   metrics.SetViewport(CSSRect(CSSPoint(), viewport));
   metrics.mCompositionBounds = ParentLayerRect(
       ParentLayerPoint(),
       ParentLayerSize(ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenIsParentLayerForRoot)));
   metrics.SetRootCompositionSize(
-      ScreenSize(mInnerSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.mDevPixelsPerCSSPixel);
+      ScreenSize(mInnerSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.GetDevPixelsPerCSSPixel());
 
   // This change to the zoom accounts for all types of changes I can conceive:
   // 1. screen size changes, CSS viewport does not (pages with no meta viewport
   //    or a fixed size viewport)
   // 2. screen size changes, CSS viewport also does (pages with a device-width
   //    viewport)
   // 3. screen size remains constant, but CSS viewport changes (meta viewport
   //    tag is added or removed)
@@ -420,23 +420,23 @@ TabChildBase::HandlePossibleViewportChan
                defaultZoom <= viewportInfo.GetMaxZoom());
     metrics.SetZoom(ConvertScaleForRoot(defaultZoom));
 
     metrics.SetScrollId(viewId);
   }
 
   if (nsIPresShell* shell = document->GetShell()) {
     if (nsPresContext* context = shell->GetPresContext()) {
-      metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(
-        (float)nsPresContext::AppUnitsPerCSSPixel() / context->AppUnitsPerDevPixel());
+      metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(
+        (float)nsPresContext::AppUnitsPerCSSPixel() / context->AppUnitsPerDevPixel()));
     }
   }
 
   metrics.mCumulativeResolution = metrics.GetZoom()
-                                / metrics.mDevPixelsPerCSSPixel
+                                / metrics.GetDevPixelsPerCSSPixel()
                                 * ParentLayerToLayerScale(1);
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   metrics.mPresShellResolution = metrics.mCumulativeResolution.scale;
   utils->SetResolution(metrics.mPresShellResolution, metrics.mPresShellResolution);
 
   CSSSize scrollPort = metrics.CalculateCompositedSizeInCssPixels();
   utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
@@ -463,17 +463,17 @@ TabChildBase::HandlePossibleViewportChan
 
   // Force a repaint with these metrics. This, among other things, sets the
   // displayport, so we start with async painting.
   mLastRootMetrics = ProcessUpdateFrame(metrics);
 
   if (viewportInfo.IsZoomAllowed() && scrollIdentifiersValid) {
     // If the CSS viewport is narrower than the screen (i.e. width <= device-width)
     // then we disable double-tap-to-zoom behaviour.
-    bool allowDoubleTapZoom = (viewport.width > screenW / metrics.mDevPixelsPerCSSPixel.scale);
+    bool allowDoubleTapZoom = (viewport.width > screenW / metrics.GetDevPixelsPerCSSPixel().scale);
     if (allowDoubleTapZoom != viewportInfo.IsDoubleTapZoomAllowed()) {
       viewportInfo.SetAllowDoubleTapZoom(allowDoubleTapZoom);
 
       ZoomConstraints constraints(
         viewportInfo.IsZoomAllowed(),
         viewportInfo.IsDoubleTapZoomAllowed(),
         ConvertScaleForRoot(viewportInfo.GetMinZoom()),
         ConvertScaleForRoot(viewportInfo.GetMaxZoom()));
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/plugins/PluginWidgetParent.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/layers/CompositorParent.h"
+#include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 #include "nsCOMPtr.h"
@@ -2098,16 +2099,30 @@ TabParent::UseAsyncPanZoom()
           GetScrollingBehavior() == ASYNC_PAN_ZOOM);
 }
 
 nsEventStatus
 TabParent::MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
                                           ScrollableLayerGuid* aOutTargetGuid,
                                           uint64_t* aOutInputBlockId)
 {
+  if (aEvent.mClass == eWheelEventClass) {
+    // Wheel events must be sent to APZ directly from the widget. New APZ-
+    // aware events should follow suit and move there as well. However, we
+    // do need to inform the child process of the correct target and block
+    // id.
+    if (aOutTargetGuid) {
+      *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid();
+    }
+    if (aOutInputBlockId) {
+      *aOutInputBlockId = InputAPZContext::GetInputBlockId();
+    }
+    return nsEventStatus_eIgnore;
+  }
+
   if (RenderFrameParent* rfp = GetRenderFrame()) {
     return rfp->NotifyInputEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
   }
   return nsEventStatus_eIgnore;
 }
 
 bool
 TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
--- 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/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -467,16 +467,17 @@ skip-if = (toolkit == 'android' && proce
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_reset_events_async.html]
 [test_reset_src.html]
 [test_resume.html]
 skip-if = true # bug 1021673
 [test_seek_out_of_range.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_seek-1.html]
+skip-if = android_version == '10' # bug 1059116
 [test_seek-2.html]
 [test_seek-3.html]
 [test_seek-4.html]
 [test_seek-5.html]
 [test_seek-6.html]
 [test_seek-7.html]
 [test_seek-8.html]
 [test_seek-9.html]
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1294,35 +1294,51 @@ private:
   nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const JSStructuredCloneCallbacks* mCallbacks;
   JSAutoStructuredCloneBuffer mBuffer;
 
   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
   PromiseWorkerProxy::RunCallbackFunc mFunc;
 };
 
+/* static */
+already_AddRefed<PromiseWorkerProxy>
+PromiseWorkerProxy::Create(WorkerPrivate* aWorkerPrivate,
+                           Promise* aWorkerPromise,
+                           const JSStructuredCloneCallbacks* aCb)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(aWorkerPromise);
+
+  nsRefPtr<PromiseWorkerProxy> proxy =
+    new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise, aCb);
+
+  // We do this to make sure the worker thread won't shut down before the
+  // promise is resolved/rejected on the worker thread.
+  if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), proxy)) {
+    // Probably the worker is terminating. We cannot complete the operation
+    // and we have to release all the resources.
+    proxy->mCleanedUp = true;
+    proxy->mWorkerPromise = nullptr;
+    return nullptr;
+  }
+
+  return proxy.forget();
+}
+
 PromiseWorkerProxy::PromiseWorkerProxy(WorkerPrivate* aWorkerPrivate,
                                        Promise* aWorkerPromise,
                                        const JSStructuredCloneCallbacks* aCallbacks)
   : mWorkerPrivate(aWorkerPrivate)
   , mWorkerPromise(aWorkerPromise)
   , mCleanedUp(false)
   , mCallbacks(aCallbacks)
   , mCleanUpLock("cleanUpLock")
 {
-  MOZ_ASSERT(mWorkerPrivate);
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mWorkerPromise);
-
-  // We do this to make sure the worker thread won't shut down before the
-  // promise is resolved/rejected on the worker thread.
-  if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
-    MOZ_ASSERT(false, "cannot add the worker feature!");
-    return;
-  }
 }
 
 PromiseWorkerProxy::~PromiseWorkerProxy()
 {
   MOZ_ASSERT(mCleanedUp);
   MOZ_ASSERT(!mWorkerPromise);
 }
 
@@ -1347,16 +1363,46 @@ PromiseWorkerProxy::StoreISupports(nsISu
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsMainThreadPtrHandle<nsISupports> supports(
     new nsMainThreadPtrHolder<nsISupports>(aSupports));
   mSupportsArray.AppendElement(supports);
 }
 
+namespace {
+
+class PromiseWorkerProxyControlRunnable MOZ_FINAL
+  : public WorkerControlRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mProxy;
+
+public:
+  PromiseWorkerProxyControlRunnable(WorkerPrivate* aWorkerPrivate,
+                                    PromiseWorkerProxy* aProxy)
+    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  virtual bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    mProxy->CleanUp(aCx);
+    return true;
+  }
+
+private:
+  ~PromiseWorkerProxyControlRunnable()
+  {}
+};
+
+} // anonymous namespace
+
 void
 PromiseWorkerProxy::RunCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue,
                                 RunCallbackFunc aFunc)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   MutexAutoLock lock(mCleanUpLock);
@@ -1375,17 +1421,21 @@ PromiseWorkerProxy::RunCallback(JSContex
   }
 
   nsRefPtr<PromiseWorkerProxyRunnable> runnable =
     new PromiseWorkerProxyRunnable(this,
                                    mCallbacks,
                                    Move(buffer),
                                    aFunc);
 
-  runnable->Dispatch(aCx);
+  if (!runnable->Dispatch(aCx)) {
+    nsRefPtr<WorkerControlRunnable> runnable =
+      new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this);
+    mWorkerPrivate->DispatchControlRunnable(runnable);
+  }
 }
 
 void
 PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
                                      JS::Handle<JS::Value> aValue)
 {
   RunCallback(aCx, aValue, &Promise::ResolveInternal);
 }
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -60,19 +60,20 @@ class PromiseWorkerProxy : public Promis
                            public workers::WorkerFeature
 {
   friend class PromiseWorkerProxyRunnable;
 
   // This overrides the non-threadsafe refcounting in PromiseNativeHandler.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseWorkerProxy)
 
 public:
-  PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
-                     Promise* aWorkerPromise,
-                     const JSStructuredCloneCallbacks* aCallbacks = nullptr);
+  static already_AddRefed<PromiseWorkerProxy>
+  Create(workers::WorkerPrivate* aWorkerPrivate,
+         Promise* aWorkerPromise,
+         const JSStructuredCloneCallbacks* aCallbacks = nullptr);
 
   workers::WorkerPrivate* GetWorkerPrivate() const;
 
   Promise* GetWorkerPromise() const;
 
   void StoreISupports(nsISupports* aSupports);
 
   void CleanUp(JSContext* aCx);
@@ -82,16 +83,20 @@ protected:
                                 JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
   virtual void RejectedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
   virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
 
 private:
+  PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
+                     Promise* aWorkerPromise,
+                     const JSStructuredCloneCallbacks* aCallbacks = nullptr);
+
   virtual ~PromiseWorkerProxy();
 
   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
   typedef void (Promise::*RunCallbackFunc)(JSContext*,
                                            JS::Handle<JS::Value>);
 
   void RunCallback(JSContext* aCx,
                    JS::Handle<JS::Value> aValue,
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -31,16 +31,17 @@
 #include "nsNetUtil.h"
 #include "nsNullPrincipal.h"
 #include "nsIContentPolicy.h"
 #include "nsSupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsString.h"
 #include "prlog.h"
 #include "mozilla/dom/CSPReportBinding.h"
+#include "mozilla/net/ReferrerPolicy.h"
 
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo *
 GetCspContextLog()
 {
   static PRLogModuleInfo *gCspContextPRLog;
@@ -301,16 +302,44 @@ nsCSPContext::GetPolicy(uint32_t aIndex,
 NS_IMETHODIMP
 nsCSPContext::GetPolicyCount(uint32_t *outPolicyCount)
 {
   *outPolicyCount = mPolicies.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsCSPContext::GetReferrerPolicy(uint32_t* outPolicy, bool* outIsSet)
+{
+  *outIsSet = false;
+  *outPolicy = mozilla::net::RP_Default;
+  nsAutoString refpol;
+  mozilla::net::ReferrerPolicy previousPolicy = mozilla::net::RP_Default;
+  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+    mPolicies[i]->getReferrerPolicy(refpol);
+    // an empty string in refpol means it wasn't set (that's the default in
+    // nsCSPPolicy).
+    if (!refpol.IsEmpty()) {
+      // if there are two policies that specify a referrer policy, then they
+      // must agree or the employed policy is no-referrer.
+      uint32_t currentPolicy = mozilla::net::ReferrerPolicyFromString(refpol);
+      if (*outIsSet && previousPolicy != currentPolicy) {
+        *outPolicy = mozilla::net::RP_No_Referrer;
+        return NS_OK;
+      }
+
+      *outPolicy = currentPolicy;
+      *outIsSet = true;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsCSPContext::RemovePolicy(uint32_t aIndex)
 {
   if (aIndex >= mPolicies.Length()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   mPolicies.RemoveElementAt(aIndex);
   // reset cache since effective policy changes
   mShouldLoadCache.Clear();
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -9,16 +9,17 @@
 #include "nsCSPUtils.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
+#include "mozilla/net/ReferrerPolicy.h"
 
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo*
 GetCspParserLog()
 {
   static PRLogModuleInfo* gCspParserPRLog;
@@ -853,16 +854,40 @@ nsCSPParser::sourceList(nsTArray<nsCSPBa
       const char16_t* params[] = { unicodeNone.get() };
       logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
                                params, ArrayLength(params));
     }
   }
 }
 
 void
+nsCSPParser::referrerDirectiveValue()
+{
+  // directive-value   = "none" / "none-when-downgrade" / "origin" / "origin-when-cross-origin" / "unsafe-url"
+  // directive name is token 0, we need to examine the remaining tokens (and
+  // there should only be one token in the value).
+  CSPPARSERLOG(("nsCSPParser::referrerDirectiveValue"));
+
+  if (mCurDir.Length() > 2) {
+    CSPPARSERLOG(("Too many tokens in referrer directive, got %d expected 1",
+                 mCurDir.Length() - 1));
+    return;
+  }
+
+  if (!mozilla::net::IsValidReferrerPolicy(mCurDir[1])) {
+    CSPPARSERLOG(("invalid value for referrer directive: %s",
+                  NS_ConvertUTF16toUTF8(mCurDir[1]).get()));
+    return;
+  }
+
+  // the referrer policy is valid, so go ahead and use it.
+  mPolicy->setReferrerPolicy(&mCurDir[1]);
+}
+
+void
 nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv;
 
   // remember, srcs start at index 1
   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     mCurToken = mCurDir[i];
@@ -895,16 +920,24 @@ nsCSPParser::directiveValue(nsTArray<nsC
 
   // The tokenzier already generated an array in the form of
   // [ name, src, src, ... ], no need to parse again, but
   // special case handling in case the directive is report-uri.
   if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
     reportURIList(outSrcs);
     return;
   }
+
+  // special case handling of the referrer directive (since it doesn't contain
+  // source lists)
+  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
+    referrerDirectiveValue();
+    return;
+  }
+
   // Otherwise just forward to sourceList
   sourceList(outSrcs);
 }
 
 // directive-name = 1*( ALPHA / DIGIT / "-" )
 nsCSPDirective*
 nsCSPParser::directiveName()
 {
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -108,16 +108,17 @@ class nsCSPParser {
     ~nsCSPParser();
 
 
     // Parsing the CSP using the source-list from http://www.w3.org/TR/CSP11/#source-list
     nsCSPPolicy*    policy();
     void            directive();
     nsCSPDirective* directiveName();
     void            directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs);
+    void            referrerDirectiveValue();
     void            sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs);
     nsCSPBaseSrc*   sourceExpression();
     nsCSPSchemeSrc* schemeSource();
     nsCSPHostSrc*   hostSource();
     nsCSPBaseSrc*   keywordSource();
     nsCSPNonceSrc*  nonceSource();
     nsCSPHashSrc*   hashSource();
     nsCSPHostSrc*   appHost(); // helper function to support app specific hosts
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -894,17 +894,24 @@ nsCSPPolicy::allows(nsContentPolicyType 
   return allows(aContentType, aKeyword, NS_LITERAL_STRING(""));
 }
 
 void
 nsCSPPolicy::toString(nsAString& outStr) const
 {
   uint32_t length = mDirectives.Length();
   for (uint32_t i = 0; i < length; ++i) {
-    mDirectives[i]->toString(outStr);
+
+    if (mDirectives[i]->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
+      outStr.AppendASCII(CSP_CSPDirectiveToString(nsIContentSecurityPolicy::REFERRER_DIRECTIVE));
+      outStr.AppendASCII(" ");
+      outStr.Append(mReferrerPolicy);
+    } else {
+      mDirectives[i]->toString(outStr);
+    }
     if (i != (length - 1)) {
       outStr.AppendASCII("; ");
     }
   }
 }
 
 bool
 nsCSPPolicy::hasDirective(CSPDirective aDir) const
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -68,17 +68,18 @@ static const char* CSPStrDirectives[] = 
   "media-src",       // MEDIA_SRC_DIRECTIVE
   "frame-src",       // FRAME_SRC_DIRECTIVE
   "font-src",        // FONT_SRC_DIRECTIVE
   "connect-src",     // CONNECT_SRC_DIRECTIVE
   "report-uri",      // REPORT_URI_DIRECTIVE
   "frame-ancestors", // FRAME_ANCESTORS_DIRECTIVE
   "reflected-xss",   // REFLECTED_XSS_DIRECTIVE
   "base-uri",        // BASE_URI_DIRECTIVE
-  "form-action"      // FORM_ACTION_DIRECTIVE
+  "form-action",     // FORM_ACTION_DIRECTIVE
+  "referrer"         // REFERRER_DIRECTIVE
 };
 
 inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
 {
   return CSPStrDirectives[static_cast<uint32_t>(aDir)];
 }
 
 inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
@@ -327,24 +328,31 @@ class nsCSPPolicy {
     bool hasDirective(CSPDirective aDir) const;
 
     inline void setReportOnlyFlag(bool aFlag)
       { mReportOnly = aFlag; }
 
     inline bool getReportOnlyFlag() const
       { return mReportOnly; }
 
+    inline void setReferrerPolicy(const nsAString* aValue)
+      { mReferrerPolicy = *aValue; }
+
+    inline void getReferrerPolicy(nsAString& outPolicy) const
+      { outPolicy.Assign(mReferrerPolicy); }
+
     void getReportURIs(nsTArray<nsString> &outReportURIs) const;
 
     void getDirectiveStringForContentType(nsContentPolicyType aContentType,
                                           nsAString& outDirective) const;
 
     void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;
 
     inline uint32_t getNumDirectives() const
       { return mDirectives.Length(); }
 
   private:
     nsTArray<nsCSPDirective*> mDirectives;
     bool                      mReportOnly;
+    nsString                  mReferrerPolicy;
 };
 
 #endif /* nsCSPUtils_h___ */
--- a/dom/svg/SVGContentUtils.cpp
+++ b/dom/svg/SVGContentUtils.cpp
@@ -194,17 +194,17 @@ SVGContentUtils::GetStrokeOptions(AutoSt
 
   aStrokeOptions->mLineWidth =
     GetStrokeWidth(aElement, styleContext, aContextPaint);
 
   aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
 
   switch (styleSVG->mStrokeLinejoin) {
   case NS_STYLE_STROKE_LINEJOIN_MITER:
-    aStrokeOptions->mLineJoin = JoinStyle::MITER;
+    aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL;
     break;
   case NS_STYLE_STROKE_LINEJOIN_ROUND:
     aStrokeOptions->mLineJoin = JoinStyle::ROUND;
     break;
   case NS_STYLE_STROKE_LINEJOIN_BEVEL:
     aStrokeOptions->mLineJoin = JoinStyle::BEVEL;
     break;
   }
--- 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/DataStore.cpp
+++ b/dom/workers/DataStore.cpp
@@ -132,89 +132,112 @@ protected:
   {
     AssertIsOnMainThread();
 
     mReadOnly = mBackingStore->GetReadOnly(mRv);
     return true;
   }
 };
 
+class DataStoreProxyRunnable : public DataStoreRunnable
+{
+public:
+  DataStoreProxyRunnable(WorkerPrivate* aWorkerPrivate,
+                         const nsMainThreadPtrHandle<DataStore>& aBackingStore,
+                         Promise* aWorkerPromise)
+    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    mPromiseWorkerProxy =
+      PromiseWorkerProxy::Create(aWorkerPrivate, aWorkerPromise);
+  }
+
+  bool Dispatch(JSContext* aCx)
+  {
+    if (mPromiseWorkerProxy) {
+      return DataStoreRunnable::Dispatch(aCx);
+    }
+
+    // If the creation of mProxyWorkerProxy failed, the worker is terminating.
+    // In this case we don't want to dispatch the runnable and we should stop
+    // the promise chain here.
+    return true;
+  }
+
+protected:
+  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+};
+
 // A DataStoreRunnable to run DataStore::Get(...) on the main thread.
-class DataStoreGetRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreGetRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   Sequence<OwningStringOrUnsignedLong> mId;
   ErrorResult& mRv;
 
 public:
   DataStoreGetRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        const Sequence<OwningStringOrUnsignedLong>& aId,
                        ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     if (!mId.AppendElements(aId)) {
       mRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     }
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     nsRefPtr<Promise> promise = mBackingStore->Get(mId, mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Put(...) on the main thread.
-class DataStorePutRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStorePutRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   JSAutoStructuredCloneBuffer mObjBuffer;
   const StringOrUnsignedLong& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStorePutRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        JSContext* aCx,
                        JS::Handle<JS::Value> aObj,
                        const StringOrUnsignedLong& aId,
                        const nsAString& aRevisionId,
                        ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // This needs to be structured cloned while it's still on the worker thread.
     if (!mObjBuffer.write(aCx, aObj)) {
       JS_ClearPendingException(aCx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     }
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -239,49 +262,45 @@ protected:
                                                    mRevisionId,
                                                    mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Add(...) on the main thread.
-class DataStoreAddRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreAddRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   JSAutoStructuredCloneBuffer mObjBuffer;
   const Optional<StringOrUnsignedLong>& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreAddRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        JSContext* aCx,
                        JS::Handle<JS::Value> aObj,
                        const Optional<StringOrUnsignedLong>& aId,
                        const nsAString& aRevisionId,
                        ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // This needs to be structured cloned while it's still on the worker thread.
     if (!mObjBuffer.write(aCx, aObj)) {
       JS_ClearPendingException(aCx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     }
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -307,76 +326,68 @@ protected:
                                                    mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Remove(...) on the main
 // thread.
-class DataStoreRemoveRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreRemoveRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const StringOrUnsignedLong& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreRemoveRunnable(WorkerPrivate* aWorkerPrivate,
                           const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                           Promise* aWorkerPromise,
                           const StringOrUnsignedLong& aId,
                           const nsAString& aRevisionId,
                           ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     nsRefPtr<Promise> promise = mBackingStore->Remove(mId, mRevisionId, mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Clear(...) on the main thread.
-class DataStoreClearRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreClearRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreClearRunnable(WorkerPrivate* aWorkerPrivate,
                          const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                          Promise* aWorkerPromise,
                          const nsAString& aRevisionId,
                          ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -621,34 +632,30 @@ WorkerDataStore::GetRevisionId(JSContext
                                    mBackingStore,
                                    &DataStore::GetRevisionId,
                                    aRevisionId,
                                    aRv);
   runnable->Dispatch(aCx);
 }
 
 // A DataStoreRunnable to run DataStore::GetLength(...) on the main thread.
-class DataStoreGetLengthRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreGetLengthRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   ErrorResult& mRv;
 
 public:
   DataStoreGetLengthRunnable(WorkerPrivate* aWorkerPrivate,
                              const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                              Promise* aWorkerPromise,
                              ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
--- a/dom/workers/DataStoreCursor.cpp
+++ b/dom/workers/DataStoreCursor.cpp
@@ -77,17 +77,29 @@ public:
                               ErrorResult& aRv)
     : DataStoreCursorRunnable(aWorkerPrivate, aBackingCursor)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
+      PromiseWorkerProxy::Create(aWorkerPrivate, aWorkerPromise);
+  }
+
+  bool Dispatch(JSContext* aCx)
+  {
+    if (mPromiseWorkerProxy) {
+      return DataStoreCursorRunnable::Dispatch(aCx);
+    }
+
+    // If the creation of mProxyWorkerProxy failed, the worker is terminating.
+    // In this case we don't want to dispatch the runnable and we should stop
+    // the promise chain here.
+    return true;
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -218,22 +218,36 @@ public:
     : WorkerMainThreadRunnable(aWorkerPrivate)
     , mName(aName)
     , mOwner(aOwner)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
+    // this might return null if the worker has started the close handler.
     mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate,
-                             aWorkerPromise,
-                             &kGetDataStoresStructuredCloneCallbacks);
+      PromiseWorkerProxy::Create(aWorkerPrivate,
+                                 aWorkerPromise,
+                                 &kGetDataStoresStructuredCloneCallbacks);
   }
 
+  bool Dispatch(JSContext* aCx)
+  {
+    if (mPromiseWorkerProxy) {
+      return WorkerMainThreadRunnable::Dispatch(aCx);
+    }
+
+    // If the creation of mProxyWorkerProxy failed, the worker is terminating.
+    // In this case we don't want to dispatch the runnable and we should stop
+    // the promise chain here.
+    return true;
+  }
+
+
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     // Walk up to the containing window.
     WorkerPrivate* wp = mWorkerPrivate;
--- 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/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -649,20 +649,19 @@ TemporaryRef<SourceSurface>
 DrawTargetCairo::Snapshot()
 {
   if (mSnapshot) {
     return mSnapshot;
   }
 
   IntSize size = GetSize();
 
-  cairo_content_t content = cairo_surface_get_content(mSurface);
   mSnapshot = new SourceSurfaceCairo(mSurface,
                                      size,
-                                     CairoContentToGfxFormat(content),
+                                     GfxFormatForCairoSurface(mSurface),
                                      this);
   return mSnapshot;
 }
 
 bool
 DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
                           int32_t* aStride, SurfaceFormat* aFormat)
 {
@@ -1498,17 +1497,17 @@ DrawTargetCairo::InitAlreadyReferenced(c
                        << aSize << " Cairo Status: " << cairo_surface_status(aSurface);
     cairo_surface_destroy(aSurface);
     return false;
   }
 
   mContext = cairo_create(aSurface);
   mSurface = aSurface;
   mSize = aSize;
-  mFormat = aFormat ? *aFormat : CairoContentToGfxFormat(cairo_surface_get_content(aSurface));
+  mFormat = aFormat ? *aFormat : GfxFormatForCairoSurface(aSurface);
 
   // Cairo image surface have a bug where they will allocate a mask surface (for clipping)
   // the size of the clip extents, and don't take the surface extents into account.
   // Add a manual clip to the surface extents to prevent this.
   cairo_new_path(mContext);
   cairo_rectangle(mContext, 0, 0, mSize.width, mSize.height);
   cairo_clip(mContext);
 
--- a/gfx/2d/HelpersCairo.h
+++ b/gfx/2d/HelpersCairo.h
@@ -211,16 +211,45 @@ CairoContentToGfxFormat(cairo_content_t 
       return SurfaceFormat::B8G8R8X8;
     case CAIRO_CONTENT_ALPHA:
       return SurfaceFormat::A8;
   }
 
   return SurfaceFormat::B8G8R8A8;
 }
 
+static inline SurfaceFormat
+CairoFormatToGfxFormat(cairo_format_t format)
+{
+  switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+      return SurfaceFormat::B8G8R8A8;
+    case CAIRO_FORMAT_RGB24:
+      return SurfaceFormat::B8G8R8X8;
+    case CAIRO_FORMAT_A8:
+      return SurfaceFormat::A8;
+    case CAIRO_FORMAT_RGB16_565:
+      return SurfaceFormat::R5G6B5;
+    default:
+      gfxWarning() << "Unknown cairo format";
+      MOZ_ASSERT(false, "Unknown cairo format");
+      return SurfaceFormat::UNKNOWN;
+  }
+}
+
+static inline SurfaceFormat
+GfxFormatForCairoSurface(cairo_surface_t* surface)
+{
+  if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
+    return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface));
+  }
+
+  return CairoContentToGfxFormat(cairo_surface_get_content(surface));
+}
+
 static inline void
 GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
 {
   cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
 }
 
 static inline void
 SetCairoStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions)
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -201,18 +201,20 @@ MOZ_BEGIN_ENUM_CLASS(PatternType, int8_t
   SURFACE,
   LINEAR_GRADIENT,
   RADIAL_GRADIENT
 MOZ_END_ENUM_CLASS(PatternType)
 
 MOZ_BEGIN_ENUM_CLASS(JoinStyle, int8_t)
   BEVEL,
   ROUND,
-  MITER,
-  MITER_OR_BEVEL
+  MITER, //!< Mitered if within the miter limit, else, if the backed supports
+         //!< it (D2D), the miter is clamped. If the backend does not support
+         //!< miter clamping the behavior is as for MITER_OR_BEVEL.
+  MITER_OR_BEVEL //!< Mitered if within the miter limit, else beveled.
 MOZ_END_ENUM_CLASS(JoinStyle)
 
 MOZ_BEGIN_ENUM_CLASS(CapStyle, int8_t)
   BUTT,
   ROUND,
   SQUARE
 MOZ_END_ENUM_CLASS(CapStyle)
 
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -170,17 +170,16 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
         'renderer/d3d/d3d11/renderer11_utils.cpp',
         'renderer/d3d/d3d11/RenderStateCache.cpp',
         'renderer/d3d/d3d11/RenderTarget11.cpp',
         'renderer/d3d/d3d11/ShaderExecutable11.cpp',
         'renderer/d3d/d3d11/TextureStorage11.cpp',
         'renderer/d3d/d3d11/VertexBuffer11.cpp',
     ]
     SOURCES += ['renderer/d3d/d3d11/SwapChain11.cpp']
-    SOURCES['renderer/d3d/d3d11/SwapChain11.cpp'].flags += ['-DANGLE_RESOURCE_SHARE_TYPE=D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX']
 
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-attributes',
         '-Wno-sign-compare',
         '-Wno-unknown-pragmas',
     ]
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -304,23 +304,27 @@ public:
   float mPresShellResolution;
 
   // The cumulative resolution that the current frame has been painted at.
   // This is the product of the pres-shell resolutions of the document
   // containing this scroll frame and its ancestors, and any css-driven
   // resolution. This information is provided by Gecko at layout/paint time.
   LayoutDeviceToLayerScale mCumulativeResolution;
 
-  // The conversion factor between CSS pixels and device pixels for this frame.
-  // This can vary based on a variety of things, such as reflowing-zoom. The
-  // conversion factor for device pixels to layers pixels is just the
-  // resolution.
-  CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
+public:
+  void SetDevPixelsPerCSSPixel(const CSSToLayoutDeviceScale& aDevPixelsPerCSSPixel)
+  {
+    mDevPixelsPerCSSPixel = aDevPixelsPerCSSPixel;
+  }
 
-public:
+  CSSToLayoutDeviceScale GetDevPixelsPerCSSPixel() const
+  {
+    return mDevPixelsPerCSSPixel;
+  }
+
   void SetIsRoot(bool aIsRoot)
   {
     mIsRoot = aIsRoot;
   }
 
   bool GetIsRoot() const
   {
     return mIsRoot;
@@ -522,16 +526,22 @@ public:
   {
     mLineScrollAmount = size;
   }
 
 private:
   // New fields from now on should be made private and old fields should
   // be refactored to be private.
 
+  // The conversion factor between CSS pixels and device pixels for this frame.
+  // This can vary based on a variety of things, such as reflowing-zoom. The
+  // conversion factor for device pixels to layers pixels is just the
+  // resolution.
+  CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
+
   // Whether or not this frame may have touch or scroll wheel listeners.
   bool mMayHaveTouchListeners;
 
   // Whether or not this frame may have a touch caret.
   bool mMayHaveTouchCaret;
 
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
@@ -632,47 +642,42 @@ struct ScrollableLayerGuid {
   uint32_t mPresShellId;
   FrameMetrics::ViewID mScrollId;
 
   ScrollableLayerGuid()
     : mLayersId(0)
     , mPresShellId(0)
     , mScrollId(0)
   {
-    MOZ_COUNT_CTOR(ScrollableLayerGuid);
   }
 
   ScrollableLayerGuid(uint64_t aLayersId, uint32_t aPresShellId,
                       FrameMetrics::ViewID aScrollId)
     : mLayersId(aLayersId)
     , mPresShellId(aPresShellId)
     , mScrollId(aScrollId)
   {
-    MOZ_COUNT_CTOR(ScrollableLayerGuid);
   }
 
   ScrollableLayerGuid(uint64_t aLayersId, const FrameMetrics& aMetrics)
     : mLayersId(aLayersId)
     , mPresShellId(aMetrics.GetPresShellId())
     , mScrollId(aMetrics.GetScrollId())
   {
-    MOZ_COUNT_CTOR(ScrollableLayerGuid);
   }
 
   ScrollableLayerGuid(const ScrollableLayerGuid& other)
     : mLayersId(other.mLayersId)
     , mPresShellId(other.mPresShellId)
     , mScrollId(other.mScrollId)
   {
-    MOZ_COUNT_CTOR(ScrollableLayerGuid);
   }
 
   ~ScrollableLayerGuid()
   {
-    MOZ_COUNT_DTOR(ScrollableLayerGuid);
   }
 
   bool operator==(const ScrollableLayerGuid& other) const
   {
     return mLayersId == other.mLayersId
         && mPresShellId == other.mPresShellId
         && mScrollId == other.mScrollId;
   }
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -185,17 +185,17 @@ AppendToString(std::stringstream& aStrea
     }
     aStream << nsPrintfCString(" z=%.3f }", m.GetZoom().scale).get();
   } else {
     AppendToString(aStream, m.GetDisplayPortMargins(), " dpm=");
     aStream << nsPrintfCString(" um=%d", m.GetUseDisplayPortMargins()).get();
     AppendToString(aStream, m.GetRootCompositionSize(), " rcs=");
     AppendToString(aStream, m.GetViewport(), " v=");
     aStream << nsPrintfCString(" z=(ld=%.3f r=%.3f cr=%.3f z=%.3f er=%.3f)",
-            m.mDevPixelsPerCSSPixel.scale, m.mPresShellResolution,
+            m.GetDevPixelsPerCSSPixel().scale, m.mPresShellResolution,
             m.mCumulativeResolution.scale, m.GetZoom().scale,
             m.GetExtraResolution().scale).get();
     aStream << nsPrintfCString(" u=(%d %d %lu)",
             m.GetScrollOffsetUpdated(), m.GetDoSmoothScroll(),
             m.GetScrollGeneration()).get();
     AppendToString(aStream, m.GetScrollParentId(), " p=");
     aStream << nsPrintfCString(" i=(%ld %lld) }",
             m.GetPresShellId(), m.GetScrollId()).get();
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -212,17 +212,17 @@ ComputeTouchSensitiveRegion(GeckoContent
     // the cumulative resolution of the parent frame. We approximate this as
     // the quotient of our cumulative resolution and our pres shell resolution;
     // this approximation may not be accurate in the presence of a css-driven
     // resolution.
     LayoutDeviceToParentLayerScale parentCumulativeResolution =
           aMetrics.mCumulativeResolution
         / ParentLayerToLayerScale(aMetrics.mPresShellResolution);
     visible = visible.Intersect(touchSensitiveRegion
-                                * aMetrics.mDevPixelsPerCSSPixel
+                                * aMetrics.GetDevPixelsPerCSSPixel()
                                 * parentCumulativeResolution);
   }
 
   // Not sure what rounding option is the most correct here, but if we ever
   // figure it out we can change this. For now I'm rounding in to minimize
   // the chances of getting a complex region.
   nsIntRegion unobscured;
   unobscured.Sub(ParentLayerIntRect::ToUntyped(RoundedIn(visible)), aObscured);
@@ -560,17 +560,17 @@ APZCTreeManager::UpdatePanZoomController
         // the quotient of our cumulative resolution and our pres shell
         // resolution; this approximation may not be accurate in the presence of
         // a css-driven resolution.
         LayoutDeviceToParentLayerScale parentCumulativeResolution =
             aLayer.Metrics().mCumulativeResolution
             / ParentLayerToLayerScale(aLayer.Metrics().mPresShellResolution);
         subtreeEventRegions.AndWith(ParentLayerIntRect::ToUntyped(
             RoundedIn(touchSensitiveRegion
-                    * aLayer.Metrics().mDevPixelsPerCSSPixel
+                    * aLayer.Metrics().GetDevPixelsPerCSSPixel()
                     * parentCumulativeResolution)));
       }
       apzc->AddHitTestRegions(subtreeEventRegions);
     } else {
       // If we don't have an APZC at this level, carry the subtree hit region
       // up to the parent.
       MOZ_ASSERT(aState.mEventRegions.Length() > 0);
       aState.mEventRegions.LastElement().OrWith(subtreeEventRegions);
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1446,17 +1446,17 @@ AsyncPanZoomController::ConvertToGecko(c
 {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
     Matrix4x4 transformToGecko = treeManagerLocal->GetApzcToGeckoTransform(this);
     // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
     // for this coordinate space and it maps the closest to LayoutDevicePoint.
     LayoutDevicePoint layoutPoint = TransformTo<LayoutDevicePixel>(transformToGecko, aPoint);
     { // scoped lock to access mFrameMetrics
       ReentrantMonitorAutoEnter lock(mMonitor);
-      *aOut = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
+      *aOut = layoutPoint / mFrameMetrics.GetDevPixelsPerCSSPixel();
     }
     return true;
   }
   return false;
 }
 
 nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
 {
@@ -1482,17 +1482,17 @@ nsEventStatus AsyncPanZoomController::On
       start.mLocalPanStartPoint = aEvent.mLocalOrigin;
       OnPanBegin(start);
 
       // Pan gestures use natural directions which are inverted from scroll
       // wheel and touchpad scroll gestures, so we invert x/y here. Since the
       // zoom includes any device : css pixel zoom, we convert to CSS pixels
       // before applying the zoom.
       LayoutDevicePoint devicePixelDelta(-deltaX, -deltaY);
-      ParentLayerPoint delta = (devicePixelDelta / mFrameMetrics.mDevPixelsPerCSSPixel) *
+      ParentLayerPoint delta = (devicePixelDelta / mFrameMetrics.GetDevPixelsPerCSSPixel()) *
                                mFrameMetrics.GetZoom();
 
       PanGestureInput move(PanGestureInput::PANGESTURE_PAN, aEvent.mTime, aEvent.mTimeStamp,
                            aEvent.mOrigin,
                            ToScreenCoordinates(delta, aEvent.mLocalOrigin),
                            aEvent.modifiers);
       move.mLocalPanStartPoint = aEvent.mLocalOrigin;
       move.mLocalPanDisplacement = delta;
@@ -1501,17 +1501,17 @@ nsEventStatus AsyncPanZoomController::On
       PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp,
                             aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
       end.mLocalPanStartPoint = aEvent.mLocalOrigin;
       OnPanEnd(start);
       break;
     }
 
     case ScrollWheelInput::SCROLLMODE_SMOOTH: {
-      CSSPoint delta = LayoutDevicePoint(deltaX, deltaY) / mFrameMetrics.mDevPixelsPerCSSPixel;
+      CSSPoint delta = LayoutDevicePoint(deltaX, deltaY) / mFrameMetrics.GetDevPixelsPerCSSPixel();
 
       // If we're already in a smooth scroll animation, don't cancel it. This
       // lets us preserve the existing scrolling velocity.
       if (mState != SMOOTH_SCROLL) {
         CancelAnimation();
         mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetScrollOffset() + delta);
       } else {
         mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetSmoothScrollOffset() + delta);
@@ -2650,17 +2650,17 @@ Matrix4x4 AsyncPanZoomController::GetNon
                             1.0f);
 }
 
 Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   LayerPoint scrollChange =
     (mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
-    * mLastContentPaintMetrics.mDevPixelsPerCSSPixel
+    * mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
     * mLastContentPaintMetrics.mCumulativeResolution
       // This transform ("LD" in the terminology of the comment above
       // GetScreenToApzcTransform() in APZCTreeManager.h) is applied in a
       // coordinate space that includes the APZC's CSS transform ("LC").
       // This CSS transform is the identity unless this APZC sets a pres-shell
       // resolution, in which case the transform has a post-scale that cancels
       // out the pres-shell resolution. We simulate applying the "LC" transform
       // by dividing by the pres-shell resolution. This will go away once
@@ -2752,34 +2752,34 @@ void AsyncPanZoomController::NotifyLayer
       needContentRepaint = true;
     }
   } else {
     // If we're not taking the aLayerMetrics wholesale we still need to pull
     // in some things into our local mFrameMetrics because these things are
     // determined by Gecko and our copy in mFrameMetrics may be stale.
 
     if (FuzzyEqualsAdditive(mFrameMetrics.mCompositionBounds.width, aLayerMetrics.mCompositionBounds.width) &&
-        mFrameMetrics.mDevPixelsPerCSSPixel == aLayerMetrics.mDevPixelsPerCSSPixel) {
+        mFrameMetrics.GetDevPixelsPerCSSPixel() == aLayerMetrics.GetDevPixelsPerCSSPixel()) {
       // Any change to the pres shell resolution was requested by APZ and is
       // already included in our zoom; however, other components of the
       // cumulative resolution (a parent document's pres-shell resolution, or
       // the css-driven resolution) may have changed, and we need to update
       // our zoom to reflect that. Note that we can't just take
       // aLayerMetrics.mZoom because the APZ may have additional async zoom
       // since the repaint request.
       float totalResolutionChange = aLayerMetrics.mCumulativeResolution.scale
                                   / mFrameMetrics.mCumulativeResolution.scale;
       float presShellResolutionChange = aLayerMetrics.mPresShellResolution
                                       / mFrameMetrics.mPresShellResolution;
       mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange);
     } else {
       // Take the new zoom as either device scale or composition width or both
       // got changed (e.g. due to orientation change).
       mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
-      mFrameMetrics.mDevPixelsPerCSSPixel.scale = aLayerMetrics.mDevPixelsPerCSSPixel.scale;
+      mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel());
     }
     if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aLayerMetrics.mScrollableRect)) {
       mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
       needContentRepaint = true;
     }
     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.mPresShellResolution = aLayerMetrics.mPresShellResolution;
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -50,18 +50,18 @@ InputQueue::ReceiveInputEvent(const nsRe
       // TODO (bug 1098430): we will eventually need to have smarter handling for
       // non-touch events as well.
       return aTarget->HandleInputEvent(aEvent);
   }
 }
 
 bool
 InputQueue::MaybeHandleCurrentBlock(const nsRefPtr<AsyncPanZoomController>& aTarget,
-                                           CancelableBlockState *block,
-                                           const InputData& aEvent) {
+                                    CancelableBlockState *block,
+                                    const InputData& aEvent) {
   if (block == CurrentBlock() && block->IsReadyForHandling()) {
     INPQ_LOG("current block is ready with target %p preventdefault %d\n",
         aTarget.get(), block->IsDefaultPrevented());
     if (!aTarget || block->IsDefaultPrevented()) {
       return true;
     }
     aTarget->HandleInputEvent(aEvent);
     return true;
@@ -157,17 +157,17 @@ InputQueue::ReceiveScrollWheelInput(cons
   // null) should take priority. This is equivalent to just always using the
   // target (confirmed or not) from the block.
   nsRefPtr<AsyncPanZoomController> target = block->GetTargetApzc();
 
   if (!MaybeHandleCurrentBlock(target, block, aEvent)) {
     block->AddEvent(aEvent.AsScrollWheelInput());
   }
 
-  return nsEventStatus_eIgnore;
+  return nsEventStatus_eConsumeDoDefault;
 }
 
 void
 InputQueue::CancelAnimationsForNewBlock(CancelableBlockState* aBlock)
 {
   // We want to cancel animations here as soon as possible (i.e. without waiting for
   // content responses) because a finger has gone down and we don't want to keep moving
   // the content under the finger. However, to prevent "future" touchstart events from
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/util/InputAPZContext.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "InputAPZContext.h"
+
+namespace mozilla {
+namespace layers {
+
+ScrollableLayerGuid InputAPZContext::sGuid;
+uint64_t InputAPZContext::sBlockId = 0;
+
+/*static*/ ScrollableLayerGuid
+InputAPZContext::GetTargetLayerGuid()
+{
+  return sGuid;
+}
+
+/*static*/ uint64_t
+InputAPZContext::GetInputBlockId()
+{
+  return sBlockId;
+}
+
+InputAPZContext::InputAPZContext(const ScrollableLayerGuid& aGuid,
+                                 const uint64_t& aBlockId)
+  : mOldGuid(sGuid)
+  , mOldBlockId(sBlockId)
+{
+  sGuid = aGuid;
+  sBlockId = aBlockId;
+}
+
+InputAPZContext::~InputAPZContext()
+{
+  sGuid = mOldGuid;
+  sBlockId = mOldBlockId;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/util/InputAPZContext.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_layers_InputAPZContext_h
+#define mozilla_layers_InputAPZContext_h
+
+#include "FrameMetrics.h"
+
+namespace mozilla {
+namespace layers {
+
+// InputAPZContext is used to communicate the ScrollableLayerGuid and input
+// block ID from nsIWidget to RenderFrameParent. It is conceptually attached
+// to any WidgetInputEvent that has been processed by APZ directly from a
+// widget.
+class MOZ_STACK_CLASS InputAPZContext
+{
+private:
+  static ScrollableLayerGuid sGuid;
+  static uint64_t sBlockId;
+
+public:
+  static ScrollableLayerGuid GetTargetLayerGuid();
+  static uint64_t GetInputBlockId();
+
+  InputAPZContext(const ScrollableLayerGuid& aGuid,
+                    const uint64_t& aBlockId);
+  ~InputAPZContext();
+
+private:
+  ScrollableLayerGuid mOldGuid;
+  uint64_t mOldBlockId;
+};
+
+}
+}
+
+#endif /* mozilla_layers_InputAPZContext_h */
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -878,30 +878,17 @@ ClientTiledLayerBuffer::PaintThebes(cons
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   long start = PR_IntervalNow();
 #endif
 
   // If this region is empty XMost() - 1 will give us a negative value.
   NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n");
 
-  bool useSinglePaintBuffer = UseSinglePaintBuffer();
-  // XXX The single-tile case doesn't work at the moment, see bug 850396
-  /*
-  if (useSinglePaintBuffer) {
-    // Check if the paint only spans a single tile. If that's
-    // the case there's no point in using a single paint buffer.
-    nsIntRect paintBounds = aPaintRegion.GetBounds();
-    useSinglePaintBuffer = GetTileStart(paintBounds.x) !=
-                           GetTileStart(paintBounds.XMost() - 1) ||
-                           GetTileStart(paintBounds.y) !=
-                           GetTileStart(paintBounds.YMost() - 1);
-  }
-  */
-  if (useSinglePaintBuffer && !gfxPrefs::TiledDrawTargetEnabled()) {
+  if (!gfxPrefs::TiledDrawTargetEnabled()) {
     nsRefPtr<gfxContext> ctxt;
 
     const nsIntRect bounds = aPaintRegion.GetBounds();
     {
       PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc",
         js::ProfileEntry::Category::GRAPHICS);
 
       gfxImageFormat format =
@@ -1113,26 +1100,28 @@ ClientTiledLayerBuffer::ValidateTile(Til
     aTile.SetLayerManager(mManager);
   }
   aTile.SetCompositableClient(mCompositableClient);
 
   bool createdTextureClient = false;
   nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
   offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
 
-  bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget;
+  bool usingTiledDrawTarget = gfxPrefs::TiledDrawTargetEnabled();
+  MOZ_ASSERT(usingTiledDrawTarget || !!mSinglePaintDrawTarget);
+
   SurfaceMode mode;
   gfxContentType content = GetContentType(&mode);
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(offsetScaledDirtyRegion,
                         content, mode,
                         &createdTextureClient, extraPainted,
-                        !usingSinglePaintBuffer && !gfxPrefs::TiledDrawTargetEnabled(),
+                        usingTiledDrawTarget,
                         &backBufferOnWhite);
 
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     NS_WARNING("Failed to allocate a tile TextureClient");
@@ -1155,17 +1144,17 @@ ClientTiledLayerBuffer::ValidateTile(Til
     if (!backBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
       NS_WARNING("Failed to lock tile TextureClient for updating.");
       aTile.DiscardBackBuffer();
       aTile.DiscardFrontBuffer();
       return TileClient();
     }
   }
 
-  if (gfxPrefs::TiledDrawTargetEnabled()) {
+  if (usingTiledDrawTarget) {
     aTile.Flip();
 
     if (createdTextureClient) {
       if (!mCompositableClient->AddTextureClient(backBuffer)) {
         NS_WARNING("Failed to add tile TextureClient.");
         aTile.DiscardFrontBuffer();
         aTile.DiscardBackBuffer();
         return aTile;
@@ -1217,129 +1206,83 @@ ClientTiledLayerBuffer::ValidateTile(Til
       if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
         dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
         dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
       } else if (content == gfxContentType::COLOR_ALPHA) {
         dt->ClearRect(drawRect);
       }
     }
 
+    // The new buffer is now validated, remove the dirty region from it.
+    aTile.mInvalidFront.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
+                            offsetScaledDirtyRegion);
+
     return aTile;
-  } else {
-    MOZ_ASSERT(!backBufferOnWhite, "Component alpha only supported with TiledDrawTarget");
   }
 
+  // Single paint buffer case:
+
+  MOZ_ASSERT(!backBufferOnWhite, "Component alpha only supported with TiledDrawTarget");
+
   // We must not keep a reference to the DrawTarget after it has been unlocked,
   // make sure these are null'd before unlocking as destruction of the context
   // may cause the target to be flushed.
   RefPtr<DrawTarget> drawTarget = backBuffer->BorrowDrawTarget();
   drawTarget->SetTransform(Matrix());
 
   RefPtr<gfxContext> ctxt = new gfxContext(drawTarget);
 
-  if (usingSinglePaintBuffer) {
-    // XXX Perhaps we should just copy the bounding rectangle here?
-    RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot();
-    nsIntRegionRectIterator it(aDirtyRegion);
-    for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) {
+  // XXX Perhaps we should just copy the bounding rectangle here?
+  RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot();
+  nsIntRegionRectIterator it(aDirtyRegion);
+  for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) {
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
-      printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n",
-                    dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height);
+    printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n",
+                  dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height);
 #endif
-      gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x,
-                         dirtyRect->y - aTileOrigin.y,
-                         dirtyRect->width,
-                         dirtyRect->height);
-      drawRect.Scale(mResolution);
-
-      gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
-                            NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
-                            drawRect.width,
-                            drawRect.height);
-      gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
-      drawTarget->CopySurface(source, copyRect, copyTarget);
+    gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x,
+                       dirtyRect->y - aTileOrigin.y,
+                       dirtyRect->width,
+                       dirtyRect->height);
+    drawRect.Scale(mResolution);
 
-      // Mark the newly updated area as invalid in the front buffer
-      aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
-    }
-
-    // only worry about padding when not doing low-res
-    // because it simplifies the math and the artifacts
-    // won't be noticable
-    if (mResolution == 1) {
-      nsIntRect unscaledTile = nsIntRect(aTileOrigin.x,
-                                         aTileOrigin.y,
-                                         GetTileSize().width,
-                                         GetTileSize().height);
-
-      nsIntRegion tileValidRegion = GetValidRegion();
-      tileValidRegion.Or(tileValidRegion, aDirtyRegion);
-      // We only need to pad out if the tile has area that's not valid
-      if (!tileValidRegion.Contains(unscaledTile)) {
-        tileValidRegion = tileValidRegion.Intersect(unscaledTile);
-        // translate the region into tile space and pad
-        tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
-        PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
-      }
-    }
+    gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
+                          NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
+                          drawRect.width,
+                          drawRect.height);
+    gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
+    drawTarget->CopySurface(source, copyRect, copyTarget);
 
-    // The new buffer is now validated, remove the dirty region from it.
-    aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
-                           offsetScaledDirtyRegion);
-  } else {
-    // Area of the full tile...
-    nsIntRegion tileRegion =
-      nsIntRect(aTileOrigin.x, aTileOrigin.y,
-                GetScaledTileSize().width, GetScaledTileSize().height);
-
-    // Intersect this area with the portion that's dirty.
-    tileRegion = tileRegion.Intersect(aDirtyRegion);
+    // Mark the newly updated area as invalid in the front buffer
+    aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
+  }
 
-    // Add the resolution scale to store the dirty region.
-    nsIntPoint unscaledTileOrigin = nsIntPoint(aTileOrigin.x * mResolution,
-                                               aTileOrigin.y * mResolution);
-    nsIntRegion unscaledTileRegion(tileRegion);
-    unscaledTileRegion.ScaleRoundOut(mResolution, mResolution);
-
-    // Move invalid areas into scaled layer space.
-    aTile.mInvalidFront.MoveBy(unscaledTileOrigin);
-    aTile.mInvalidBack.MoveBy(unscaledTileOrigin);
-
-    // Add the area that's going to be redrawn to the invalid area of the
-    // front region.
-    aTile.mInvalidFront.Or(aTile.mInvalidFront, unscaledTileRegion);
+  // only worry about padding when not doing low-res
+  // because it simplifies the math and the artifacts
+  // won't be noticable
+  if (mResolution == 1) {
+    nsIntRect unscaledTile = nsIntRect(aTileOrigin.x,
+                                       aTileOrigin.y,
+                                       GetTileSize().width,
+                                       GetTileSize().height);
 
-    // Add invalid areas of the backbuffer to the area to redraw.
-    tileRegion.Or(tileRegion, aTile.mInvalidBack);
-
-    // Move invalid areas back into tile space.
-    aTile.mInvalidFront.MoveBy(-unscaledTileOrigin);
-
-    // This will be validated now.
-    aTile.mInvalidBack.SetEmpty();
-
-    nsIntRect bounds = tileRegion.GetBounds();
-    bounds.MoveBy(-aTileOrigin);
+    nsIntRegion tileValidRegion = GetValidRegion();
+    tileValidRegion.Or(tileValidRegion, aDirtyRegion);
+    // We only need to pad out if the tile has area that's not valid
+    if (!tileValidRegion.Contains(unscaledTile)) {
+      tileValidRegion = tileValidRegion.Intersect(unscaledTile);
+      // translate the region into tile space and pad
+      tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
+      PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
+    }
+  }
 
-    if (GetContentType() != gfxContentType::COLOR) {
-      drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height));
-    }
-
-    ctxt->NewPath();
-    ctxt->Clip(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height));
-    ctxt->SetMatrix(
-      ctxt->CurrentMatrix().Translate(-unscaledTileOrigin.x,
-                                      -unscaledTileOrigin.y).
-                            Scale(mResolution, mResolution));
-    mCallback(mPaintedLayer, ctxt,
-              tileRegion.GetBounds(),
-              DrawRegionClip::NONE,
-              nsIntRegion(), mCallbackData);
-
-  }
+  // The new buffer is now validated, remove the dirty region from it.
+  aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
+                         offsetScaledDirtyRegion);
 
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution,
                    aTileOrigin.y * mPresShellResolution, GetTileLength(), GetTileLength());
 #endif
 
   ctxt = nullptr;
   drawTarget = nullptr;
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -440,22 +440,16 @@ protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
                           const nsIntRegion& dirtyRect);
 
   void PostValidate(const nsIntRegion& aPaintRegion);
 
   void UnlockTile(TileClient aTile);
 
-  // If this returns true, we perform the paint operation into a single large
-  // buffer and copy it out to the tiles instead of calling PaintThebes() on
-  // each tile individually. Somewhat surprisingly, this turns out to be faster
-  // on Android.
-  bool UseSinglePaintBuffer() { return !gfxPrefs::PerTileDrawing(); }
-
   void ReleaseTile(TileClient aTile) { aTile.Release(); }
 
   void SwapTiles(TileClient& aTileA, TileClient& aTileB) { std::swap(aTileA, aTileB); }
 
   TileClient GetPlaceholderTile() const { return TileClient(); }
 
 private:
   gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -866,17 +866,17 @@ AsyncCompositionManager::TransformScroll
   LayerMargin fixedLayerMargins(0, 0, 0, 0);
   ScreenPoint offset(0, 0);
 
   // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics)
   // but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this
   // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios,
   // however, we can assume there is no async zooming in progress and so the following statement
   // works fine.
-  CSSToParentLayerScale userZoom(metrics.mDevPixelsPerCSSPixel * metrics.mCumulativeResolution * LayerToParentLayerScale(1));
+  CSSToParentLayerScale userZoom(metrics.GetDevPixelsPerCSSPixel() * metrics.mCumulativeResolution * LayerToParentLayerScale(1));
   ParentLayerPoint userScroll = metrics.GetScrollOffset() * userZoom;
   SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated,
                    userScroll, userZoom, fixedLayerMargins,
                    offset);
   mLayersUpdated = false;
 
   // Apply the render offset
   mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -109,16 +109,17 @@ EXPORTS.mozilla.layers += [
     'apz/public/GeckoContentController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZCTreeManager.h',
     'apz/testutil/APZTestData.h',
     'apz/util/ActiveElementManager.h',
     'apz/util/APZCCallbackHelper.h',
     'apz/util/ChromeProcessController.h',
+    'apz/util/InputAPZContext.h',
     'AtomicRefCountedWithFinalize.h',
     'AxisPhysicsModel.h',
     'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
     'client/CanvasClient.h',
     'client/CompositableClient.h',
@@ -241,16 +242,17 @@ UNIFIED_SOURCES += [
     'apz/src/InputBlockState.cpp',
     'apz/src/InputQueue.cpp',
     'apz/src/OverscrollHandoffState.cpp',
     'apz/src/TaskThrottler.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
     'apz/util/ChromeProcessController.cpp',
+    'apz/util/InputAPZContext.cpp',
     'AxisPhysicsModel.cpp',
     'AxisPhysicsMSDModel.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
     'basic/BasicLayerManager.cpp',
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -817,17 +817,17 @@ TEST_F(APZCBasicTester, ComplexTransform
   FrameMetrics metrics;
   metrics.mCompositionBounds = ParentLayerRect(0, 0, 24, 24);
   metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
   metrics.SetScrollOffset(CSSPoint(10, 10));
   metrics.mScrollableRect = CSSRect(0, 0, 50, 50);
   metrics.mCumulativeResolution = LayoutDeviceToLayerScale(2);
   metrics.mPresShellResolution = 2.0f;
   metrics.SetZoom(CSSToParentLayerScale(6));
-  metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3);
+  metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3));
   metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
 
   FrameMetrics childMetrics = metrics;
   childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1);
 
   layers[0]->SetFrameMetrics(metrics);
   layers[1]->SetFrameMetrics(childMetrics);
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -275,17 +275,16 @@ private:
   // 0 is "no change" for contrast, positive values increase it, negative values
   // decrease it until we hit mid gray at -1 contrast, after that it gets weird.
   DECL_GFX_PREF(Live, "layers.effect.contrast",                LayersEffectContrast, float, 0.0f);
   DECL_GFX_PREF(Live, "layers.effect.grayscale",               LayersEffectGrayscale, bool, false);
   DECL_GFX_PREF(Live, "layers.effect.invert",                  LayersEffectInvert, bool, false);
 
   DECL_GFX_PREF(Once, "layers.enable-tiles",                   LayersTilesEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layers.force-enable-tiles",             LayersTilesForceEnabled, bool, false);
-  DECL_GFX_PREF(Once, "layers.force-per-tile-drawing",         PerTileDrawing, bool, false);
   DECL_GFX_PREF(Once, "layers.tiled-drawtarget.enabled",       TiledDrawTargetEnabled, bool, false);
   // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
   DECL_GFX_PREF(Once, "layers.tile-max-pool-size",             LayersTileMaxPoolSize, uint32_t, (uint32_t)50);
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -61,17 +61,16 @@ UNIFIED_SOURCES += [
     'src/base/tracked_objects.cc',
     'src/chrome/common/child_process.cc',
     'src/chrome/common/child_process_host.cc',
     'src/chrome/common/child_process_info.cc',
     'src/chrome/common/child_thread.cc',
     'src/chrome/common/chrome_switches.cc',
     'src/chrome/common/env_vars.cc',
     'src/chrome/common/ipc_channel_proxy.cc',
-    'src/chrome/common/ipc_logging.cc',
     'src/chrome/common/ipc_message.cc',
     'src/chrome/common/ipc_sync_channel.cc',
     'src/chrome/common/ipc_sync_message.cc',
     'src/chrome/common/message_router.cc',
     'src/chrome/common/notification_service.cc',
 ]
 
 if os_win:
--- a/ipc/chromium/src/chrome/common/child_process_host.cc
+++ b/ipc/chromium/src/chrome/common/child_process_host.cc
@@ -9,17 +9,16 @@
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "base/singleton.h"
 #include "base/waitable_event.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/ipc/Transport.h"
 typedef mozilla::ipc::BrowserProcessSubThread ChromeThread;
-#include "chrome/common/ipc_logging.h"
 #include "chrome/common/notification_service.h"
 #include "chrome/common/notification_type.h"
 #include "chrome/common/process_watcher.h"
 #include "chrome/common/result_codes.h"
 
 using mozilla::ipc::FileDescriptor;
 
 namespace {
@@ -151,41 +150,27 @@ void ChildProcessHost::OnWaitableEventSi
 }
 
 ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host)
     : host_(host) {
 }
 
 void ChildProcessHost::ListenerHook::OnMessageReceived(
     const IPC::Message& msg) {
-#ifdef IPC_MESSAGE_LOG_ENABLED
-  IPC::Logging* logger = IPC::Logging::current();
-  if (msg.type() == IPC_LOGGING_ID) {
-    logger->OnReceivedLoggingMessage(msg);
-    return;
-  }
-
-  if (logger->Enabled())
-    logger->OnPreDispatchMessage(msg);
-#endif
 
   bool msg_is_ok = true;
   bool handled = false;
 
   if (!handled) {
       host_->OnMessageReceived(msg);
   }
 
   if (!msg_is_ok)
     base::KillProcess(host_->handle(), ResultCodes::KILLED_BAD_MESSAGE, false);
 
-#ifdef IPC_MESSAGE_LOG_ENABLED
-  if (logger->Enabled())
-    logger->OnPostDispatchMessage(msg, host_->channel_id_);
-#endif
 }
 
 void ChildProcessHost::ListenerHook::OnChannelConnected(int32_t peer_pid) {
   host_->opening_channel_ = false;
   host_->OnChannelConnected(peer_pid);
 
   // Notify in the main loop of the connection.
   host_->Notify(NotificationType(NotificationType::CHILD_PROCESS_HOST_CONNECTED));
--- a/ipc/chromium/src/chrome/common/child_thread.cc
+++ b/ipc/chromium/src/chrome/common/child_thread.cc
@@ -3,17 +3,16 @@
 // found in the LICENSE file.
 
 #include "chrome/common/child_thread.h"
 
 #include "base/string_util.h"
 #include "base/command_line.h"
 #include "chrome/common/child_process.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/ipc_logging.h"
 
 // V8 needs a 1MB stack size.
 const size_t ChildThread::kV8StackSize = 1024 * 1024;
 
 ChildThread::ChildThread(Thread::Options options)
     : Thread("Chrome_ChildThread"),
       owner_loop_(MessageLoop::current()),
       options_(options),
@@ -88,25 +87,19 @@ ChildThread* ChildThread::current() {
   return ChildProcess::current()->child_thread();
 }
 
 void ChildThread::Init() {
   channel_ = mozilla::MakeUnique<IPC::Channel>(channel_name_,
                                                IPC::Channel::MODE_CLIENT,
                                                this);
 
-#ifdef IPC_MESSAGE_LOG_ENABLED
-  IPC::Logging::current()->SetIPCSender(this);
-#endif
 }
 
 void ChildThread::CleanUp() {
-#ifdef IPC_MESSAGE_LOG_ENABLED
-  IPC::Logging::current()->SetIPCSender(NULL);
-#endif
   // Need to destruct the SyncChannel to the browser before we go away because
   // it caches a pointer to this thread.
   channel_ = nullptr;
 }
 
 void ChildThread::OnProcessFinalRelease() {
   if (!check_with_browser_before_shutdown_) {
     owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -21,17 +21,16 @@
 #include "base/eintr_wrapper.h"
 #include "base/lock.h"
 #include "base/logging.h"
 #include "base/process_util.h"
 #include "base/string_util.h"
 #include "base/singleton.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/file_descriptor_set_posix.h"
-#include "chrome/common/ipc_logging.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/UniquePtr.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracerImpl.h"
 using namespace mozilla::tasktracer;
 #endif
@@ -768,19 +767,16 @@ bool Channel::ChannelImpl::ProcessOutgoi
 
 bool Channel::ChannelImpl::Send(Message* message) {
 #ifdef IPC_MESSAGE_DEBUG_EXTRA
   DLOG(INFO) << "sending message @" << message << " on channel @" << this
              << " with type " << message->type()
              << " (" << output_queue_.size() << " in queue)";
 #endif
 
-#ifdef IPC_MESSAGE_LOG_ENABLED
-  Logging::current()->OnSendMessage(message, L"");
-#endif
 
   // If the channel has been closed, ProcessOutgoingMessages() is never going