Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 23 Jan 2017 11:42:26 +0100
changeset 330639 0b284d45431ef73097cb313ec9b1390bb5cbba24
parent 330616 4c7d2394a2adf1ad257fb080ebc31b78741245af (current diff)
parent 330638 36486fdc3813ef7943ae5b07b4128866d1938a6c (diff)
child 330640 c61b0e70d0cfadd25f31fe288dbe2fb18cc7da0f
push id36350
push usercbook@mozilla.com
push dateMon, 23 Jan 2017 10:42:40 +0000
treeherderautoland@0b284d45431e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/mediasink/DecodedStream.cpp
dom/media/mediasink/DecodedStream.h
--- a/browser/components/migration/tests/unit/head_migration.js
+++ b/browser/components/migration/tests/unit/head_migration.js
@@ -15,17 +15,18 @@ Cu.import("resource://gre/modules/Promis
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://testing-common/TestUtils.jsm");
 Cu.import("resource://testing-common/PlacesTestUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
-
+XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
+                                  "resource://gre/modules/Sqlite.jsm");
 // Initialize profile.
 var gProfD = do_get_profile();
 
 Cu.import("resource://testing-common/AppInfo.jsm"); /* globals updateAppInfo */
 updateAppInfo();
 
 /**
  * Migrates the requested resource and waits for the migration to be complete.
--- a/browser/components/migration/tests/unit/test_Chrome_passwords.js
+++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js
@@ -75,41 +75,22 @@ const TEST_LOGINS = [
     timesUsed: 1,
   },
 ];
 
 var crypto = new OSCrypto();
 var dbConn;
 
 function promiseSetPassword(login) {
-  return new Promise((resolve, reject) => {
-    let stmt = dbConn.createAsyncStatement(`
-      UPDATE logins
-      SET password_value = :password_value
-      WHERE rowid = :rowid
-    `);
-    let passwordValue = crypto.stringToArray(crypto.encryptData(login.password));
-    stmt.bindBlobByName("password_value", passwordValue, passwordValue.length);
-    stmt.params.rowid = login.id;
-
-    stmt.executeAsync({
-      handleError(aError) {
-        reject("Error with the query: " + aError.message);
-      },
-
-      handleCompletion(aReason) {
-        if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-          resolve();
-        } else {
-          reject("Query has failed: " + aReason);
-        }
-      },
-    });
-    stmt.finalize();
-  });
+  let passwordValue = crypto.stringToArray(crypto.encryptData(login.password));
+  return dbConn.execute(`UPDATE logins
+                         SET password_value = :password_value
+                         WHERE rowid = :rowid
+                        `, { password_value: passwordValue,
+                             rowid: login.id });
 }
 
 function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) {
   passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
 
   Assert.equal(passwordManagerLogin.username, chromeLogin.username,
                "The two logins ID " + id + " have the same username");
   Assert.equal(passwordManagerLogin.password, chromeLogin.password,
@@ -142,23 +123,23 @@ function generateDifferentLogin(login) {
   newLogin.timeCreated = login.timeCreated + 1;
   newLogin.timePasswordChanged = login.timePasswordChanged + 1;
   newLogin.timesUsed = login.timesUsed + 1;
   return newLogin;
 }
 
 add_task(function* setup() {
   let loginDataFile = do_get_file("AppData/Local/Google/Chrome/User Data/Default/Login Data");
-  dbConn = Services.storage.openUnsharedDatabase(loginDataFile);
+  dbConn = yield Sqlite.openConnection({ path: loginDataFile.path });
   registerFakePath("LocalAppData", do_get_file("AppData/Local/"));
 
   do_register_cleanup(() => {
     Services.logins.removeAllLogins();
-    dbConn.asyncClose();
     crypto.finalize();
+    return dbConn.close();
   });
 });
 
 add_task(function* test_importIntoEmptyDB() {
   for (let login of TEST_LOGINS) {
     yield promiseSetPassword(login);
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -64,16 +64,17 @@
 #include "nsIPrompt.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScrollableFrame.h"
+#include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
 #include "nsISeekableStream.h"
 #include "nsAutoPtr.h"
 #include "nsQueryObject.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsIAppShell.h"
 #include "nsWidgetsCID.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsView.h"
@@ -133,16 +134,17 @@
 #include "nsIController.h"
 #include "nsPICommandUpdater.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIWebBrowserChrome3.h"
 #include "nsITabChild.h"
 #include "nsISiteSecurityService.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsIStructuredCloneContainer.h"
+#include "nsISupportsPrimitives.h"
 #ifdef MOZ_PLACES
 #include "nsIFaviconService.h"
 #include "mozIPlacesPendingOperation.h"
 #include "mozIAsyncFavicons.h"
 #endif
 #include "nsINetworkPredictor.h"
 
 // Editor-related
@@ -9877,16 +9879,44 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
 
       // requestingElement docshell type = current docshell type.
       MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
                 "subframes should have the same docshell type as their parent");
 #endif
     }
 
+    // Since Content Policy checks are performed within docShell as well as
+    // the ContentSecurityManager we need a reliable way to let certain
+    // nsIContentPolicy consumers ignore duplicate calls. Let's use the 'extra'
+    // argument to pass a specific identifier.
+    nsCOMPtr<nsISupportsString> extraStr =
+      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_NAMED_LITERAL_STRING(msg, "conPolCheckFromDocShell");
+    rv = extraStr->SetData(msg);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+    rv = NS_CheckContentLoadPolicy(contentType,
+                                   aURI,
+                                   aTriggeringPrincipal,
+                                   requestingContext,
+                                   EmptyCString(),  // mime guess
+                                   extraStr,  // extra
+                                   &shouldLoad);
+  
+    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+      if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
+        return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
+      }
+
+      return NS_ERROR_CONTENT_BLOCKED;
+    }
+
     // If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
     // would block due to mixed content, go ahead and block here. If we try to
     // proceed with priming, we will error out later on.
     nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(requestingContext);
     // When loading toplevel windows, requestingContext can be null.  We don't
     // really care about HSTS in that situation, though; loads in toplevel
     // windows should all be browser UI.
     if (docShell) {
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_contentpolicy_block_window.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This window should never be openend!
+</body>
+</html>
--- a/docshell/test/navigation/mochitest.ini
+++ b/docshell/test/navigation/mochitest.ini
@@ -31,16 +31,17 @@ support-files =
   file_triggeringprincipal_parent_iframe_window_open_nav.html
   file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
   file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
   file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
   file_bug1300461.html
   file_bug1300461_redirect.html
   file_bug1300461_redirect.html^headers^
   file_bug1300461_back.html
+  file_contentpolicy_block_window.html
 
 [test_bug13871.html]
 [test_bug270414.html]
 [test_bug278916.html]
 [test_bug279495.html]
 [test_bug344861.html]
 skip-if = toolkit == "android" || toolkit == "windows" # disabled on Windows because of bug 1234520
 [test_bug386782.html]
@@ -57,8 +58,9 @@ skip-if = (toolkit == 'android') || (deb
 [test_sessionhistory.html]
 skip-if = toolkit == 'android' #RANDOM
 [test_sibling-matching-parent.html]
 [test_sibling-off-domain.html]
 [test_triggeringprincipal_frame_nav.html]
 [test_triggeringprincipal_window_open.html]
 [test_triggeringprincipal_parent_iframe_window_open.html]
 [test_triggeringprincipal_iframe_iframe_window_open.html]
+[test_contentpolicy_block_window.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/test_contentpolicy_block_window.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1329288
+-->
+<head>
+  <title>Test for Bug 1329288</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1329288">Mozilla Bug 1329288</a>
+
+
+<!-- have a testlink which we can use for the test to open a new window -->
+<a href="http://test1.example.org/tests/docshell/test/navigation/file_contentpolicy_block_window.html"
+   target="_blank"
+   id="testlink">This is a link</a>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * The test tries to open a new window and makes sure that a registered contentPolicy
+ * gets called with the right (a non null) 'context' for the TYPE_DOCUMENT load.
+ */
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager
+                                    .QueryInterface(Ci.nsIComponentRegistrar);
+
+// Content policy / factory implementation for the test
+var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
+var policyName = "@mozilla.org/testpolicy;1";
+var policy = {
+  // nsISupports implementation
+  QueryInterface: function(iid) {
+    iid = SpecialPowers.wrap(iid);
+    if (iid.equals(Ci.nsISupports) ||
+        iid.equals(Ci.nsIFactory) ||
+        iid.equals(Ci.nsIContentPolicy))
+      return this;
+    throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+  },
+
+  // nsIFactory implementation
+  createInstance: function(outer, iid) {
+    return this.QueryInterface(iid);
+  },
+
+  // nsIContentPolicy implementation
+  shouldLoad: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
+
+    if (SpecialPowers.wrap(contentLocation).spec !== document.getElementById("testlink").href) {
+      // not the URI we are looking for, allow the load
+      return Ci.nsIContentPolicy.ACCEPT;
+    }
+
+    is(contentType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+       "needs to be type document load");
+    ok(context, "context is not allowed to be null");
+    ok(context.name.endsWith("test_contentpolicy_block_window.html"),
+       "context should be the current window");
+
+    // remove the policy and finish test.
+    categoryManager.deleteCategoryEntry("content-policy", policyName, false);
+
+    setTimeout(function() {
+      // Component must be unregistered delayed, otherwise other content
+      // policy will not be removed from the category correctly
+      componentManager.unregisterFactory(policyID, policy);
+    }, 0);
+
+    SimpleTest.finish();
+    return Ci.nsIContentPolicy.REJECT_REQUEST;
+  },
+
+  shouldProcess: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
+    return Ci.nsIContentPolicy.ACCEPT;
+  }
+}
+
+policy = SpecialPowers.wrapCallbackObject(policy);
+componentManager.registerFactory(policyID, "Test content policy", policyName, policy);
+categoryManager.addCategoryEntry("content-policy", policyName, policyName, false, true);
+
+SimpleTest.waitForExplicitFinish();
+
+// now everything is set up, let's start the test
+document.getElementById("testlink").click()
+
+</script>
+</body>
+</html>
--- a/dom/base/Dispatcher.cpp
+++ b/dom/base/Dispatcher.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "mozilla/dom/Dispatcher.h"
+
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Move.h"
 #include "nsINamed.h"
 #include "nsQueryObject.h"
 
 using namespace mozilla;
 
 nsresult
 DispatcherTrait::Dispatch(const char* aName,
@@ -31,16 +33,23 @@ DispatcherTrait::Dispatch(const char* aN
 
 nsIEventTarget*
 DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
 {
   nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
   return main;
 }
 
+AbstractThread*
+DispatcherTrait::AbstractMainThreadFor(TaskCategory aCategory)
+{
+  // Return non DocGroup version by default.
+  return AbstractThread::MainThread();
+}
+
 namespace {
 
 #define NS_DISPATCHEREVENTTARGET_IID \
 { 0xbf4e36c8, 0x7d04, 0x4ef4, \
   { 0xbb, 0xd8, 0x11, 0x09, 0x0a, 0xdb, 0x4d, 0xf7 } }
 
 class DispatcherEventTarget final : public nsIEventTarget
 {
--- a/dom/base/Dispatcher.h
+++ b/dom/base/Dispatcher.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "nsISupports.h"
 
 class nsIEventTarget;
 class nsIRunnable;
 
 namespace mozilla {
+class AbstractThread;
 namespace dom {
 
 class TabGroup;
 class DocGroup;
 
 enum class TaskCategory {
   // User input (clicks, keypresses, etc.)
   UI,
@@ -55,30 +56,38 @@ public:
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable);
 
   // This method may or may not be safe off of the main thread. For nsIDocument
   // it is safe. For nsIGlobalWindow it is not safe. The nsIEventTarget can
   // always be used off the main thread.
   virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const;
+
+  // Must be called on the main thread. The AbstractThread can always be used
+  // off the main thread.
+  virtual AbstractThread* AbstractMainThreadFor(TaskCategory aCategory);
 };
 
 // Base class for DocGroup and TabGroup.
 class Dispatcher : public nsISupports {
 public:
   // This method is always safe to call off the main thread.
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) = 0;
 
   // This method is always safe to call off the main thread. The nsIEventTarget
   // can always be used off the main thread.
   virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const = 0;
 
+  // Must be called on the main thread. The AbstractThread can always be used
+  // off the main thread.
+  virtual AbstractThread* AbstractMainThreadFor(TaskCategory aCategory) = 0;
+
   // These methods perform a safe cast. They return null if |this| is not of the
   // requested type.
   virtual TabGroup* AsTabGroup() { return nullptr; }
 
 protected:
   virtual already_AddRefed<nsIEventTarget>
   CreateEventTargetFor(TaskCategory aCategory);
 
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -74,10 +74,17 @@ DocGroup::Dispatch(const char* aName,
 }
 
 nsIEventTarget*
 DocGroup::EventTargetFor(TaskCategory aCategory) const
 {
   return mTabGroup->EventTargetFor(aCategory);
 }
 
+AbstractThread*
+DocGroup::AbstractMainThreadFor(TaskCategory aCategory)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  return mTabGroup->AbstractMainThreadFor(aCategory);
+}
+
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -12,16 +12,17 @@
 #include "nsIPrincipal.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 
 #include "mozilla/dom/Dispatcher.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
+class AbstractThread;
 namespace dom {
 
 // Two browsing contexts are considered "related" if they are reachable from one
 // another through window.opener, window.parent, or window.frames. This is the
 // spec concept of a "unit of related browsing contexts"
 //
 // Two browsing contexts are considered "similar-origin" if they can be made to
 // have the same origin by setting document.domain. This is the spec concept of
@@ -71,16 +72,19 @@ public:
   }
 
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
+  virtual AbstractThread*
+  AbstractMainThreadFor(TaskCategory aCategory) override;
+
 private:
   DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
   ~DocGroup();
 
   nsCString mKey;
   RefPtr<TabGroup> mTabGroup;
   nsTArray<nsIDocument*> mDocuments;
 };
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1538,17 +1538,17 @@ Navigator::PublishServer(const nsAString
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
   ErrorResult result;
   RefPtr<Promise> domPromise = Promise::Create(global, result);
   if (result.Failed()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  mozPromise->Then(AbstractThread::MainThread(),
+  mozPromise->Then(global->AbstractMainThreadFor(TaskCategory::Other),
                    __func__,
                    [domPromise] (FlyWebPublishedServer* aServer) {
                      domPromise->MaybeResolve(aServer);
                    },
                    [domPromise] (nsresult aStatus) {
                      domPromise->MaybeReject(aStatus);
                    });
 
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -4,16 +4,17 @@
  * 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 "mozilla/dom/TabGroup.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/DocGroup.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "nsIDocShell.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIURI.h"
 
@@ -176,16 +177,17 @@ TabGroup::Leave(nsPIDOMWindowOuter* aWin
     mLastWindowLeft = true;
 
     // There is a RefPtr cycle TabGroup -> DispatcherEventTarget -> TabGroup. To
     // avoid leaks, we need to break the chain somewhere. We shouldn't be using
     // the ThrottledEventQueue for this TabGroup when no windows belong to it,
     // so it's safe to null out the queue here.
     for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
       mEventTargets[i] = nullptr;
+      mAbstractThreads[i] = nullptr;
     }
   }
 }
 
 nsresult
 TabGroup::FindItemWithName(const nsAString& aName,
                            nsIDocShellTreeItem* aRequestor,
                            nsIDocShellTreeItem* aOriginalRequestor,
@@ -261,24 +263,48 @@ TabGroup::Dispatch(const char* aName,
   } else {
     return NS_DispatchToMainThread(runnable.forget());
   }
 }
 
 nsIEventTarget*
 TabGroup::EventTargetFor(TaskCategory aCategory) const
 {
+  MOZ_ASSERT(aCategory != TaskCategory::Count);
   if (aCategory == TaskCategory::Worker || aCategory == TaskCategory::Timer) {
     MOZ_RELEASE_ASSERT(mThrottledQueuesInitialized || this == sChromeTabGroup);
   }
 
   if (NS_WARN_IF(mLastWindowLeft)) {
     // Once we've disconnected everything, we still allow people to
     // dispatch. We'll just go directly to the main thread.
     nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
     return main;
   }
 
   return mEventTargets[size_t(aCategory)];
 }
 
+AbstractThread*
+TabGroup::AbstractMainThreadFor(TaskCategory aCategory)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aCategory != TaskCategory::Count);
+
+  // The mEventTargets of the chrome TabGroup are all set to do_GetMainThread().
+  // We could just return AbstractThread::MainThread() without a wrapper.
+  // Once we've disconnected everything, we still allow people to dispatch.
+  // We'll just go directly to the main thread.
+  if (this == sChromeTabGroup || NS_WARN_IF(mLastWindowLeft)) {
+    return AbstractThread::MainThread();
+  }
+
+  if (!mAbstractThreads[size_t(aCategory)]) {
+    mAbstractThreads[size_t(aCategory)] =
+      AbstractThread::CreateEventTargetWrapper(mEventTargets[size_t(aCategory)],
+                                               /* aDrainDirectTasks = */ true);
+  }
+
+  return mAbstractThreads[size_t(aCategory)];
+}
+
 }
 }
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -13,16 +13,17 @@
 #include "nsTHashtable.h"
 #include "nsString.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/dom/Dispatcher.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
+class AbstractThread;
 class ThrottledEventQueue;
 namespace dom {
 
 // Two browsing contexts are considered "related" if they are reachable from one
 // another through window.opener, window.parent, or window.frames. This is the
 // spec concept of a "unit of related browsing contexts"
 //
 // Two browsing contexts are considered "similar-origin" if they can be made to
@@ -116,23 +117,27 @@ public:
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   // This method is always safe to call off the main thread. The nsIEventTarget
   // can always be used off the main thread.
   virtual nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
   TabGroup* AsTabGroup() override { return this; }
 
+  virtual AbstractThread*
+  AbstractMainThreadFor(TaskCategory aCategory) override;
+
 private:
   void EnsureThrottledEventQueues();
 
   ~TabGroup();
   DocGroupMap mDocGroups;
   Atomic<bool> mLastWindowLeft;
   nsTArray<nsPIDOMWindowOuter*> mWindows;
   Atomic<bool> mThrottledQueuesInitialized;
   nsCOMPtr<nsIEventTarget> mEventTargets[size_t(TaskCategory::Count)];
+  RefPtr<AbstractThread> mAbstractThreads[size_t(TaskCategory::Count)];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // defined(TabGroup_h)
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -158,28 +158,28 @@ using namespace mozilla::dom;
 // 2. Scriptable helper class
 // 3. nsIClassInfo/nsIXPCScriptable flags (i.e. for GetScriptableFlags)
 
 static nsDOMClassInfoData sClassInfoData[] = {
   // Base classes
 
   NS_DEFINE_CLASSINFO_DATA(DOMPrototype, nsDOMConstructorSH,
                            DOM_BASE_SCRIPTABLE_FLAGS |
-                           nsIXPCScriptable::WANT_PRECREATE |
-                           nsIXPCScriptable::WANT_RESOLVE |
-                           nsIXPCScriptable::WANT_HASINSTANCE |
-                           nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
+                           XPC_SCRIPTABLE_WANT_PRECREATE |
+                           XPC_SCRIPTABLE_WANT_RESOLVE |
+                           XPC_SCRIPTABLE_WANT_HASINSTANCE |
+                           XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE)
   NS_DEFINE_CLASSINFO_DATA(DOMConstructor, nsDOMConstructorSH,
                            DOM_BASE_SCRIPTABLE_FLAGS |
-                           nsIXPCScriptable::WANT_PRECREATE |
-                           nsIXPCScriptable::WANT_RESOLVE |
-                           nsIXPCScriptable::WANT_HASINSTANCE |
-                           nsIXPCScriptable::WANT_CALL |
-                           nsIXPCScriptable::WANT_CONSTRUCT |
-                           nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
+                           XPC_SCRIPTABLE_WANT_PRECREATE |
+                           XPC_SCRIPTABLE_WANT_RESOLVE |
+                           XPC_SCRIPTABLE_WANT_HASINSTANCE |
+                           XPC_SCRIPTABLE_WANT_CALL |
+                           XPC_SCRIPTABLE_WANT_CONSTRUCT |
+                           XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE)
 
   // Misc Core related classes
 
   // XUL classes
 #ifdef MOZ_XUL
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCommandDispatcher, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
@@ -198,23 +198,23 @@ static nsDOMClassInfoData sClassInfoData
 
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULTreeBuilder, nsDOMGenericSH,
                                       DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager,
                                        nsMessageManagerSH<nsEventTargetSH>,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                                       nsIXPCScriptable::WANT_ENUMERATE |
-                                       nsIXPCScriptable::IS_GLOBAL_OBJECT)
+                                       XPC_SCRIPTABLE_WANT_ENUMERATE |
+                                       XPC_SCRIPTABLE_IS_GLOBAL_OBJECT)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentProcessMessageManager,
                                        nsMessageManagerSH<nsDOMGenericSH>,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                                       nsIXPCScriptable::WANT_ENUMERATE |
-                                       nsIXPCScriptable::IS_GLOBAL_OBJECT)
+                                       XPC_SCRIPTABLE_WANT_ENUMERATE |
+                                       XPC_SCRIPTABLE_IS_GLOBAL_OBJECT)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControlElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2896,16 +2896,26 @@ nsIEventTarget*
 nsIDocument::EventTargetFor(TaskCategory aCategory) const
 {
   if (mDocGroup) {
     return mDocGroup->EventTargetFor(aCategory);
   }
   return DispatcherTrait::EventTargetFor(aCategory);
 }
 
+AbstractThread*
+nsIDocument::AbstractMainThreadFor(mozilla::dom::TaskCategory aCategory)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mDocGroup) {
+    return mDocGroup->AbstractMainThreadFor(aCategory);
+  }
+  return DispatcherTrait::AbstractMainThreadFor(aCategory);
+}
+
 void
 nsIDocument::NoteScriptTrackingStatus(const nsACString& aURL, bool aIsTracking)
 {
   if (aIsTracking) {
     mTrackingScripts.PutEntry(aURL);
   } else {
     MOZ_ASSERT(!mTrackingScripts.Contains(aURL));
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14290,16 +14290,26 @@ nsGlobalWindow::EventTargetFor(TaskCateg
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
     return GetDocGroup()->EventTargetFor(aCategory);
   }
   return DispatcherTrait::EventTargetFor(aCategory);
 }
 
+AbstractThread*
+nsGlobalWindow::AbstractMainThreadFor(TaskCategory aCategory)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  if (GetDocGroup()) {
+    return GetDocGroup()->AbstractMainThreadFor(aCategory);
+  }
+  return DispatcherTrait::AbstractMainThreadFor(aCategory);
+}
+
 nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
   nsGlobalWindow* topWindow = aWindow->GetScriptableTopInternal();
   if (!topWindow) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -98,16 +98,17 @@ class nsGlobalWindowObserver;
 class nsGlobalWindow;
 class nsDOMWindowUtils;
 class nsIIdleService;
 struct nsRect;
 
 class nsWindowSizes;
 
 namespace mozilla {
+class AbstractThread;
 class DOMEventTargetHelper;
 class ThrottledEventQueue;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
 class CustomElementRegistry;
@@ -1770,16 +1771,19 @@ public:
   // Dispatch a runnable related to the global.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::dom::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsIEventTarget*
   EventTargetFor(mozilla::dom::TaskCategory aCategory) const override;
 
+  virtual mozilla::AbstractThread*
+  AbstractMainThreadFor(mozilla::dom::TaskCategory aCategory) override;
+
 protected:
   // These members are only used on outer window objects. Make sure
   // you never set any of these on an inner object!
   bool                          mFullScreen : 1;
   bool                          mFullscreenMode : 1;
   bool                          mIsClosed : 1;
   bool                          mInClose : 1;
   // mHavePendingClose means we've got a termination function set to
--- a/dom/base/nsIDOMClassInfo.h
+++ b/dom/base/nsIDOMClassInfo.h
@@ -5,26 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIDOMClassInfo_h___
 #define nsIDOMClassInfo_h___
 
 #include "nsIXPCScriptable.h"
 
 #define DOM_BASE_SCRIPTABLE_FLAGS                                          \
-  (nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY |                          \
-   nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY |                          \
-   nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY |                          \
-   nsIXPCScriptable::ALLOW_PROP_MODS_TO_PROTOTYPE |                        \
-   nsIXPCScriptable::DONT_ASK_INSTANCE_FOR_SCRIPTABLE |                    \
-   nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES)
+  (XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |                             \
+   XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |                             \
+   XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY |                             \
+   XPC_SCRIPTABLE_ALLOW_PROP_MODS_TO_PROTOTYPE |                           \
+   XPC_SCRIPTABLE_DONT_ASK_INSTANCE_FOR_SCRIPTABLE |                       \
+   XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
 
 #define DEFAULT_SCRIPTABLE_FLAGS                                           \
   (DOM_BASE_SCRIPTABLE_FLAGS |                                             \
-   nsIXPCScriptable::WANT_RESOLVE |                                        \
-   nsIXPCScriptable::WANT_PRECREATE)
+   XPC_SCRIPTABLE_WANT_RESOLVE |                                           \
+   XPC_SCRIPTABLE_WANT_PRECREATE)
 
 #define DOM_DEFAULT_SCRIPTABLE_FLAGS                                       \
   (DEFAULT_SCRIPTABLE_FLAGS |                                              \
-   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE |                           \
-   nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY)
+   XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE |                              \
+   XPC_SCRIPTABLE_CLASSINFO_INTERFACES_ONLY)
 
 #endif /* nsIDOMClassInfo_h___ */
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -97,16 +97,17 @@ class nsSMILAnimationController;
 class nsTextNode;
 class nsWindowSizes;
 class nsDOMCaretPosition;
 class nsViewportInfo;
 class nsIGlobalObject;
 struct nsCSSSelectorList;
 
 namespace mozilla {
+class AbstractThread;
 class CSSStyleSheet;
 class ErrorResult;
 class EventStates;
 class PendingAnimationTracker;
 class StyleSetHandle;
 class SVGAttrAnimationRuleProcessor;
 template<typename> class OwningNonNull;
 
@@ -2878,16 +2879,19 @@ public:
   // Dispatch a runnable related to the document.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::dom::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsIEventTarget*
   EventTargetFor(mozilla::dom::TaskCategory aCategory) const override;
 
+  virtual mozilla::AbstractThread*
+  AbstractMainThreadFor(mozilla::dom::TaskCategory aCategory) override;
+
   // The URLs passed to these functions should match what
   // JS::DescribeScriptedCaller() returns, since these APIs are used to
   // determine whether some code is being called from a tracking script.
   void NoteScriptTrackingStatus(const nsACString& aURL, bool isTracking);
   bool IsScriptTracking(const nsACString& aURL) const;
 
   bool PrerenderHref(nsIURI* aHref);
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -651,17 +651,17 @@ skip-if = (toolkit == 'android') # Andro
 [test_iframe_referrer.html]
 [test_iframe_referrer_changing.html]
 [test_iframe_referrer_invalid.html]
 [test_Image_constructor.html]
 [test_img_referrer.html]
 [test_innersize_scrollport.html]
 [test_integer_attr_with_leading_zero.html]
 [test_intersectionobservers.html]
-skip-if = (os == "android") # Timing issues
+skip-if = true # Track Bug 1320704
 [test_link_prefetch.html]
 skip-if = !e10s # Track Bug 1281415
 [test_link_stylesheet.html]
 [test_messagemanager_targetchain.html]
 [test_meta_viewport0.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport1.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
--- a/dom/flyweb/FlyWebPublishedServer.cpp
+++ b/dom/flyweb/FlyWebPublishedServer.cpp
@@ -2,24 +2,26 @@
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/FlyWebPublishBinding.h"
 #include "mozilla/dom/FlyWebService.h"
+#include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/FlyWebServerEvents.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsGlobalWindow.h"
 #include "WebSocketChannel.h"
 
 namespace mozilla {
@@ -163,17 +165,17 @@ FlyWebPublishedServer::OnWebSocketAccept
 /******** FlyWebPublishedServerImpl ********/
 
 NS_IMPL_ISUPPORTS_INHERITED0(FlyWebPublishedServerImpl, mozilla::DOMEventTargetHelper)
 
 FlyWebPublishedServerImpl::FlyWebPublishedServerImpl(nsPIDOMWindowInner* aOwner,
                                                      const nsAString& aName,
                                                      const FlyWebPublishOptions& aOptions)
   : FlyWebPublishedServer(aOwner, aName, aOptions)
-  , mHttpServer(new HttpServer())
+  , mHttpServer(new HttpServer(aOwner->GetDocGroup()->AbstractMainThreadFor(TaskCategory::Other)))
 {
   LOG_I("FlyWebPublishedServerImpl::FlyWebPublishedServerImpl(%p)", this);
 }
 
 void
 FlyWebPublishedServerImpl::PermissionGranted(bool aGranted)
 {
   LOG_I("FlyWebPublishedServerImpl::PermissionGranted(%b)", aGranted);
@@ -481,16 +483,17 @@ FlyWebPublishedServerParent::FlyWebPubli
   if (!mozPromise) {
     Unused << SendServerReady(NS_ERROR_FAILURE);
     return;
   }
 
   RefPtr<FlyWebPublishedServerParent> self = this;
 
   mozPromise->Then(
+    // Non DocGroup-version of AbstractThread::MainThread() for the task in parent.
     AbstractThread::MainThread(),
     __func__,
     [this, self] (FlyWebPublishedServer* aServer) {
       mPublishedServer = static_cast<FlyWebPublishedServerImpl*>(aServer);
       if (mActorDestroyed) {
         mPublishedServer->Close();
         return;
       }
--- a/dom/flyweb/HttpServer.cpp
+++ b/dom/flyweb/HttpServer.cpp
@@ -31,19 +31,20 @@ static LazyLogModule gHttpServerLog("Htt
 #undef LOG_E
 #define LOG_E(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Error, (__VA_ARGS__))
 
 
 NS_IMPL_ISUPPORTS(HttpServer,
                   nsIServerSocketListener,
                   nsILocalCertGetCallback)
 
-HttpServer::HttpServer()
+HttpServer::HttpServer(AbstractThread* aMainThread)
   : mPort()
   , mHttps()
+  , mAbstractMainThread(aMainThread)
 {
 }
 
 HttpServer::~HttpServer()
 {
 }
 
 void
@@ -1248,17 +1249,17 @@ HttpServer::Connection::OnOutputStreamRe
       mOutputCopy =
         StreamCopier::Copy(mOutputBuffers[0].mStream,
                            mOutput,
                            mOutputBuffers[0].mChunked);
 
       RefPtr<Connection> self = this;
 
       mOutputCopy->
-        Then(AbstractThread::MainThread(),
+        Then(mServer->mAbstractMainThread,
              __func__,
              [self, this] (nsresult aStatus) {
                MOZ_ASSERT(mOutputBuffers[0].mStream);
                LOG_V("HttpServer::Connection::OnOutputStreamReady(%p) - "
                      "Sent body. Status 0x%lx",
                      this, aStatus);
 
                mOutputBuffers.RemoveElementAt(0);
--- a/dom/flyweb/HttpServer.h
+++ b/dom/flyweb/HttpServer.h
@@ -16,16 +16,19 @@
 #include "nsIRequestObserver.h"
 #include "mozilla/MozPromise.h"
 #include "nsITransportProvider.h"
 #include "nsILocalCertService.h"
 
 class nsIX509Cert;
 
 namespace mozilla {
+
+class AbstractThread;
+
 namespace dom {
 
 extern bool
 ContainsToken(const nsCString& aList, const nsCString& aToken);
 
 class InternalRequest;
 class InternalResponse;
 
@@ -41,17 +44,17 @@ public:
   virtual void OnWebSocket(InternalRequest* aConnectRequest) = 0;
   virtual void OnServerClose() = 0;
 };
 
 class HttpServer final : public nsIServerSocketListener,
                          public nsILocalCertGetCallback
 {
 public:
-  HttpServer();
+  explicit HttpServer(AbstractThread* aMainThread);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVERSOCKETLISTENER
   NS_DECL_NSILOCALCERTGETCALLBACK
 
   void Init(int32_t aPort, bool aHttps, HttpServerListener* aListener);
 
   void SendResponse(InternalRequest* aRequest, InternalResponse* aResponse);
@@ -180,14 +183,16 @@ private:
   RefPtr<HttpServerListener> mListener;
   nsCOMPtr<nsIServerSocket> mServerSocket;
   nsCOMPtr<nsIX509Cert> mCert;
 
   nsTArray<RefPtr<Connection>> mConnections;
 
   int32_t mPort;
   bool mHttps;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HttpServer_h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3588,17 +3588,18 @@ private:
   HTMLMediaElement* mWeak = nullptr;
   Phase mPhase = Phase::Init;
 };
 
 NS_IMPL_ISUPPORTS(HTMLMediaElement::ShutdownObserver, nsIObserver)
 
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
-    mWatchManager(this, AbstractThread::MainThread()),
+    mAbstractMainThread(OwnerDoc()->AbstractMainThreadFor(TaskCategory::Other)),
+    mWatchManager(this, mAbstractMainThread),
     mSrcStreamTracksAvailable(false),
     mSrcStreamPausedCurrentTime(-1),
     mShutdownObserver(new ShutdownObserver),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
@@ -4652,17 +4653,17 @@ nsresult HTMLMediaElement::FinishDecoder
       return NS_ERROR_FAILURE;
     }
   }
 
   MediaEventSource<void>* waitingForKeyProducer = mDecoder->WaitingForKeyEvent();
   // Not every decoder will produce waitingForKey events, only add ones that can
   if (waitingForKeyProducer) {
     mWaitingForKeyListener = waitingForKeyProducer->Connect(
-      AbstractThread::MainThread(), this, &HTMLMediaElement::CannotDecryptWaitingForKey);
+      mAbstractMainThread, this, &HTMLMediaElement::CannotDecryptWaitingForKey);
   }
 
   if (mChannelLoader) {
     mChannelLoader->Done();
     mChannelLoader = nullptr;
   }
 
   AddMediaElementToURITable();
@@ -4687,24 +4688,25 @@ nsresult HTMLMediaElement::FinishDecoder
 
   return rv;
 }
 
 class HTMLMediaElement::StreamListener : public MediaStreamListener,
                                          public WatchTarget
 {
 public:
-  explicit StreamListener(HTMLMediaElement* aElement, const char* aName) :
+  StreamListener(HTMLMediaElement* aElement, const char* aName) :
     WatchTarget(aName),
     mElement(aElement),
     mHaveCurrentData(false),
     mBlocked(false),
     mFinished(false),
     mMutex(aName),
-    mPendingNotifyOutput(false)
+    mPendingNotifyOutput(false),
+    mAbstractMainThread(aElement->AbstractMainThread())
   {}
   void Forget()
   {
     mElement = nullptr;
     NotifyWatchers();
   }
 
   // Main thread
@@ -4756,47 +4758,49 @@ public:
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) override
   {
     nsCOMPtr<nsIRunnable> event;
     if (aBlocked == BLOCKED) {
       event = NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
       event = NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                       event.forget());
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) override
   {
     MutexAutoLock lock(mMutex);
-    nsCOMPtr<nsIRunnable> event =
-      NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+      mAbstractMainThread,
+      NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData));
   }
   virtual void NotifyOutput(MediaStreamGraph* aGraph,
                             GraphTime aCurrentTime) override
   {
     MutexAutoLock lock(mMutex);
     if (mPendingNotifyOutput)
       return;
     mPendingNotifyOutput = true;
-    nsCOMPtr<nsIRunnable> event =
-      NewRunnableMethod(this, &StreamListener::DoNotifyOutput);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+      mAbstractMainThread,
+      NewRunnableMethod(this, &StreamListener::DoNotifyOutput));
   }
 
 private:
   // These fields may only be accessed on the main thread
   HTMLMediaElement* mElement;
   bool mHaveCurrentData;
   bool mBlocked;
   bool mFinished;
 
   // mMutex protects the fields below; they can be accessed on any thread
   Mutex mMutex;
   bool mPendingNotifyOutput;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 class HTMLMediaElement::MediaStreamTracksAvailableCallback:
   public OnTracksAvailableCallback
 {
 public:
   explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement):
       OnTracksAvailableCallback(), mElement(aElement)
@@ -7151,16 +7155,24 @@ void
 HTMLMediaElement::UpdateCustomPolicyAfterPlayed()
 {
   OpenUnsupportedMediaWithExternalAppIfNeeded();
   if (mAudioChannelWrapper) {
     mAudioChannelWrapper->NotifyPlayStateChanged();
   }
 }
 
+AbstractThread*
+HTMLMediaElement::AbstractMainThread() const
+{
+  MOZ_ASSERT(mAbstractMainThread);
+
+  return mAbstractMainThread;
+}
+
 nsTArray<RefPtr<Promise>>
 HTMLMediaElement::TakePendingPlayPromises()
 {
   return Move(mPendingPlayPromises);
 }
 
 void
 HTMLMediaElement::NotifyAboutPlaying()
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -36,16 +36,17 @@
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 typedef uint32_t SuspendTypes;
 typedef uint32_t AudibleChangedReasons;
 typedef uint8_t AudibleState;
 
 namespace mozilla {
+class AbstractThread;
 class DecoderDoctorDiagnostics;
 class DOMMediaStream;
 class ErrorResult;
 class MediaResource;
 class MediaDecoder;
 class VideoFrameContainer;
 namespace dom {
 class MediaKeys;
@@ -1167,16 +1168,18 @@ protected:
 
   // Get the HTMLMediaElement object if the decoder is being used from an
   // HTML media element, and null otherwise.
   virtual HTMLMediaElement* GetMediaElement() final override
   {
     return this;
   }
 
+  virtual AbstractThread* AbstractMainThread() const final override;
+
   // Return true if decoding should be paused
   virtual bool GetPaused() final override
   {
     bool isPaused = false;
     GetPaused(&isPaused);
     return isPaused;
   }
 
@@ -1245,17 +1248,17 @@ protected:
   class nsResolveOrRejectPendingPlayPromisesRunner;
   using nsGenericHTMLElement::DispatchEvent;
   // For nsAsyncEventRunner.
   nsresult DispatchEvent(const nsAString& aName);
 
   // Open unsupported types media with the external app when the media element
   // triggers play() after loaded fail. eg. preload the data before start play.
   void OpenUnsupportedMediaWithExternalAppIfNeeded() const;
-  
+
   // This method moves the mPendingPlayPromises into a temperate object. So the
   // mPendingPlayPromises is cleared after this method call.
   nsTArray<RefPtr<Promise>> TakePendingPlayPromises();
 
   // This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
   // and queues a task to resolve them.
   void AsyncResolvePendingPlayPromises();
 
@@ -1268,16 +1271,19 @@ protected:
   void NotifyAboutPlaying();
 
   already_AddRefed<Promise> CreateDOMPromise(ErrorResult& aRv) const;
 
   // The current decoder. Load() has been called on this decoder.
   // At most one of mDecoder and mSrcStream can be non-null.
   RefPtr<MediaDecoder> mDecoder;
 
+  // The DocGroup-specific AbstractThread::MainThread() of this HTML element.
+  RefPtr<AbstractThread> mAbstractMainThread;
+
   // Observers listening to changes to the mDecoder principal.
   // Used by streams captured from this element.
   nsTArray<DecoderPrincipalChangeObserver*> mDecoderPrincipalChangeObservers;
 
   // State-watching manager.
   WatchManager<HTMLMediaElement> mWatchManager;
 
   // A reference to the VideoFrameContainer which contains the current frame
@@ -1696,17 +1702,17 @@ private:
   Visibility mVisibilityState;
 
   UniquePtr<ErrorSink> mErrorSink;
 
   // This wrapper will handle all audio channel related stuffs, eg. the operations
   // of tab audio indicator, Fennec's media control.
   // Note: mAudioChannelWrapper might be null after GC happened.
   RefPtr<AudioChannelAgentCallback> mAudioChannelWrapper;
-  
+
   // A list of pending play promises. The elements are pushed during the play()
   // method call and are resolved/rejected during further playback steps.
   nsTArray<RefPtr<Promise>> mPendingPlayPromises;
 
   // A list of already-dispatched but not yet run
   // nsResolveOrRejectPendingPlayPromisesRunners.
   // Runners whose Run() method is called remove themselves from this list.
   // We keep track of these because the load algorithm resolves/rejects all
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -20,16 +20,17 @@
 namespace mozilla
 {
 
 namespace layers
 {
   class ImageContainer;
   class KnowsCompositor;
 } // namespace layers
+class AbstractThread;
 class MediaResource;
 class ReentrantMonitor;
 class VideoFrameContainer;
 class MediaDecoderOwner;
 class CDMProxy;
 class GMPCrashHelper;
 
 typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
@@ -85,16 +86,19 @@ public:
 
   // Return an event that will be notified when a decoder is waiting for a
   // decryption key before it can return more output.
   virtual MediaEventSource<void>* WaitingForKeyEvent()
   {
     return nullptr;
   }
 
+  // Return an abstract thread on which to run main thread runnables.
+  virtual AbstractThread* AbstractMainThread() const = 0;
+
 protected:
   virtual void UpdateEstimatedMediaDuration(int64_t aDuration) {};
 public:
   void DispatchUpdateEstimatedMediaDuration(int64_t aDuration)
   {
     NS_DispatchToMainThread(NewRunnableMethod<int64_t>(this,
                                                        &AbstractMediaDecoder::UpdateEstimatedMediaDuration,
                                                        aDuration));
--- a/dom/media/AudioCaptureStream.cpp
+++ b/dom/media/AudioCaptureStream.cpp
@@ -25,18 +25,18 @@ using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 namespace mozilla
 {
 
 // We are mixing to mono until PeerConnection can accept stereo
 static const uint32_t MONO = 1;
 
-AudioCaptureStream::AudioCaptureStream(TrackID aTrackId)
-  : ProcessedMediaStream(), mTrackId(aTrackId), mStarted(false), mTrackCreated(false)
+AudioCaptureStream::AudioCaptureStream(TrackID aTrackId, AbstractThread* aMainThread)
+  : ProcessedMediaStream(aMainThread), mTrackId(aTrackId), mStarted(false), mTrackCreated(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(AudioCaptureStream);
   mMixer.AddCallback(this);
 }
 
 AudioCaptureStream::~AudioCaptureStream()
 {
--- a/dom/media/AudioCaptureStream.h
+++ b/dom/media/AudioCaptureStream.h
@@ -9,26 +9,27 @@
 #include "MediaStreamGraph.h"
 #include "AudioMixer.h"
 #include "StreamTracks.h"
 #include <algorithm>
 
 namespace mozilla
 {
 
+class AbstractThread;
 class DOMMediaStream;
 
 /**
  * See MediaStreamGraph::CreateAudioCaptureStream.
  */
 class AudioCaptureStream : public ProcessedMediaStream,
                            public MixerCallbackReceiver
 {
 public:
-  explicit AudioCaptureStream(TrackID aTrackId);
+  AudioCaptureStream(TrackID aTrackId, AbstractThread* aMainThread);
   virtual ~AudioCaptureStream();
 
   void Start();
 
   void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
 protected:
   void MixerCallback(AudioDataValue* aMixedBuffer, AudioSampleFormat aFormat,
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -349,20 +349,19 @@ AudioStream::Init(uint32_t aNumChannels,
 
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
     NS_WARNING("Can't get cubeb context!");
     CubebUtils::ReportCubebStreamInitFailure(true);
     return NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR;
   }
 
-  cubeb_channel_layout layout;
-  int r = cubeb_get_preferred_channel_layout(cubebContext, &layout);
-  MOZ_ASSERT(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
-  params.layout = (r == CUBEB_OK) ? layout : CUBEB_LAYOUT_UNDEFINED;
+  // The DecodedAudioDataSink forces mono or stereo for now.
+  params.layout = params.channels == 1 ? CUBEB_LAYOUT_MONO
+                                       : CUBEB_LAYOUT_STEREO;
 
   return OpenCubeb(cubebContext, params, startTime, CubebUtils::GetFirstStream());
 }
 
 nsresult
 AudioStream::OpenCubeb(cubeb* aContext, cubeb_stream_params& aParams,
                        TimeStamp aStartTime, bool aIsFirst)
 {
--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Benchmark.h"
 #include "BufferMediaResource.h"
 #include "MediaData.h"
 #include "MediaPrefs.h"
 #include "PDMFactory.h"
 #include "WebMDemuxer.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/ContentChild.h"
 
 #ifndef MOZ_WIDGET_ANDROID
 #include "WebMSample.h"
 #endif
 
@@ -52,16 +53,17 @@ VP9Benchmark::IsVP9DecodeFast()
                     {
                       Preferences::GetInt("media.benchmark.frames", 300), // frames to measure
                       1, // start benchmarking after decoding this frame.
                       8, // loop after decoding that many frames.
                       TimeDuration::FromMilliseconds(
                         Preferences::GetUint("media.benchmark.timeout", 1000))
                     });
     estimiser->Run()->Then(
+      // Non-DocGroup version of AbstractThread::MainThread for utility function.
       AbstractThread::MainThread(), __func__,
       [](uint32_t aDecodeFps) {
         if (XRE_IsContentProcess()) {
           dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
           if (contentChild) {
             contentChild->SendNotifyBenchmarkResult(NS_LITERAL_STRING("VP9"),
                                                     aDecodeFps);
           }
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -4,16 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMMediaStream.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIScriptError.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"
+#include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackEvent.h"
 #include "mozilla/dom/LocalMediaStreamBinding.h"
 #include "mozilla/dom/AudioNode.h"
 #include "AudioChannelAgent.h"
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/VideoTrack.h"
@@ -133,16 +134,17 @@ NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTr
  * Listener registered on the Owned stream to detect added and ended owned
  * tracks for keeping the list of MediaStreamTracks in sync with the tracks
  * added and ended directly at the source.
  */
 class DOMMediaStream::OwnedStreamListener : public MediaStreamListener {
 public:
   explicit OwnedStreamListener(DOMMediaStream* aStream)
     : mStream(aStream)
+    , mAbstractMainThread(aStream->mAbstractMainThread)
   {}
 
   void Forget() { mStream = nullptr; }
 
   void DoNotifyTrackCreated(TrackID aTrackID, MediaSegment::Type aType,
                             MediaStream* aInputStream, TrackID aInputTrackID)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -207,44 +209,47 @@ public:
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
     if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
-      nsCOMPtr<nsIRunnable> runnable =
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+        mAbstractMainThread,
         NewRunnableMethod<TrackID, MediaSegment::Type, RefPtr<MediaStream>, TrackID>(
           this, &OwnedStreamListener::DoNotifyTrackCreated,
-          aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+          aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID));
     } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
-      nsCOMPtr<nsIRunnable> runnable =
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+        mAbstractMainThread,
         NewRunnableMethod<RefPtr<MediaStream>, TrackID, TrackID>(
           this, &OwnedStreamListener::DoNotifyTrackEnded,
-          aInputStream, aInputTrackID, aID);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+          aInputStream, aInputTrackID, aID));
     }
   }
 
 private:
   // These fields may only be accessed on the main thread
   DOMMediaStream* mStream;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 /**
  * Listener registered on the Playback stream to detect when tracks end and when
  * all new tracks this iteration have been created - for when several tracks are
  * queued by the source and committed all at once.
  */
 class DOMMediaStream::PlaybackStreamListener : public MediaStreamListener {
 public:
   explicit PlaybackStreamListener(DOMMediaStream* aStream)
     : mStream(aStream)
+    , mAbstractMainThread(aStream->mAbstractMainThread)
   {}
 
   void Forget()
   {
     MOZ_ASSERT(NS_IsMainThread());
     mStream = nullptr;
   }
 
@@ -274,34 +279,37 @@ public:
     NS_DispatchToMainThread(NewRunnableMethod(
       mStream, &DOMMediaStream::NotifyFinished));
   }
 
   // The methods below are called on the MediaStreamGraph thread.
 
   void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
   {
-    nsCOMPtr<nsIRunnable> runnable =
-      NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+      mAbstractMainThread,
+      NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation));
   }
 
 
   void NotifyEvent(MediaStreamGraph* aGraph,
                    MediaStreamGraphEvent event) override
   {
     if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+        mAbstractMainThread,
         NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinished));
     }
   }
 
 private:
   // These fields may only be accessed on the main thread
   DOMMediaStream* mStream;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 class DOMMediaStream::PlaybackTrackListener : public MediaStreamTrackConsumer
 {
 public:
   explicit PlaybackTrackListener(DOMMediaStream* aStream) :
     mStream(aStream) {}
 
@@ -393,17 +401,18 @@ NS_INTERFACE_MAP_END_INHERITING(DOMMedia
 
 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
                                MediaStreamTrackSourceGetter* aTrackSourceGetter)
   : mLogicalStreamStartTime(0), mWindow(aWindow),
     mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
     mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
     mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)),
     mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
-    mActive(false), mSetInactiveOnFinish(false)
+    mActive(false), mSetInactiveOnFinish(false),
+    mAbstractMainThread(aWindow->GetDocGroup()->AbstractMainThreadFor(TaskCategory::Other))
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
 
   if (NS_SUCCEEDED(rv) && uuidgen) {
     nsID uuid;
     memset(&uuid, 0, sizeof(uuid));
@@ -837,39 +846,39 @@ void
 DOMMediaStream::SetInactiveOnFinish()
 {
   mSetInactiveOnFinish = true;
 }
 
 void
 DOMMediaStream::InitSourceStream(MediaStreamGraph* aGraph)
 {
-  InitInputStreamCommon(aGraph->CreateSourceStream(), aGraph);
+  InitInputStreamCommon(aGraph->CreateSourceStream(mAbstractMainThread), aGraph);
   InitOwnedStreamCommon(aGraph);
   InitPlaybackStreamCommon(aGraph);
 }
 
 void
 DOMMediaStream::InitTrackUnionStream(MediaStreamGraph* aGraph)
 {
-  InitInputStreamCommon(aGraph->CreateTrackUnionStream(), aGraph);
+  InitInputStreamCommon(aGraph->CreateTrackUnionStream(mAbstractMainThread), aGraph);
   InitOwnedStreamCommon(aGraph);
   InitPlaybackStreamCommon(aGraph);
 }
 
 void
 DOMMediaStream::InitAudioCaptureStream(nsIPrincipal* aPrincipal, MediaStreamGraph* aGraph)
 {
   const TrackID AUDIO_TRACK = 1;
 
   RefPtr<BasicTrackSource> audioCaptureSource =
     new BasicTrackSource(aPrincipal, MediaSourceEnum::AudioCapture);
 
   AudioCaptureStream* audioCaptureStream =
-    static_cast<AudioCaptureStream*>(aGraph->CreateAudioCaptureStream(AUDIO_TRACK));
+    static_cast<AudioCaptureStream*>(aGraph->CreateAudioCaptureStream(AUDIO_TRACK, mAbstractMainThread));
   InitInputStreamCommon(audioCaptureStream, aGraph);
   InitOwnedStreamCommon(aGraph);
   InitPlaybackStreamCommon(aGraph);
   RefPtr<MediaStreamTrack> track =
     CreateDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, audioCaptureSource);
   AddTrackInternal(track);
 
   audioCaptureStream->Start();
@@ -885,32 +894,32 @@ DOMMediaStream::InitInputStreamCommon(Me
   mInputStream->RegisterUser();
 }
 
 void
 DOMMediaStream::InitOwnedStreamCommon(MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(!mPlaybackStream, "Owned stream must be initialized before playback stream");
 
-  mOwnedStream = aGraph->CreateTrackUnionStream();
+  mOwnedStream = aGraph->CreateTrackUnionStream(mAbstractMainThread);
   mOwnedStream->SetAutofinish(true);
   mOwnedStream->RegisterUser();
   if (mInputStream) {
     mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
   }
 
   // Setup track listeners
   mOwnedListener = new OwnedStreamListener(this);
   mOwnedStream->AddListener(mOwnedListener);
 }
 
 void
 DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph)
 {
-  mPlaybackStream = aGraph->CreateTrackUnionStream();
+  mPlaybackStream = aGraph->CreateTrackUnionStream(mAbstractMainThread);
   mPlaybackStream->SetAutofinish(true);
   mPlaybackStream->RegisterUser();
   if (mOwnedStream) {
     mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
   }
 
   mPlaybackListener = new PlaybackStreamListener(this);
   mPlaybackStream->AddListener(mPlaybackListener);
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -21,16 +21,17 @@
 // X11 has a #define for CurrentTime. Unbelievable :-(.
 // See dom/media/webaudio/AudioContext.h for more fun!
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
 namespace mozilla {
 
+class AbstractThread;
 class DOMHwMediaStream;
 class DOMLocalMediaStream;
 class DOMMediaStream;
 class MediaStream;
 class MediaInputPort;
 class DirectMediaStreamListener;
 class MediaStreamGraph;
 class ProcessedMediaStream;
@@ -584,16 +585,18 @@ public:
   // being destroyed, so we don't hold on to a dead pointer. Main thread only.
   void RegisterTrackListener(TrackListener* aListener);
 
   // Unregisters a track listener from this MediaStream. The caller must call
   // UnregisterTrackListener before being destroyed, so we don't hold on to
   // a dead pointer. Main thread only.
   void UnregisterTrackListener(TrackListener* aListener);
 
+  AbstractThread* AbstractMainThread() const { return mAbstractMainThread; }
+
 protected:
   virtual ~DOMMediaStream();
 
   void Destroy();
   void InitSourceStream(MediaStreamGraph* aGraph);
   void InitTrackUnionStream(MediaStreamGraph* aGraph);
   void InitAudioCaptureStream(nsIPrincipal* aPrincipal, MediaStreamGraph* aGraph);
 
@@ -748,16 +751,17 @@ private:
   // Principal identifying who may access the collected contents of this stream.
   // If null, this stream can be used by anyone because it has no content yet.
   nsCOMPtr<nsIPrincipal> mPrincipal;
   // Video principal is used by video element as access is requested to its
   // image data.
   nsCOMPtr<nsIPrincipal> mVideoPrincipal;
   nsTArray<dom::PrincipalChangeObserver<DOMMediaStream>*> mPrincipalChangeObservers;
   CORSMode mCORSMode;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMMediaStream,
                               NS_DOMMEDIASTREAM_IID)
 
 #define NS_DOMLOCALMEDIASTREAM_IID \
 { 0xb1437260, 0xec61, 0x4dfa, \
   { 0x92, 0x54, 0x04, 0x44, 0xe2, 0xb5, 0x94, 0x9c } }
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -634,20 +634,18 @@ AudioCallbackDriver::Init()
 
   output.channels = mGraphImpl->AudioChannelCount();
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
     output.format = CUBEB_SAMPLE_S16NE;
   } else {
     output.format = CUBEB_SAMPLE_FLOAT32NE;
   }
 
-  cubeb_channel_layout layout;
-  int r = cubeb_get_preferred_channel_layout(cubebContext, &layout);
-  MOZ_ASSERT(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
-  output.layout = (r == CUBEB_OK) ? layout : CUBEB_LAYOUT_UNDEFINED;
+  // Graphs are always stereo for now.
+  output.layout = CUBEB_LAYOUT_STEREO;
 
   Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
   if (latencyPref) {
     latency_frames = latencyPref.value();
   } else {
     if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
       NS_WARNING("Could not get minimal latency from cubeb.");
       return false;
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -18,16 +18,17 @@
 #include "nsError.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
 #include "nsComponentManagerUtils.h"
 #include <algorithm>
 #include "MediaShutdownManager.h"
 #include "AudioChannelService.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Telemetry.h"
@@ -134,16 +135,21 @@ MediaDecoder::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
 
 NS_IMPL_ISUPPORTS0(MediaDecoder)
 
+MediaDecoder::ResourceCallback::ResourceCallback(AbstractThread* aMainThread)
+  : mAbstractMainThread(aMainThread)
+{
+  MOZ_ASSERT(aMainThread);
+}
 
 void
 MediaDecoder::ResourceCallback::Connect(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mDecoder = aDecoder;
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
 }
@@ -188,17 +194,17 @@ void
 MediaDecoder::ResourceCallback::NotifyDecodeError()
 {
   RefPtr<ResourceCallback> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     if (self->mDecoder) {
       self->mDecoder->DecodeError(NS_ERROR_DOM_MEDIA_FATAL_ERR);
     }
   });
-  AbstractThread::MainThread()->Dispatch(r.forget());
+  mAbstractMainThread->Dispatch(r.forget());
 }
 
 /* static */ void
 MediaDecoder::ResourceCallback::TimerCallback(nsITimer* aTimer, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ResourceCallback* thiz = static_cast<ResourceCallback*>(aClosure);
   MOZ_ASSERT(thiz->mDecoder);
@@ -246,17 +252,17 @@ MediaDecoder::ResourceCallback::NotifyDa
         element->DownloadSuspended();
       }
       // NotifySuspendedStatusChanged will tell the element that download
       // has been suspended "by the cache", which is true since we never
       // download anything. The element can then transition to HAVE_ENOUGH_DATA.
       self->mDecoder->NotifySuspendedStatusChanged();
     }
   });
-  AbstractThread::MainThread()->Dispatch(r.forget());
+  mAbstractMainThread->Dispatch(r.forget());
 }
 
 void
 MediaDecoder::ResourceCallback::NotifyPrincipalChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mDecoder) {
     mDecoder->NotifyPrincipalChanged();
@@ -277,17 +283,17 @@ MediaDecoder::ResourceCallback::NotifyBy
                                                     int64_t aOffset)
 {
   RefPtr<ResourceCallback> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     if (self->mDecoder) {
       self->mDecoder->NotifyBytesConsumed(aBytes, aOffset);
     }
   });
-  AbstractThread::MainThread()->Dispatch(r.forget());
+  mAbstractMainThread->Dispatch(r.forget());
 }
 
 void
 MediaDecoder::NotifyOwnerActivityChanged(bool aIsVisible)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   SetElementVisibility(aIsVisible);
@@ -368,29 +374,30 @@ MediaDecoder::SetInfinite(bool aInfinite
 bool
 MediaDecoder::IsInfinite() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mInfiniteStream;
 }
 
 #define INIT_MIRROR(name, val) \
-  name(AbstractThread::MainThread(), val, "MediaDecoder::" #name " (Mirror)")
+  name(aOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Mirror)")
 #define INIT_CANONICAL(name, val) \
-  name(AbstractThread::MainThread(), val, "MediaDecoder::" #name " (Canonical)")
+  name(aOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Canonical)")
 
 MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
-  : mWatchManager(this, AbstractThread::MainThread())
+  : mWatchManager(this, aOwner->AbstractMainThread())
   , mLogicalPosition(0.0)
   , mDuration(std::numeric_limits<double>::quiet_NaN())
-  , mResourceCallback(new ResourceCallback())
+  , mResourceCallback(new ResourceCallback(aOwner->AbstractMainThread()))
   , mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__))
   , mIgnoreProgressData(false)
   , mInfiniteStream(false)
   , mOwner(aOwner)
+  , mAbstractMainThread(aOwner->AbstractMainThread())
   , mFrameStats(new FrameStatistics())
   , mVideoFrameContainer(aOwner->GetVideoFrameContainer())
   , mPlaybackStatistics(new MediaChannelStatistics())
   , mPinnedForSeek(false)
   , mMinimizePreroll(false)
   , mMediaTracksConstructed(false)
   , mFiredMetadataLoaded(false)
   , mElementVisible(!aOwner->IsHidden())
@@ -415,16 +422,17 @@ MediaDecoder::MediaDecoder(MediaDecoderO
   , INIT_CANONICAL(mPlaybackRateReliable, true)
   , INIT_CANONICAL(mDecoderPosition, 0)
   , INIT_CANONICAL(mIsVisible, !aOwner->IsHidden())
   , mTelemetryReported(false)
   , mIsMediaElement(!!aOwner->GetMediaElement())
   , mElement(aOwner->GetMediaElement())
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mAbstractMainThread);
   MediaMemoryTracker::AddMediaDecoder(this);
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
   mResourceCallback->Connect(this);
 
   //
   // Initialize watchers.
   //
@@ -481,28 +489,28 @@ MediaDecoder::Shutdown()
     mMetadataLoadedListener.Disconnect();
     mFirstFrameLoadedListener.Disconnect();
     mOnPlaybackEvent.Disconnect();
     mOnPlaybackErrorEvent.Disconnect();
     mOnDecoderDoctorEvent.Disconnect();
     mOnMediaNotSeekable.Disconnect();
 
     mDecoderStateMachine->BeginShutdown()
-      ->Then(AbstractThread::MainThread(), __func__, this,
+      ->Then(mAbstractMainThread, __func__, this,
              &MediaDecoder::FinishShutdown,
              &MediaDecoder::FinishShutdown);
   } else {
     // Ensure we always unregister asynchronously in order not to disrupt
     // the hashtable iterating in MediaShutdownManager::Shutdown().
     RefPtr<MediaDecoder> self = this;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
       self->mVideoFrameContainer = nullptr;
       MediaShutdownManager::Instance().Unregister(self);
     });
-    AbstractThread::MainThread()->Dispatch(r.forget());
+    mAbstractMainThread->Dispatch(r.forget());
   }
 
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
   if (mResource) {
     mResource->Close();
   }
 
@@ -632,30 +640,30 @@ MediaDecoder::InitializeStateMachine()
 void
 MediaDecoder::SetStateMachineParameters()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mPlaybackRate != 1 && mPlaybackRate != 0) {
     mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
   }
   mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::OnMetadataUpdate);
+    mAbstractMainThread, this, &MediaDecoder::OnMetadataUpdate);
   mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::MetadataLoaded);
+    mAbstractMainThread, this, &MediaDecoder::MetadataLoaded);
   mFirstFrameLoadedListener = mDecoderStateMachine->FirstFrameLoadedEvent().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::FirstFrameLoaded);
+    mAbstractMainThread, this, &MediaDecoder::FirstFrameLoaded);
 
   mOnPlaybackEvent = mDecoderStateMachine->OnPlaybackEvent().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackEvent);
+    mAbstractMainThread, this, &MediaDecoder::OnPlaybackEvent);
   mOnPlaybackErrorEvent = mDecoderStateMachine->OnPlaybackErrorEvent().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackErrorEvent);
+    mAbstractMainThread, this, &MediaDecoder::OnPlaybackErrorEvent);
   mOnDecoderDoctorEvent = mDecoderStateMachine->OnDecoderDoctorEvent().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::OnDecoderDoctorEvent);
+    mAbstractMainThread, this, &MediaDecoder::OnDecoderDoctorEvent);
   mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
-    AbstractThread::MainThread(), this, &MediaDecoder::OnMediaNotSeekable);
+    mAbstractMainThread, this, &MediaDecoder::OnMediaNotSeekable);
 }
 
 void
 MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
   mMinimizePreroll = true;
@@ -713,31 +721,31 @@ void
 MediaDecoder::AsyncResolveSeekDOMPromiseIfExists()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mSeekDOMPromise) {
     RefPtr<dom::Promise> promise = mSeekDOMPromise;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
       promise->MaybeResolveWithUndefined();
     });
-    AbstractThread::MainThread()->Dispatch(r.forget());
+    mAbstractMainThread->Dispatch(r.forget());
     mSeekDOMPromise = nullptr;
   }
 }
 
 void
 MediaDecoder::AsyncRejectSeekDOMPromiseIfExists()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mSeekDOMPromise) {
     RefPtr<dom::Promise> promise = mSeekDOMPromise;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     });
-    AbstractThread::MainThread()->Dispatch(r.forget());
+    mAbstractMainThread->Dispatch(r.forget());
     mSeekDOMPromise = nullptr;
   }
 }
 
 void
 MediaDecoder::DiscardOngoingSeekIfExists()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -748,17 +756,17 @@ MediaDecoder::DiscardOngoingSeekIfExists
 void
 MediaDecoder::CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DiscardOngoingSeekIfExists();
 
   mSeekDOMPromise = aPromise;
   mDecoderStateMachine->InvokeSeek(aTarget)
-  ->Then(AbstractThread::MainThread(), __func__, this,
+  ->Then(mAbstractMainThread, __func__, this,
          &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected)
   ->Track(mSeekRequest);
 }
 
 double
 MediaDecoder::GetCurrentTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -1600,17 +1608,19 @@ MediaMemoryTracker::CollectReports(nsIHa
   //     resources memory and finish the asynchronous memory report.
   RefPtr<MediaDecoder::ResourceSizes> resourceSizes =
       new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf);
 
   nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
   nsCOMPtr<nsISupports> data = aData;
 
   resourceSizes->Promise()->Then(
-      AbstractThread::MainThread(), __func__,
+      // Non-DocGroup version of AbstractThread::MainThread is fine for memory report.
+      AbstractThread::MainThread(),
+      __func__,
       [handleReport, data] (size_t size) {
         handleReport->Callback(
             EmptyCString(), NS_LITERAL_CSTRING("explicit/media/resources"),
             KIND_HEAP, UNITS_BYTES, size,
             NS_LITERAL_CSTRING("Memory used by media resources including "
                                "streaming buffers, caches, etc."),
             data);
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -40,16 +40,17 @@ class nsIPrincipal;
 
 namespace mozilla {
 
 namespace dom {
 class Promise;
 class HTMLMediaElement;
 }
 
+class AbstractThread;
 class VideoFrameContainer;
 class MediaDecoderStateMachine;
 
 enum class MediaEventType : int8_t;
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation.
 #ifdef GetCurrentTime
@@ -62,16 +63,17 @@ public:
   // Used to register with MediaResource to receive notifications which will
   // be forwarded to MediaDecoder.
   class ResourceCallback : public MediaResourceCallback {
     // Throttle calls to MediaDecoder::NotifyDataArrived()
     // to be at most once per 500ms.
     static const uint32_t sDelay = 500;
 
   public:
+    explicit ResourceCallback(AbstractThread* aMainThread);
     // Start to receive notifications from ResourceCallback.
     void Connect(MediaDecoder* aDecoder);
     // Called upon shutdown to stop receiving notifications.
     void Disconnect();
 
   private:
     /* MediaResourceCallback functions */
     MediaDecoderOwner* GetMediaOwner() const override;
@@ -86,16 +88,17 @@ public:
     void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) override;
 
     static void TimerCallback(nsITimer* aTimer, void* aClosure);
 
     // The decoder to send notifications. Main-thread only.
     MediaDecoder* mDecoder = nullptr;
     nsCOMPtr<nsITimer> mTimer;
     bool mTimerArmed = false;
+    const RefPtr<AbstractThread> mAbstractMainThread;
   };
 
   typedef MozPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> SeekPromise;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // Enumeration for the valid play states (see mPlayState)
   enum PlayState {
@@ -417,16 +420,21 @@ private:
   // Notifies the element that decoding has failed.
   void DecodeError(const MediaResult& aError);
 
   // Indicate whether the media is same-origin with the element.
   void UpdateSameOriginStatus(bool aSameOrigin);
 
   MediaDecoderOwner* GetOwner() const override;
 
+  AbstractThread* AbstractMainThread() const final override
+  {
+    return mAbstractMainThread;
+  }
+
   typedef MozPromise<RefPtr<CDMProxy>, bool /* aIgnored */, /* IsExclusive = */ true> CDMProxyPromise;
 
   // Resolved when a CDMProxy is available and the capabilities are known or
   // rejected when this decoder is about to shut down.
   RefPtr<CDMProxyPromise> RequestCDMProxy() const;
 
   void SetCDMProxy(CDMProxy* aProxy);
 
@@ -629,16 +637,19 @@ protected:
 
   void OnMetadataUpdate(TimedMetadata&& aMetadata);
 
   // This should only ever be accessed from the main thread.
   // It is set in the constructor and cleared in Shutdown when the element goes
   // away. The decoder does not add a reference the element.
   MediaDecoderOwner* mOwner;
 
+  // The AbstractThread from mOwner.
+  const RefPtr<AbstractThread> mAbstractMainThread;
+
   // Counters related to decode and presentation of frames.
   const RefPtr<FrameStatistics> mFrameStats;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
 
   // Data needed to estimate playback data rate. The timeline used for
   // this estimate is "decode time" (where the "current time" is the
   // time of the last decoded video frame).
--- a/dom/media/MediaDecoderOwner.h
+++ b/dom/media/MediaDecoderOwner.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef MediaDecoderOwner_h_
 #define MediaDecoderOwner_h_
 #include "AbstractMediaDecoder.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 
+class AbstractThread;
 class VideoFrameContainer;
 class MediaResult;
 
 namespace dom {
 class HTMLMediaElement;
 } // namespace dom
 
 class MediaDecoderOwner
@@ -39,16 +40,19 @@ public:
 
   // Get the HTMLMediaElement object if the decoder is being used from an
   // HTML media element, and null otherwise.
   virtual dom::HTMLMediaElement* GetMediaElement()
   {
     return nullptr;
   }
 
+  // Return an abstract thread on which to run main thread runnables.
+  virtual AbstractThread* AbstractMainThread() const = 0;
+
   // Return true if decoding should be paused
   virtual bool GetPaused() = 0;
 
   // Called by the video decoder object, on the main thread,
   // when it has read the metadata containing video dimensions,
   // etc.
   // Must take ownership of MetadataTags aTags argument.
   virtual void MetadataLoaded(const MediaInfo* aInfo,
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1933,18 +1933,22 @@ StateObject::HandleResumeVideoDecoding()
   const SeekTarget::Type type = mMaster->HasAudio()
                                 ? SeekTarget::Type::Accurate
                                 : SeekTarget::Type::PrevSyncPoint;
 
   seekJob.mTarget.emplace(mMaster->GetMediaTime(),
                           type,
                           true /* aVideoOnly */);
 
+  // Hold mMaster->mAbstractMainThread here because this->mMaster will be invalid
+  // after the current state object is deleted in SetState();
+  RefPtr<AbstractThread> mainThread = mMaster->mAbstractMainThread;
+
   SetSeekingState(Move(seekJob), EventVisibility::Suppressed)->Then(
-    AbstractThread::MainThread(), __func__,
+    mainThread, __func__,
     [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
     [](){});
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 StateObject::SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility)
 {
@@ -2468,16 +2472,17 @@ ShutdownState::Enter()
 #define INIT_MIRROR(name, val) \
   name(mTaskQueue, val, "MediaDecoderStateMachine::" #name " (Mirror)")
 #define INIT_CANONICAL(name, val) \
   name(mTaskQueue, val, "MediaDecoderStateMachine::" #name " (Canonical)")
 
 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
                                                    MediaDecoderReader* aReader) :
   mDecoderID(aDecoder),
+  mAbstractMainThread(aDecoder->AbstractMainThread()),
   mFrameStats(&aDecoder->GetFrameStatistics()),
   mVideoFrameContainer(aDecoder->GetVideoFrameContainer()),
   mAudioChannel(aDecoder->GetAudioChannel()),
   mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
                            /* aSupportsTailDispatch = */ true)),
   mWatchManager(this, mTaskQueue),
   mDispatchedStateMachine(false),
   mDelayedScheduler(mTaskQueue),
@@ -2603,17 +2608,17 @@ MediaDecoderStateMachine::CreateAudioSin
   };
   return new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
 }
 
 already_AddRefed<media::MediaSink>
 MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured)
 {
   RefPtr<media::MediaSink> audioSink = aAudioCaptured
-    ? new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue,
+    ? new DecodedStream(mTaskQueue, mAbstractMainThread, mAudioQueue, mVideoQueue,
                         mOutputStreamManager, mSameOriginMedia.Ref(),
                         mMediaPrincipalHandle.Ref())
     : CreateAudioSink();
 
   RefPtr<media::MediaSink> mediaSink =
     new VideoSink(mTaskQueue, audioSink, mVideoQueue,
                   mVideoFrameContainer, *mFrameStats,
                   sVideoQueueSendToCompositorSize);
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -99,16 +99,17 @@ hardware (via AudioStream).
 #include "SeekJob.h"
 
 namespace mozilla {
 
 namespace media {
 class MediaSink;
 }
 
+class AbstractThread;
 class AudioSegment;
 class DecodedStream;
 class MediaDecoderReaderWrapper;
 class OutputStreamManager;
 class TaskQueue;
 
 extern LazyLogModule gMediaDecoderLog;
 extern LazyLogModule gMediaSampleLog;
@@ -461,16 +462,17 @@ private:
   void OnMediaSinkAudioComplete();
   void OnMediaSinkVideoComplete();
 
   // Rejected by the MediaSink to signal errors for audio/video.
   void OnMediaSinkAudioError(nsresult aResult);
   void OnMediaSinkVideoError();
 
   void* const mDecoderID;
+  const RefPtr<AbstractThread> mAbstractMainThread;
   const RefPtr<FrameStatistics> mFrameStats;
   const RefPtr<VideoFrameContainer> mVideoFrameContainer;
   const dom::AudioChannel mAudioChannel;
 
   // Task queue for running the state machine.
   RefPtr<TaskQueue> mTaskQueue;
 
   // State-watching manager.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -10,16 +10,17 @@
 #include "MediaInfo.h"
 #include "MediaFormatReader.h"
 #include "MediaPrefs.h"
 #include "MediaResource.h"
 #include "VideoUtils.h"
 #include "VideoFrameContainer.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/CDMProxy.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/SyncRunnable.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
@@ -113,16 +114,17 @@ private:
   const TrackType mTrack;
 };
 
 DecoderAllocPolicy::DecoderAllocPolicy(TrackType aTrack)
   : mMonitor("DecoderAllocPolicy::mMonitor")
   , mDecoderLimit(MediaPrefs::MediaDecoderLimit())
   , mTrack(aTrack)
 {
+  // Non DocGroup-version AbstractThread::MainThread is fine for ClearOnShutdown().
   AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction([this] () {
     ClearOnShutdown(this, ShutdownPhase::ShutdownThreads);
   }));
 }
 
 DecoderAllocPolicy::~DecoderAllocPolicy()
 {
   while (!mPromises.empty()) {
@@ -434,19 +436,20 @@ MediaFormatReader::DecoderFactory::DoIni
 // This ensure that the reader's taskqueue will never blocked while a demuxer
 // is itself blocked attempting to access the MediaCache or the MediaResource.
 class MediaFormatReader::DemuxerProxy
 {
   using TrackType = TrackInfo::TrackType;
   class Wrapper;
 
 public:
-  explicit DemuxerProxy(MediaDataDemuxer* aDemuxer)
+  explicit DemuxerProxy(MediaDataDemuxer* aDemuxer, AbstractThread* mainThread)
     : mTaskQueue(new AutoTaskQueue(
-                   GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
+                   GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
+                   mainThread))
     , mData(new Data(aDemuxer))
   {
     MOZ_COUNT_CTOR(DemuxerProxy);
   }
 
   ~DemuxerProxy()
   {
     MOZ_COUNT_DTOR(DemuxerProxy);
@@ -783,17 +786,17 @@ TrackTypeToStr(TrackInfo::TrackType aTra
 MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
                                      MediaDataDemuxer* aDemuxer,
                                      VideoFrameContainer* aVideoFrameContainer)
   : MediaDecoderReader(aDecoder)
   , mAudio(this, MediaData::AUDIO_DATA,
            Preferences::GetUint("media.audio-max-decode-error", 3))
   , mVideo(this, MediaData::VIDEO_DATA,
            Preferences::GetUint("media.video-max-decode-error", 2))
-  , mDemuxer(new DemuxerProxy(aDemuxer))
+  , mDemuxer(new DemuxerProxy(aDemuxer, aDecoder->AbstractMainThread()))
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
   , mInitDone(false)
   , mTrackDemuxersMayBlock(false)
   , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
   , mDecoderFactory(new DecoderFactory(this))
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1045,17 +1045,17 @@ public:
     // DOMMediaStream::NotifyMediaStreamGraphShutdown is called.
     RefPtr<DOMMediaStream> mStream;
   };
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
-    auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
+    nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
     nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner() : nullptr;
 
     // We're on main-thread, and the windowlist can only
     // be invalidated from the main-thread (see OnNavigation)
     StreamListeners* listeners = mManager->GetWindowListeners(mWindowID);
     if (!listeners || !window || !window->GetExtantDoc()) {
       // This window is no longer live.  mListener has already been removed
       return NS_OK;
@@ -1077,17 +1077,18 @@ public:
     if (mAudioDevice &&
         mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) {
       // It should be possible to pipe the capture stream to anything. CORS is
       // not a problem here, we got explicit user content.
       nsCOMPtr<nsIPrincipal> principal = window->GetExtantDoc()->NodePrincipal();
       domStream =
         DOMMediaStream::CreateAudioCaptureStreamAsInput(window, principal, msg);
 
-      stream = msg->CreateSourceStream(); // Placeholder
+      stream = msg->CreateSourceStream(
+        globalWindow->AbstractMainThreadFor(dom::TaskCategory::Other)); // Placeholder
       msg->RegisterCaptureStreamForWindow(
             mWindowID, domStream->GetInputStream()->AsProcessedStream());
       window->SetAudioCapture(true);
     } else {
       class LocalTrackSource : public MediaStreamTrackSource
       {
       public:
         LocalTrackSource(nsIPrincipal* aPrincipal,
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -416,16 +416,17 @@ class MediaRecorder::Session: public nsI
 public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder)
     , mTimeSlice(aTimeSlice)
     , mStopIssued(false)
     , mIsStartEventFired(false)
     , mNeedSessionEndTask(true)
     , mSelectedVideoTrackID(TRACK_NONE)
+    , mAbstractMainThread(aRecorder->mAbstractMainThread)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
                                            MAX_ALLOW_MEMORY_BUFFER);
     mEncodedBufferCache = new EncodedBufferCache(maxMem);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
@@ -471,17 +472,17 @@ public:
   void Start()
   {
     LOG(LogLevel::Debug, ("Session.Start %p", this));
     MOZ_ASSERT(NS_IsMainThread());
 
     // Create a Track Union Stream
     MediaStreamGraph* gm = mRecorder->GetSourceMediaStream()->Graph();
     TrackRate trackRate = gm->GraphRate();
-    mTrackUnionStream = gm->CreateTrackUnionStream();
+    mTrackUnionStream = gm->CreateTrackUnionStream(mAbstractMainThread);
     MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed");
 
     mTrackUnionStream->SetAutofinish(true);
 
     DOMMediaStream* domStream = mRecorder->Stream();
     if (domStream) {
       // Get the available tracks from the DOMMediaStream.
       // The callback will report back tracks that we have to connect to
@@ -931,16 +932,17 @@ private:
   bool mStopIssued;
   // Indicate the session had fire start event. Encoding thread only.
   bool mIsStartEventFired;
   // False if the InitEncoder called successfully, ensure the
   // ExtractRunnable/DestroyRunnable will end the session.
   // Main thread only.
   bool mNeedSessionEndTask;
   TrackID mSelectedVideoTrackID;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 NS_IMPL_ISUPPORTS(MediaRecorder::Session, nsIObserver)
 
 MediaRecorder::~MediaRecorder()
 {
   if (mPipeStream != nullptr) {
     mInputPort->Destroy();
@@ -949,29 +951,31 @@ MediaRecorder::~MediaRecorder()
   LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
   UnRegisterActivityObserver();
 }
 
 MediaRecorder::MediaRecorder(DOMMediaStream& aSourceMediaStream,
                              nsPIDOMWindowInner* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mState(RecordingState::Inactive)
+  , mAbstractMainThread(aSourceMediaStream.AbstractMainThread())
 {
   MOZ_ASSERT(aOwnerWindow);
   MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
   mDOMStream = &aSourceMediaStream;
 
   RegisterActivityObserver();
 }
 
 MediaRecorder::MediaRecorder(AudioNode& aSrcAudioNode,
                              uint32_t aSrcOutput,
                              nsPIDOMWindowInner* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mState(RecordingState::Inactive)
+  , mAbstractMainThread(aSrcAudioNode.AbstractMainThread())
 {
   MOZ_ASSERT(aOwnerWindow);
   MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
 
   // Only AudioNodeStream of kind EXTERNAL_STREAM stores output audio data in
   // the track (see AudioNodeStream::AdvanceOutputSegment()). That means track
   // union stream in recorder session won't be able to copy data from the
   // stream of non-destination node. Create a pipe stream in this case.
--- a/dom/media/MediaRecorder.h
+++ b/dom/media/MediaRecorder.h
@@ -11,16 +11,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsIDocumentActivity.h"
 
 // Max size for allowing queue encoded data in memory
 #define MAX_ALLOW_MEMORY_BUFFER 1024000
 namespace mozilla {
 
+class AbstractThread;
 class AudioNodeStream;
 class DOMMediaStream;
 class ErrorResult;
 class MediaInputPort;
 struct MediaRecorderOptions;
 class MediaStream;
 class GlobalObject;
 
@@ -152,16 +153,18 @@ protected:
   nsCOMPtr<nsIDocument> mDocument;
 
   // It specifies the container format as well as the audio and video capture formats.
   nsString mMimeType;
 
   uint32_t mAudioBitsPerSecond;
   uint32_t mVideoBitsPerSecond;
   uint32_t mBitsPerSecond;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 private:
   // Register MediaRecorder into Document to listen the activity changes.
   void RegisterActivityObserver();
   void UnRegisterActivityObserver();
 
   bool CheckPermission(const nsString &aType);
 };
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -24,18 +24,18 @@
 #include "AudioNodeExternalInputStream.h"
 #include "MediaStreamListener.h"
 #include "MediaStreamVideoSink.h"
 #include "mozilla/dom/BaseAudioContextBinding.h"
 #include "mozilla/media/MediaUtils.h"
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "VideoFrameContainer.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Unused.h"
-#include "mozilla/media/MediaUtils.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
 #endif
 #include "mtransport/runnable_utils.h"
 
 #include "webaudio/blink/DenormalDisabler.h"
 #include "webaudio/blink/HRTFDatabaseLoader.h"
 
@@ -1755,26 +1755,16 @@ MediaStreamGraphImpl::RunInStableState(b
 
 #ifdef DEBUG
   mCanRunMessagesSynchronously = mDetectedNotRunning &&
     mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
 #endif
 
   for (uint32_t i = 0; i < runnables.Length(); ++i) {
     runnables[i]->Run();
-    // "Direct" tail dispatcher are supposed to run immediately following the
-    // execution of the current task. So the meta-tasking that we do here in
-    // RunInStableState() breaks that abstraction a bit unless we handle it here.
-    //
-    // This is particularly important because we can end up with a "stream
-    // ended" notification immediately following a "stream available" notification,
-    // and we need to make sure that the watcher responding to "stream available"
-    // has a chance to run before the second notification starts tearing things
-    // down.
-    AbstractThread::MainThread()->TailDispatcher().DrainDirectTasks();
   }
 }
 
 
 void
 MediaStreamGraphImpl::EnsureRunInStableState()
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
@@ -1834,32 +1824,33 @@ MediaStreamGraphImpl::AppendMessage(Uniq
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(Move(aMessage));
   EnsureRunInStableState();
 }
 
-MediaStream::MediaStream()
+MediaStream::MediaStream(AbstractThread* aMainThread)
   : mTracksStartTime(0)
   , mStartBlocking(GRAPH_TIME_MAX)
   , mSuspendedCount(0)
   , mFinished(false)
   , mNotifiedFinished(false)
   , mNotifiedBlocked(false)
   , mHasCurrentData(false)
   , mNotifiedHasCurrentData(false)
   , mMainThreadCurrentTime(0)
   , mMainThreadFinished(false)
   , mFinishedNotificationSent(false)
   , mMainThreadDestroyed(false)
   , mNrOfMainThreadUsers(0)
   , mGraph(nullptr)
   , mAudioChannelType(dom::AudioChannel::Normal)
+  , mAbstractMainThread(aMainThread)
 {
   MOZ_COUNT_CTOR(MediaStream);
 }
 
 MediaStream::~MediaStream()
 {
   MOZ_COUNT_DTOR(MediaStream);
   NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
@@ -2494,37 +2485,43 @@ MediaStream::RunAfterPendingUpdates(alre
   // runnable will run in finite time.
   if (!(graph->mRealtime || graph->mNonRealtimeProcessing)) {
     runnable->Run();
     return;
   }
 
   class Message : public ControlMessage {
   public:
-    explicit Message(MediaStream* aStream,
-                     already_AddRefed<nsIRunnable> aRunnable)
+    Message(MediaStream* aStream,
+            already_AddRefed<nsIRunnable> aRunnable,
+            AbstractThread* aMainThread)
       : ControlMessage(aStream)
-      , mRunnable(aRunnable) {}
+      , mRunnable(aRunnable)
+      , mAbstractMainThread(aMainThread)
+      {}
     void Run() override
     {
       mStream->Graph()->
-        DispatchToMainThreadAfterStreamStateUpdate(mRunnable.forget());
+        DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                   mRunnable.forget());
     }
     void RunDuringShutdown() override
     {
       // Don't run mRunnable now as it may call AppendMessage() which would
       // assume that there are no remaining controlMessagesToRunDuringShutdown.
       MOZ_ASSERT(NS_IsMainThread());
       NS_DispatchToCurrentThread(mRunnable);
     }
   private:
     nsCOMPtr<nsIRunnable> mRunnable;
+    const RefPtr<AbstractThread> mAbstractMainThread;
   };
 
-  graph->AppendMessage(MakeUnique<Message>(this, runnable.forget()));
+  graph->AppendMessage(
+    MakeUnique<Message>(this, runnable.forget(), mAbstractMainThread));
 }
 
 void
 MediaStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode)
 {
   if (aMode == DisabledTrackMode::ENABLED) {
     for (int32_t i = mDisabledTracks.Length() - 1; i >= 0; --i) {
       if (aTrackID == mDisabledTracks[i].mTrackID) {
@@ -2628,18 +2625,18 @@ MediaStream::AddMainThreadListener(MainT
 
     RefPtr<MediaStream> mStream;
   };
 
   nsCOMPtr<nsIRunnable> runnable = new NotifyRunnable(this);
   Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
 }
 
-SourceMediaStream::SourceMediaStream() :
-  MediaStream(),
+SourceMediaStream::SourceMediaStream(AbstractThread* aMainThread) :
+  MediaStream(aMainThread),
   mMutex("mozilla::media::SourceMediaStream"),
   mUpdateKnownTracksTime(0),
   mPullEnabled(false),
   mUpdateFinished(false),
   mNeedsMixing(false)
 {
 }
 
@@ -3202,50 +3199,56 @@ MediaInputPort::BlockSourceTrackIdImpl(T
   mBlockedTracks.AppendElement(Pair<TrackID, BlockingMode>(aTrackId, aBlockingMode));
 }
 
 already_AddRefed<Pledge<bool>>
 MediaInputPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
 {
   class Message : public ControlMessage {
   public:
-    explicit Message(MediaInputPort* aPort,
-                     TrackID aTrackId,
-                     BlockingMode aBlockingMode,
-                     already_AddRefed<nsIRunnable> aRunnable)
+    Message(MediaInputPort* aPort,
+            TrackID aTrackId,
+            BlockingMode aBlockingMode,
+            already_AddRefed<nsIRunnable> aRunnable,
+            AbstractThread* aMainThread)
       : ControlMessage(aPort->GetDestination()),
         mPort(aPort), mTrackId(aTrackId), mBlockingMode(aBlockingMode),
-        mRunnable(aRunnable) {}
+        mRunnable(aRunnable), mAbstractMainThread(aMainThread) {}
     void Run() override
     {
       mPort->BlockSourceTrackIdImpl(mTrackId, mBlockingMode);
       if (mRunnable) {
-        mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(mRunnable.forget());
+        mStream->Graph()->
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     mRunnable.forget());
       }
     }
     void RunDuringShutdown() override
     {
       Run();
     }
     RefPtr<MediaInputPort> mPort;
     TrackID mTrackId;
     BlockingMode mBlockingMode;
     nsCOMPtr<nsIRunnable> mRunnable;
+    const RefPtr<AbstractThread> mAbstractMainThread;
   };
 
   MOZ_ASSERT(IsTrackIDExplicit(aTrackId),
              "Only explicit TrackID is allowed");
 
   RefPtr<Pledge<bool>> pledge = new Pledge<bool>();
   nsCOMPtr<nsIRunnable> runnable = NewRunnableFrom([pledge]() {
     MOZ_ASSERT(NS_IsMainThread());
     pledge->Resolve(true);
     return NS_OK;
   });
-  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId, aBlockingMode, runnable.forget()));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId, aBlockingMode,
+                                                 runnable.forget(),
+                                                 mAbstractMainThread));
   return pledge.forget();
 }
 
 already_AddRefed<MediaInputPort>
 ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID,
                                         TrackID aDestTrackID,
                                         uint16_t aInputNumber, uint16_t aOutputNumber,
                                         nsTArray<TrackID>* aBlockedTracks)
@@ -3275,17 +3278,17 @@ ProcessedMediaStream::AllocateInputPort(
   MOZ_ASSERT(aTrackID == TRACK_ANY || IsTrackIDExplicit(aTrackID),
              "Only TRACK_ANY and explicit ID are allowed for source track");
   MOZ_ASSERT(aDestTrackID == TRACK_ANY || IsTrackIDExplicit(aDestTrackID),
              "Only TRACK_ANY and explicit ID are allowed for destination track");
   MOZ_ASSERT(aTrackID != TRACK_ANY || aDestTrackID == TRACK_ANY,
              "Generic MediaInputPort cannot produce a single destination track");
   RefPtr<MediaInputPort> port =
     new MediaInputPort(aStream, aTrackID, this, aDestTrackID,
-                       aInputNumber, aOutputNumber);
+                       aInputNumber, aOutputNumber, mAbstractMainThread);
   if (aBlockedTracks) {
     for (TrackID trackID : *aBlockedTracks) {
       port->BlockSourceTrackIdImpl(trackID, BlockingMode::CREATION);
     }
   }
   port->SetGraphImpl(GraphImpl());
   GraphImpl()->AppendMessage(MakeUnique<Message>(port));
   return port.forget();
@@ -3629,35 +3632,35 @@ FinishCollectReports(nsIHandleReportCall
   }
 
 #undef REPORT
 
   manager->EndReport();
 }
 
 SourceMediaStream*
-MediaStreamGraph::CreateSourceStream()
+MediaStreamGraph::CreateSourceStream(AbstractThread* aMainThread)
 {
-  SourceMediaStream* stream = new SourceMediaStream();
+  SourceMediaStream* stream = new SourceMediaStream(aMainThread);
   AddStream(stream);
   return stream;
 }
 
 ProcessedMediaStream*
-MediaStreamGraph::CreateTrackUnionStream()
+MediaStreamGraph::CreateTrackUnionStream(AbstractThread* aMainThread)
 {
-  TrackUnionStream* stream = new TrackUnionStream();
+  TrackUnionStream* stream = new TrackUnionStream(aMainThread);
   AddStream(stream);
   return stream;
 }
 
 ProcessedMediaStream*
-MediaStreamGraph::CreateAudioCaptureStream(TrackID aTrackId)
+MediaStreamGraph::CreateAudioCaptureStream(TrackID aTrackId, AbstractThread* aMainThread)
 {
-  AudioCaptureStream* stream = new AudioCaptureStream(aTrackId);
+  AudioCaptureStream* stream = new AudioCaptureStream(aTrackId, aMainThread);
   AddStream(stream);
   return stream;
 }
 
 void
 MediaStreamGraph::AddStream(MediaStream* aStream)
 {
   NS_ADDREF(aStream);
@@ -4048,9 +4051,20 @@ MediaStreamGraphImpl::ConnectToCaptureSt
     if (mWindowCaptureStreams[i].mWindowId == aWindowId) {
       ProcessedMediaStream* sink = mWindowCaptureStreams[i].mCaptureStreamSink;
       return sink->AllocateInputPort(aMediaStream);
     }
   }
   return nullptr;
 }
 
+void
+MediaStreamGraph::
+DispatchToMainThreadAfterStreamStateUpdate(AbstractThread* aMainThread,
+                                           already_AddRefed<nsIRunnable> aRunnable)
+{
+  MOZ_ASSERT(aMainThread);
+  AssertOnGraphThreadOrNotRunning();
+  *mPendingUpdateRunnables.AppendElement() =
+    aMainThread->CreateDirectTaskDrainer(Move(aRunnable));
+}
+
 } // namespace mozilla
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -72,16 +72,17 @@ namespace media {
  *
  * Media decoding, audio processing and media playback use thread-safe APIs to
  * the media graph to ensure they can continue while the main thread is blocked.
  *
  * When the graph is changed, we may need to throw out buffered data and
  * reprocess it. This is triggered automatically by the MediaStreamGraph.
  */
 
+class AbstractThread;
 class AudioNodeEngine;
 class AudioNodeExternalInputStream;
 class AudioNodeStream;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
 class MediaStreamGraphImpl;
 class ProcessedMediaStream;
@@ -270,17 +271,17 @@ struct DisabledTrack {
 #undef GetCurrentTime
 #endif
 
 class MediaStream : public mozilla::LinkedListElement<MediaStream>
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
-  MediaStream();
+  explicit MediaStream(AbstractThread* aMainThread);
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~MediaStream();
 
 public:
   /**
    * Returns the graph that owns this stream.
@@ -680,28 +681,30 @@ protected:
   bool mFinishedNotificationSent;
   bool mMainThreadDestroyed;
   int mNrOfMainThreadUsers;
 
   // Our media stream graph.  null if destroyed on the graph thread.
   MediaStreamGraphImpl* mGraph;
 
   dom::AudioChannel mAudioChannelType;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 /**
  * This is a stream into which a decoder can write audio and video.
  *
  * Audio and video can be written on any thread, but you probably want to
  * always write from the same thread to avoid unexpected interleavings.
  */
 class SourceMediaStream : public MediaStream
 {
 public:
-  explicit SourceMediaStream();
+  explicit SourceMediaStream(AbstractThread* aMainThread);
 
   SourceMediaStream* AsSourceStream() override { return this; }
 
   // Media graph thread only
 
   // Users of audio inputs go through the stream so it can track when the
   // last stream referencing an input goes away, so it can close the cubeb
   // input.  Also note: callable on any thread (though it bounces through
@@ -972,24 +975,26 @@ enum class BlockingMode
  * the port and drop the graph's reference, destroying the object.
  */
 class MediaInputPort final
 {
 private:
   // Do not call this constructor directly. Instead call aDest->AllocateInputPort.
   MediaInputPort(MediaStream* aSource, TrackID& aSourceTrack,
                  ProcessedMediaStream* aDest, TrackID& aDestTrack,
-                 uint16_t aInputNumber, uint16_t aOutputNumber)
+                 uint16_t aInputNumber, uint16_t aOutputNumber,
+                 AbstractThread* aMainThread)
     : mSource(aSource)
     , mSourceTrack(aSourceTrack)
     , mDest(aDest)
     , mDestTrack(aDestTrack)
     , mInputNumber(aInputNumber)
     , mOutputNumber(aOutputNumber)
     , mGraph(nullptr)
+    , mAbstractMainThread(aMainThread)
   {
     MOZ_COUNT_CTOR(MediaInputPort);
   }
 
   // Private destructor, to discourage deletion outside of Release():
   ~MediaInputPort()
   {
     MOZ_COUNT_DTOR(MediaInputPort);
@@ -1122,28 +1127,30 @@ private:
   const uint16_t mInputNumber;
   const uint16_t mOutputNumber;
 
   typedef Pair<TrackID, BlockingMode> BlockedTrack;
   nsTArray<BlockedTrack> mBlockedTracks;
 
   // Our media stream graph
   MediaStreamGraphImpl* mGraph;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 /**
  * This stream processes zero or more input streams in parallel to produce
  * its output. The details of how the output is produced are handled by
  * subclasses overriding the ProcessInput method.
  */
 class ProcessedMediaStream : public MediaStream
 {
 public:
-  explicit ProcessedMediaStream()
-    : MediaStream(), mAutofinish(false), mCycleMarker(0)
+  explicit ProcessedMediaStream(AbstractThread* aMainThread)
+    : MediaStream(aMainThread), mAutofinish(false), mCycleMarker(0)
   {}
 
   // Control API.
   /**
    * Allocates a new input port attached to source aStream.
    * This stream can be removed by calling MediaInputPort::Remove().
    *
    * The input port is tied to aTrackID in the source stream.
@@ -1300,36 +1307,37 @@ public:
   }
   virtual void CloseAudioInput(AudioDataListener *aListener) {}
 
   // Control API.
   /**
    * Create a stream that a media decoder (or some other source of
    * media data, such as a camera) can write to.
    */
-  SourceMediaStream* CreateSourceStream();
+  SourceMediaStream* CreateSourceStream(AbstractThread* aMainThread);
   /**
    * Create a stream that will form the union of the tracks of its input
    * streams.
    * A TrackUnionStream contains all the tracks of all its input streams.
    * Adding a new input stream makes that stream's tracks immediately appear as new
    * tracks starting at the time the input stream was added.
    * Removing an input stream makes the output tracks corresponding to the
    * removed tracks immediately end.
    * For each added track, the track ID of the output track is the track ID
    * of the input track or one plus the maximum ID of all previously added
    * tracks, whichever is greater.
    * TODO at some point we will probably need to add API to select
    * particular tracks of each input stream.
    */
-  ProcessedMediaStream* CreateTrackUnionStream();
+  ProcessedMediaStream* CreateTrackUnionStream(AbstractThread* aMainThread);
   /**
    * Create a stream that will mix all its audio input.
    */
-  ProcessedMediaStream* CreateAudioCaptureStream(TrackID aTrackId);
+  ProcessedMediaStream* CreateAudioCaptureStream(TrackID aTrackId,
+                                                 AbstractThread* aMainThread);
 
   /**
    * Add a new stream to the graph.  Main thread.
    */
   void AddStream(MediaStream* aStream);
 
   /* From the main thread, ask the MSG to send back an event when the graph
    * thread is running, and audio is being processed. */
@@ -1354,24 +1362,32 @@ public:
    * Start processing non-realtime for a specific number of ticks.
    */
   void StartNonRealtimeProcessing(uint32_t aTicksToProcess);
 
   /**
    * Media graph thread only.
    * Dispatches a runnable that will run on the main thread after all
    * main-thread stream state has been next updated.
+   *
    * Should only be called during MediaStreamListener callbacks or during
    * ProcessedMediaStream::ProcessInput().
+   *
+   * |aMainThread| is the corresponding AbstractThread on the main thread to
+   * drain the direct tasks generated by |aRunnable|.
+   * Note: The reasons for assigning proper |aMainThread| are
+   * - MSG serves media elements in multiple windows run on main thread.
+   * - DocGroup-specific AbstractMainThread is introduced to cluster the tasks
+   *   of the same window for prioritizing tasks among different windows.
+   * - Proper |aMainThread| ensures that tasks dispatched to the main thread are
+   *   clustered to the right queue and are executed in right order.
    */
-  virtual void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
-  {
-    AssertOnGraphThreadOrNotRunning();
-    *mPendingUpdateRunnables.AppendElement() = aRunnable;
-  }
+  virtual void
+  DispatchToMainThreadAfterStreamStateUpdate(AbstractThread* aMainThread,
+                                             already_AddRefed<nsIRunnable> aRunnable);
 
   /**
    * Returns graph sample rate in Hz.
    */
   TrackRate GraphRate() const { return mSampleRate; }
 
   void RegisterCaptureStreamForWindow(uint64_t aWindowId,
                                       ProcessedMediaStream* aCaptureStream);
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -78,17 +78,19 @@ NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTr
  * In case of multiple changes to the main thread state, the track's principal
  * will be a combination of its old principal and all the new ones until the
  * latest main thread principal matches the PrincipalHandle on the MSG thread.
  */
 class MediaStreamTrack::PrincipalHandleListener : public MediaStreamTrackListener
 {
 public:
   explicit PrincipalHandleListener(MediaStreamTrack* aTrack)
-    : mTrack(aTrack) {}
+    : mTrack(aTrack)
+    , mAbstractMainThread(aTrack->mOwningStream->AbstractMainThread())
+    {}
 
   void Forget()
   {
     MOZ_ASSERT(NS_IsMainThread());
     mTrack = nullptr;
   }
 
   void DoNotifyPrincipalHandleChanged(const PrincipalHandle& aNewPrincipalHandle)
@@ -100,25 +102,26 @@ public:
     }
 
     mTrack->NotifyPrincipalHandleChanged(aNewPrincipalHandle);
   }
 
   void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
                                     const PrincipalHandle& aNewPrincipalHandle) override
   {
-    nsCOMPtr<nsIRunnable> runnable =
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+      mAbstractMainThread,
       NewRunnableMethod<StoreCopyPassByConstLRef<PrincipalHandle>>(
-        this, &PrincipalHandleListener::DoNotifyPrincipalHandleChanged, aNewPrincipalHandle);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+        this, &PrincipalHandleListener::DoNotifyPrincipalHandleChanged, aNewPrincipalHandle));
   }
 
 protected:
   // These fields may only be accessed on the main thread
   MediaStreamTrack* mTrack;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                                    TrackID aInputTrackID,
                                    MediaStreamTrackSource* aSource,
                                    const MediaTrackConstraints& aConstraints)
   : mOwningStream(aStream), mTrackID(aTrackID),
     mInputTrackID(aInputTrackID), mSource(aSource),
--- a/dom/media/TextTrackCue.cpp
+++ b/dom/media/TextTrackCue.cpp
@@ -51,17 +51,17 @@ TextTrackCue::TextTrackCue(nsPIDOMWindow
                            const nsAString& aText,
                            ErrorResult& aRv)
   : DOMEventTargetHelper(aOwnerWindow)
   , mText(aText)
   , mStartTime(aStartTime)
   , mEndTime(aEndTime)
   , mReset(false, "TextTrackCue::mReset")
   , mHaveStartedWatcher(false)
-  , mWatchManager(this, AbstractThread::MainThread())
+  , mWatchManager(this, GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other))
 {
   SetDefaultCueSettings();
   MOZ_ASSERT(aOwnerWindow);
   if (NS_FAILED(StashDocument())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
@@ -73,17 +73,17 @@ TextTrackCue::TextTrackCue(nsPIDOMWindow
                            ErrorResult& aRv)
   : DOMEventTargetHelper(aOwnerWindow)
   , mText(aText)
   , mStartTime(aStartTime)
   , mEndTime(aEndTime)
   , mTrackElement(aTrackElement)
   , mReset(false, "TextTrackCue::mReset")
   , mHaveStartedWatcher(false)
-  , mWatchManager(this, AbstractThread::MainThread())
+  , mWatchManager(this, GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other))
 {
   SetDefaultCueSettings();
   MOZ_ASSERT(aOwnerWindow);
   if (NS_FAILED(StashDocument())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -41,18 +41,18 @@ namespace mozilla {
 
 #ifdef STREAM_LOG
 #undef STREAM_LOG
 #endif
 
 LazyLogModule gTrackUnionStreamLog("TrackUnionStream");
 #define STREAM_LOG(type, msg) MOZ_LOG(gTrackUnionStreamLog, type, msg)
 
-TrackUnionStream::TrackUnionStream() :
-  ProcessedMediaStream(), mNextAvailableTrackID(1)
+TrackUnionStream::TrackUnionStream(AbstractThread* aMainThread) :
+  ProcessedMediaStream(aMainThread), mNextAvailableTrackID(1)
 {
 }
 
   void TrackUnionStream::RemoveInput(MediaInputPort* aPort)
   {
     STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing input %p", this, aPort));
     for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
       if (mTrackMap[i].mInputPort == aPort) {
--- a/dom/media/TrackUnionStream.h
+++ b/dom/media/TrackUnionStream.h
@@ -12,17 +12,17 @@
 
 namespace mozilla {
 
 /**
  * See MediaStreamGraph::CreateTrackUnionStream.
  */
 class TrackUnionStream : public ProcessedMediaStream {
 public:
-  explicit TrackUnionStream();
+  explicit TrackUnionStream(AbstractThread* aMainThread);
 
   virtual TrackUnionStream* AsTrackUnionStream() override { return this; }
   friend class DOMMediaStream;
 
   void RemoveInput(MediaInputPort* aPort) override;
   void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
   void SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) override;
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -247,17 +247,18 @@ MP4Decoder::IsVideoAccelerated(layers::K
   if (!decoder) {
     taskQueue->BeginShutdown();
     taskQueue->AwaitShutdownAndIdle();
     promise->MaybeResolve(NS_LITERAL_STRING("No; Failed to create H264 decoder"));
     return promise.forget();
   }
 
   decoder->Init()
-    ->Then(AbstractThread::MainThread(), __func__,
+    ->Then(aParent->AbstractMainThreadFor(dom::TaskCategory::Other),
+           __func__,
            [promise, decoder, taskQueue] (TrackInfo::TrackType aTrack) {
              nsCString failureReason;
              bool ok = decoder->IsHardwareAccelerated(failureReason);
              nsAutoString result;
              if (ok) {
                result.AssignLiteral("Yes");
              } else {
                result.AssignLiteral("No");
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -6,16 +6,17 @@
 #include "GMPParent.h"
 #include "mozilla/Logging.h"
 #include "nsComponentManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
 #include "nsIWritablePropertyBag2.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/SSE.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Unused.h"
 #include "nsIObserverService.h"
 #include "GMPTimerParent.h"
 #include "runnable_utils.h"
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
@@ -738,16 +739,17 @@ GMPParent::ReadChromiumManifestFile(nsIF
 {
   nsAutoCString json;
   if (!ReadIntoString(aFile, json, 5 * 1024)) {
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   // DOM JSON parsing needs to run on the main thread.
   return InvokeAsync<nsString&&>(
+    // Non DocGroup-version of AbstractThread::MainThread for the task in parent.
     AbstractThread::MainThread(), this, __func__,
     &GMPParent::ParseChromiumManifest, NS_ConvertUTF8toUTF16(json));
 }
 
 RefPtr<GenericPromise>
 GMPParent::ParseChromiumManifest(const nsAString& aJSON)
 {
   LOGD("%s: for '%s'", __FUNCTION__, NS_LossyConvertUTF16toASCII(aJSON).get());
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -2,16 +2,17 @@
 /* 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 "GMPServiceParent.h"
 #include "GMPService.h"
 #include "prio.h"
 #include "base/task.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Logging.h"
 #include "mozilla/dom/ContentParent.h"
 #include "GMPParent.h"
 #include "GMPVideoDecoderParent.h"
 #include "nsAutoPtr.h"
 #include "nsIObserverService.h"
 #include "GeckoChildProcessHost.h"
 #include "mozilla/Preferences.h"
@@ -609,17 +610,19 @@ GeckoMediaPluginServiceParent::AsyncAddP
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   nsString dir(aDirectory);
   RefPtr<GeckoMediaPluginServiceParent> self = this;
   return InvokeAsync<nsString&&>(
            thread, this, __func__,
            &GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
-    ->Then(AbstractThread::MainThread(), __func__,
+    ->Then(
+      AbstractThread::MainThread(), // Non DocGroup-version for the task in parent.
+      __func__,
       [dir, self]() -> void {
         LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s succeeded",
               NS_ConvertUTF16toUTF8(dir).get()));
         MOZ_ASSERT(NS_IsMainThread());
         self->UpdateContentProcessGMPCapabilities();
       },
       [dir]() -> void {
         LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s failed",
--- a/dom/media/gtest/MockMediaDecoderOwner.h
+++ b/dom/media/gtest/MockMediaDecoderOwner.h
@@ -1,16 +1,17 @@
 /* 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 MOCK_MEDIA_DECODER_OWNER_H_
 #define MOCK_MEDIA_DECODER_OWNER_H_
 
 #include "MediaDecoderOwner.h"
+#include "mozilla/AbstractThread.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla
 {
 
 class MockMediaDecoderOwner : public MediaDecoderOwner
 {
 public:
@@ -43,12 +44,17 @@ public:
   void NotifySuspendedByCache(bool aIsSuspended) override {}
   void NotifyDecoderPrincipalChanged() override {}
   VideoFrameContainer* GetVideoFrameContainer() override
   {
     return nullptr;
   }
   void SetAudibleState(bool aAudible) override {}
   void NotifyXPCOMShutdown() override {}
+  AbstractThread* AbstractMainThread() const override
+  {
+    // Non-DocGroup version for Mock.
+    return AbstractThread::MainThread();
+  }
 };
 }
 
 #endif
--- a/dom/media/gtest/TestMediaDataDecoder.cpp
+++ b/dom/media/gtest/TestMediaDataDecoder.cpp
@@ -7,32 +7,34 @@
 #include "Benchmark.h"
 #include "MockMediaResource.h"
 #include "DecoderTraits.h"
 #include "MediaContainerType.h"
 #include "MP4Decoder.h"
 #include "MP4Demuxer.h"
 #include "WebMDecoder.h"
 #include "WebMDemuxer.h"
+#include "mozilla/AbstractThread.h"
 
 using namespace mozilla;
 
 class BenchmarkRunner
 {
 public:
   explicit BenchmarkRunner(Benchmark* aBenchmark)
     : mBenchmark(aBenchmark) {}
 
   uint32_t Run()
   {
     bool done = false;
     uint32_t result = 0;
 
     mBenchmark->Init();
     mBenchmark->Run()->Then(
+      // Non DocGroup-version of AbstractThread::MainThread() is fine for testing.
       AbstractThread::MainThread(), __func__,
       [&](uint32_t aDecodeFps) { result = aDecodeFps; done = true; },
       [&]() { done = true; });
 
     // Wait until benchmark completes.
     while (!done) {
       NS_ProcessNextEvent();
     }
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
+#include "mozilla/AbstractThread.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/SyncRunnable.h"
 
 #include "AudioSegment.h"
 #include "DecodedStream.h"
 #include "MediaData.h"
 #include "MediaQueue.h"
@@ -28,19 +29,21 @@ namespace mozilla {
 struct PlaybackInfoInit {
   int64_t mStartTime;
   MediaInfo mInfo;
 };
 
 class DecodedStreamGraphListener : public MediaStreamListener {
 public:
   DecodedStreamGraphListener(MediaStream* aStream,
-                             MozPromiseHolder<GenericPromise>&& aPromise)
+                             MozPromiseHolder<GenericPromise>&& aPromise,
+                             AbstractThread* aMainThread)
     : mMutex("DecodedStreamGraphListener::mMutex")
     , mStream(aStream)
+    , mAbstractMainThread(aMainThread)
   {
     mFinishPromise = Move(aPromise);
   }
 
   void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) override
   {
     MutexAutoLock lock(mMutex);
     if (mStream) {
@@ -48,31 +51,31 @@ public:
         mStream->GraphTimeToStreamTime(aCurrentTime));
       mOnOutput.Notify(t);
     }
   }
 
   void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
   {
     if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
-      nsCOMPtr<nsIRunnable> event =
-        NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+        mAbstractMainThread,
+        NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished));
     }
   }
 
   void DoNotifyFinished()
   {
     MOZ_ASSERT(NS_IsMainThread());
     mFinishPromise.ResolveIfExists(true, __func__);
   }
 
   void Forget()
   {
-    AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction([this] () {
+    mAbstractMainThread->Dispatch(NS_NewRunnableFunction([this] () {
       MOZ_ASSERT(NS_IsMainThread());
       mFinishPromise.ResolveIfExists(true, __func__);
     }));
     MutexAutoLock lock(mMutex);
     mStream = nullptr;
   }
 
   MediaEventSource<int64_t>& OnOutput()
@@ -83,51 +86,54 @@ public:
 private:
   MediaEventProducer<int64_t> mOnOutput;
 
   Mutex mMutex;
   // Members below are protected by mMutex.
   RefPtr<MediaStream> mStream;
   // Main thread only.
   MozPromiseHolder<GenericPromise> mFinishPromise;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 static void
-UpdateStreamSuspended(MediaStream* aStream, bool aBlocking)
+UpdateStreamSuspended(AbstractThread* aMainThread, MediaStream* aStream, bool aBlocking)
 {
   if (NS_IsMainThread()) {
     if (aBlocking) {
       aStream->Suspend();
     } else {
       aStream->Resume();
     }
   } else {
     nsCOMPtr<nsIRunnable> r;
     if (aBlocking) {
       r = NewRunnableMethod(aStream, &MediaStream::Suspend);
     } else {
       r = NewRunnableMethod(aStream, &MediaStream::Resume);
     }
-    AbstractThread::MainThread()->Dispatch(r.forget());
+    aMainThread->Dispatch(r.forget());
   }
 }
 
 /*
  * All MediaStream-related data is protected by the decoder's monitor.
  * We have at most one DecodedStreamDaata per MediaDecoder. Its stream
  * is used as the input for each ProcessedMediaStream created by calls to
  * captureStream(UntilEnded). Seeking creates a new source stream, as does
  * replaying after the input as ended. In the latter case, the new source is
  * not connected to streams created by captureStreamUntilEnded.
  */
 class DecodedStreamData {
 public:
   DecodedStreamData(OutputStreamManager* aOutputStreamManager,
                     PlaybackInfoInit&& aInit,
-                    MozPromiseHolder<GenericPromise>&& aPromise);
+                    MozPromiseHolder<GenericPromise>&& aPromise,
+                    AbstractThread* aMainThread);
   ~DecodedStreamData();
   void SetPlaying(bool aPlaying);
   MediaEventSource<int64_t>& OnOutput();
   void Forget();
   nsCString GetDebugInfo();
 
   /* The following group of fields are protected by the decoder's monitor
    * and can be read or written on any thread.
@@ -151,35 +157,38 @@ public:
   const RefPtr<SourceMediaStream> mStream;
   const RefPtr<DecodedStreamGraphListener> mListener;
   bool mPlaying;
   // True if we need to send a compensation video frame to ensure the
   // StreamTime going forward.
   bool mEOSVideoCompensation;
 
   const RefPtr<OutputStreamManager> mOutputStreamManager;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 DecodedStreamData::DecodedStreamData(OutputStreamManager* aOutputStreamManager,
                                      PlaybackInfoInit&& aInit,
-                                     MozPromiseHolder<GenericPromise>&& aPromise)
+                                     MozPromiseHolder<GenericPromise>&& aPromise,
+                                     AbstractThread* aMainThread)
   : mAudioFramesWritten(0)
   , mNextVideoTime(aInit.mStartTime)
   , mNextAudioTime(aInit.mStartTime)
   , mHaveSentFinish(false)
   , mHaveSentFinishAudio(false)
   , mHaveSentFinishVideo(false)
-  , mStream(aOutputStreamManager->Graph()->CreateSourceStream())
+  , mStream(aOutputStreamManager->Graph()->CreateSourceStream(aMainThread))
   // DecodedStreamGraphListener will resolve this promise.
-  , mListener(new DecodedStreamGraphListener(mStream, Move(aPromise)))
+  , mListener(new DecodedStreamGraphListener(mStream, Move(aPromise), aMainThread))
   // mPlaying is initially true because MDSM won't start playback until playing
   // becomes true. This is consistent with the settings of AudioSink.
   , mPlaying(true)
   , mEOSVideoCompensation(false)
   , mOutputStreamManager(aOutputStreamManager)
+  , mAbstractMainThread(aMainThread)
 {
   mStream->AddListener(mListener);
   mOutputStreamManager->Connect(mStream);
 
   // Initialize tracks.
   if (aInit.mInfo.HasAudio()) {
     mStream->AddAudioTrack(aInit.mInfo.mAudio.mTrackId,
                            aInit.mInfo.mAudio.mRate,
@@ -202,17 +211,17 @@ DecodedStreamData::OnOutput()
   return mListener->OnOutput();
 }
 
 void
 DecodedStreamData::SetPlaying(bool aPlaying)
 {
   if (mPlaying != aPlaying) {
     mPlaying = aPlaying;
-    UpdateStreamSuspended(mStream, !mPlaying);
+    UpdateStreamSuspended(mAbstractMainThread, mStream, !mPlaying);
   }
 }
 
 void
 DecodedStreamData::Forget()
 {
   mListener->Forget();
 }
@@ -224,22 +233,24 @@ DecodedStreamData::GetDebugInfo()
     "DecodedStreamData=%p mPlaying=%d mAudioFramesWritten=%lld "
     "mNextAudioTime=%lld mNextVideoTime=%lld mHaveSentFinish=%d "
     "mHaveSentFinishAudio=%d mHaveSentFinishVideo=%d",
     this, mPlaying, mAudioFramesWritten, mNextAudioTime, mNextVideoTime,
     mHaveSentFinish, mHaveSentFinishAudio, mHaveSentFinishVideo);
 }
 
 DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
+                             AbstractThread* aMainThread,
                              MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue,
                              OutputStreamManager* aOutputStreamManager,
                              const bool& aSameOrigin,
                              const PrincipalHandle& aPrincipalHandle)
   : mOwnerThread(aOwnerThread)
+  , mAbstractMainThread(aMainThread)
   , mOutputStreamManager(aOutputStreamManager)
   , mPlaying(false)
   , mSameOrigin(aSameOrigin)
   , mPrincipalHandle(aPrincipalHandle)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
 {
 }
@@ -290,52 +301,55 @@ DecodedStream::Start(int64_t aStartTime,
   mLastOutputTime = 0;
   mInfo = aInfo;
   mPlaying = true;
   ConnectListener();
 
   class R : public Runnable {
     typedef MozPromiseHolder<GenericPromise> Promise;
   public:
-    R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager* aManager)
-      : mInit(Move(aInit)), mOutputStreamManager(aManager)
+    R(PlaybackInfoInit&& aInit, Promise&& aPromise,
+      OutputStreamManager* aManager, AbstractThread* aMainThread)
+      : mInit(Move(aInit)), mOutputStreamManager(aManager), mAbstractMainThread(aMainThread)
     {
       mPromise = Move(aPromise);
     }
     NS_IMETHOD Run() override
     {
       MOZ_ASSERT(NS_IsMainThread());
       // No need to create a source stream when there are no output streams. This
       // happens when RemoveOutput() is called immediately after StartPlayback().
       if (!mOutputStreamManager->Graph()) {
         // Resolve the promise to indicate the end of playback.
         mPromise.Resolve(true, __func__);
         return NS_OK;
       }
       mData = MakeUnique<DecodedStreamData>(
-        mOutputStreamManager, Move(mInit), Move(mPromise));
+        mOutputStreamManager, Move(mInit), Move(mPromise), mAbstractMainThread);
       return NS_OK;
     }
     UniquePtr<DecodedStreamData> ReleaseData()
     {
       return Move(mData);
     }
   private:
     PlaybackInfoInit mInit;
     Promise mPromise;
     RefPtr<OutputStreamManager> mOutputStreamManager;
     UniquePtr<DecodedStreamData> mData;
+    const RefPtr<AbstractThread> mAbstractMainThread;
   };
 
   MozPromiseHolder<GenericPromise> promise;
   mFinishPromise = promise.Ensure(__func__);
   PlaybackInfoInit init {
     aStartTime, aInfo
   };
-  nsCOMPtr<nsIRunnable> r = new R(Move(init), Move(promise), mOutputStreamManager);
+  nsCOMPtr<nsIRunnable> r =
+    new R(Move(init), Move(promise), mOutputStreamManager, mAbstractMainThread);
   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   SyncRunnable::DispatchToThread(mainThread, r);
   mData = static_cast<R*>(r.get())->ReleaseData();
 
   if (mData) {
     mOutputListener = mData->OnOutput().Connect(
       mOwnerThread, this, &DecodedStream::NotifyOutput);
     mData->SetPlaying(mPlaying);
@@ -383,17 +397,17 @@ DecodedStream::DestroyData(UniquePtr<Dec
 
   mOutputListener.Disconnect();
 
   DecodedStreamData* data = aData.release();
   data->Forget();
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     delete data;
   });
-  AbstractThread::MainThread()->Dispatch(r.forget());
+  mAbstractMainThread->Dispatch(r.forget());
 }
 
 void
 DecodedStream::SetPlaying(bool aPlaying)
 {
   AssertOwnerThread();
 
   // Resume/pause matters only when playback started.
--- a/dom/media/mediasink/DecodedStream.h
+++ b/dom/media/mediasink/DecodedStream.h
@@ -29,16 +29,17 @@ class TimeStamp;
 
 template <class T> class MediaQueue;
 
 class DecodedStream : public media::MediaSink {
   using media::MediaSink::PlaybackParams;
 
 public:
   DecodedStream(AbstractThread* aOwnerThread,
+                AbstractThread* aMainThread,
                 MediaQueue<MediaData>& aAudioQueue,
                 MediaQueue<MediaData>& aVideoQueue,
                 OutputStreamManager* aOutputStreamManager,
                 const bool& aSameOrigin,
                 const PrincipalHandle& aPrincipalHandle);
 
   // MediaSink functions.
   const PlaybackParams& GetPlaybackParams() const override;
@@ -80,16 +81,18 @@ private:
     MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   }
 
   void ConnectListener();
   void DisconnectListener();
 
   const RefPtr<AbstractThread> mOwnerThread;
 
+  const RefPtr<AbstractThread> mAbstractMainThread;
+
   /*
    * Main thread only members.
    */
   // Data about MediaStreams that are being fed by the decoder.
   const RefPtr<OutputStreamManager> mOutputStreamManager;
 
   /*
    * Worker thread only members.
--- a/dom/media/mediasource/AutoTaskQueue.h
+++ b/dom/media/mediasource/AutoTaskQueue.h
@@ -12,19 +12,22 @@
 #include "mozilla/TaskQueue.h"
 
 namespace mozilla {
 
 // A convenience TaskQueue not requiring explicit shutdown.
 class AutoTaskQueue : public AbstractThread
 {
 public:
-  explicit AutoTaskQueue(already_AddRefed<SharedThreadPool> aPool, bool aSupportsTailDispatch = false)
+  explicit AutoTaskQueue(already_AddRefed<SharedThreadPool> aPool,
+                         AbstractThread* aAbstractMainThread,
+                         bool aSupportsTailDispatch = false)
   : AbstractThread(aSupportsTailDispatch)
   , mTaskQueue(new TaskQueue(Move(aPool), aSupportsTailDispatch))
+  , mAbstractMainThread(aAbstractMainThread)
   {}
 
   TaskDispatcher& TailDispatcher() override
   {
     return mTaskQueue->TailDispatcher();
   }
 
   void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
@@ -44,16 +47,17 @@ public:
   bool IsCurrentThreadIn() override { return mTaskQueue->IsCurrentThreadIn(); }
 
 private:
   ~AutoTaskQueue()
   {
     RefPtr<TaskQueue> taskqueue = mTaskQueue;
     nsCOMPtr<nsIRunnable> task =
       NS_NewRunnableFunction([taskqueue]() { taskqueue->BeginShutdown(); });
-    AbstractThread::MainThread()->Dispatch(task.forget());
+    mAbstractMainThread->Dispatch(task.forget());
   }
   RefPtr<TaskQueue> mTaskQueue;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -449,16 +449,17 @@ MediaSource::Detach()
   mDecoder->DetachMediaSource();
   mDecoder = nullptr;
 }
 
 MediaSource::MediaSource(nsPIDOMWindowInner* aWindow)
   : DOMEventTargetHelper(aWindow)
   , mDecoder(nullptr)
   , mPrincipal(nullptr)
+  , mAbstractMainThread(GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other))
   , mReadyState(MediaSourceReadyState::Closed)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
   if (sop) {
--- a/dom/media/mediasource/MediaSource.h
+++ b/dom/media/mediasource/MediaSource.h
@@ -22,16 +22,17 @@
 #include "TimeUnits.h"
 
 struct JSContext;
 class JSObject;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 
+class AbstractThread;
 class ErrorResult;
 template <typename T> class AsyncEventRunner;
 class MediaResult;
 
 namespace dom {
 
 class GlobalObject;
 class SourceBuffer;
@@ -108,16 +109,21 @@ public:
   void GetMozDebugReaderData(nsAString& aString);
 
   bool HasLiveSeekableRange() const { return mLiveSeekableRange.isSome(); }
   media::TimeInterval LiveSeekableRange() const
   {
     return mLiveSeekableRange.value();
   }
 
+  AbstractThread* AbstractMainThread() const
+  {
+    return mAbstractMainThread;
+  }
+
 private:
   // SourceBuffer uses SetDuration and SourceBufferIsActive
   friend class mozilla::dom::SourceBuffer;
 
   ~MediaSource();
 
   explicit MediaSource(nsPIDOMWindowInner* aWindow);
 
@@ -138,16 +144,18 @@ private:
 
   RefPtr<MediaSourceDecoder> mDecoder;
   // Ensures the media element remains alive to dispatch progress and
   // durationchanged events.
   RefPtr<HTMLMediaElement> mMediaElement;
 
   RefPtr<nsIPrincipal> mPrincipal;
 
+  const RefPtr<AbstractThread> mAbstractMainThread;
+
   MediaSourceReadyState mReadyState;
 
   Maybe<media::TimeInterval> mLiveSeekableRange;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
 } // namespace dom
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -39,17 +39,17 @@ MediaSourceDecoder::Clone(MediaDecoderOw
   // TODO: Sort out cloning.
   return nullptr;
 }
 
 MediaDecoderStateMachine*
 MediaSourceDecoder::CreateStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mDemuxer = new MediaSourceDemuxer();
+  mDemuxer = new MediaSourceDemuxer(AbstractMainThread());
   mReader = new MediaFormatReader(this, mDemuxer, GetVideoFrameContainer());
   return new MediaDecoderStateMachine(this, mReader);
 }
 
 nsresult
 MediaSourceDecoder::Load(nsIStreamListener**)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -15,18 +15,19 @@
 #include "OpusDecoder.h"
 
 namespace mozilla {
 
 typedef TrackInfo::TrackType TrackType;
 using media::TimeUnit;
 using media::TimeIntervals;
 
-MediaSourceDemuxer::MediaSourceDemuxer()
+MediaSourceDemuxer::MediaSourceDemuxer(AbstractThread* aAbstractMainThread)
   : mTaskQueue(new AutoTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
+                                 aAbstractMainThread,
                                  /* aSupportsTailDispatch = */ false))
   , mMonitor("MediaSourceDemuxer")
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 // Due to inaccuracies in determining buffer end
 // frames (Bug 1065207). This value is based on videos seen in the wild.
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -15,23 +15,24 @@
 #include "MediaDataDemuxer.h"
 #include "MediaDecoderReader.h"
 #include "MediaResource.h"
 #include "MediaSource.h"
 #include "TrackBuffersManager.h"
 
 namespace mozilla {
 
+class AbstractThread;
 class MediaResult;
 class MediaSourceTrackDemuxer;
 
 class MediaSourceDemuxer : public MediaDataDemuxer
 {
 public:
-  explicit MediaSourceDemuxer();
+  explicit MediaSourceDemuxer(AbstractThread* aAbstractMainThread);
 
   RefPtr<InitPromise> Init() override;
 
   bool HasTrackType(TrackInfo::TrackType aType) const override;
 
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(TrackInfo::TrackType aType,
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -255,17 +255,17 @@ SourceBuffer::Remove(double aStart, doub
 void
 SourceBuffer::RangeRemoval(double aStart, double aEnd)
 {
   StartUpdating();
 
   RefPtr<SourceBuffer> self = this;
     mTrackBuffersManager->RangeRemoval(TimeUnit::FromSeconds(aStart),
                                        TimeUnit::FromSeconds(aEnd))
-      ->Then(AbstractThread::MainThread(), __func__,
+      ->Then(mAbstractMainThread, __func__,
              [self] (bool) {
                self->mPendingRemoval.Complete();
                self->StopUpdating();
              },
              []() { MOZ_ASSERT(false); })
       ->Track(mPendingRemoval);
 }
 
@@ -296,16 +296,17 @@ SourceBuffer::Ended()
   MSE_DEBUG("Ended");
   mTrackBuffersManager->Ended();
 }
 
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource,
                            const MediaContainerType& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
+  , mAbstractMainThread(aMediaSource->AbstractMainThread())
   , mCurrentAttributes(aType.Type() == MEDIAMIMETYPE("audio/mpeg") ||
                        aType.Type() == MEDIAMIMETYPE("audio/aac"))
   , mUpdating(false)
   , mActive(false)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
@@ -412,17 +413,17 @@ SourceBuffer::AppendData(const uint8_t* 
 
   RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aLength, aRv);
   if (!data) {
     return;
   }
   StartUpdating();
 
   mTrackBuffersManager->AppendData(data, mCurrentAttributes)
-    ->Then(AbstractThread::MainThread(), __func__, this,
+    ->Then(mAbstractMainThread, __func__, this,
            &SourceBuffer::AppendDataCompletedWithSuccess,
            &SourceBuffer::AppendDataErrored)
     ->Track(mPendingAppend);
 }
 
 void
 SourceBuffer::AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult)
 {
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -27,16 +27,17 @@
 #include "TrackBuffersManager.h"
 #include "SourceBufferTask.h"
 
 class JSObject;
 struct JSContext;
 
 namespace mozilla {
 
+class AbstractThread;
 class ErrorResult;
 class MediaByteBuffer;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
 class TimeRanges;
 
@@ -162,16 +163,17 @@ private:
   already_AddRefed<MediaByteBuffer> PrepareAppend(const uint8_t* aData,
                                                   uint32_t aLength,
                                                   ErrorResult& aRv);
 
   void AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult);
   void AppendDataErrored(const MediaResult& aError);
 
   RefPtr<MediaSource> mMediaSource;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 
   RefPtr<TrackBuffersManager> mTrackBuffersManager;
   SourceBufferAttributes mCurrentAttributes;
 
   bool mUpdating;
 
   mozilla::Atomic<bool> mActive;
 
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -91,16 +91,17 @@ TrackBuffersManager::TrackBuffersManager
   , mFirstInitializationSegmentReceived(false)
   , mNewMediaSegmentStarted(false)
   , mActiveTrack(false)
   , mType(aType)
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
+  , mAbstractMainThread(aParentDecoder->AbstractMainThread())
   , mEnded(false)
   , mVideoEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.video",
                                                  100 * 1024 * 1024))
   , mAudioEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.audio",
                                                  20 * 1024 * 1024))
   , mEvictionState(EvictionState::NO_EVICTION_NEEDED)
   , mMonitor("TrackBuffersManager")
 {
@@ -957,20 +958,20 @@ TrackBuffersManager::OnDemuxerInitDone(n
   }
 
   int64_t videoDuration = numVideos ? info.mVideo.mDuration : 0;
   int64_t audioDuration = numAudios ? info.mAudio.mDuration : 0;
 
   int64_t duration = std::max(videoDuration, audioDuration);
   // 1. Update the duration attribute if it currently equals NaN.
   // Those steps are performed by the MediaSourceDecoder::SetInitialDuration
-  AbstractThread::MainThread()->Dispatch(NewRunnableMethod<int64_t>
-                                         (mParentDecoder.get(),
-                                          &MediaSourceDecoder::SetInitialDuration,
-                                          duration ? duration : -1));
+  mAbstractMainThread->Dispatch(NewRunnableMethod<int64_t>
+                                (mParentDecoder.get(),
+                                &MediaSourceDecoder::SetInitialDuration,
+                                duration ? duration : -1));
 
   // 2. If the initialization segment has no audio, video, or text tracks, then
   // run the append error algorithm with the decode error parameter set to true
   // and abort these steps.
   if (!numVideos && !numAudios) {
     RejectAppend(NS_ERROR_FAILURE, __func__);
     return;
   }
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -21,16 +21,17 @@
 #include "SourceBufferTask.h"
 #include "TimeUnits.h"
 #include "nsAutoPtr.h"
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
+class AbstractThread;
 class ContainerParser;
 class MediaByteBuffer;
 class MediaRawData;
 class MediaSourceDemuxer;
 class SourceBufferResource;
 
 class SourceBufferTaskQueue
 {
@@ -462,16 +463,18 @@ private:
   UniquePtr<SourceBufferAttributes> mSourceBufferAttributes;
   // The current sourcebuffer append window. It's content is equivalent to
   // mSourceBufferAttributes.mAppendWindowStart/End
   media::TimeInterval mAppendWindow;
 
   // Strong references to external objects.
   nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
 
+  const RefPtr<AbstractThread> mAbstractMainThread;
+
   // Return public highest end time across all aTracks.
   // Monitor must be held.
   media::TimeUnit HighestEndTime(nsTArray<const media::TimeIntervals*>& aTracks) const;
 
   // Set to true if mediasource state changed to ended.
   Atomic<bool> mEnded;
 
   // Global size of this source buffer content.
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -4,16 +4,17 @@
  * 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 "nsError.h"
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
 #include "OggDemuxer.h"
 #include "OggCodecState.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "MediaDataDemuxer.h"
 #include "nsAutoRef.h"
 #include "XiphExtradata.h"
@@ -126,16 +127,17 @@ OggDemuxer::~OggDemuxer()
   if (HasAudio() || HasVideo()) {
     // If we were able to initialize our decoders, report whether we encountered
     // a chained stream or not.
     bool isChained = mIsChained;
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
       OGG_DEBUG("Reporting telemetry MEDIA_OGG_LOADED_IS_CHAINED=%d", isChained);
       Telemetry::Accumulate(Telemetry::ID::MEDIA_OGG_LOADED_IS_CHAINED, isChained);
     });
+    // Non-DocGroup version of AbstractThread::MainThread is fine for Telemetry.
     AbstractThread::MainThread()->Dispatch(task.forget());
   }
 }
 
 void
 OggDemuxer::SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
                               MediaEventProducer<void>* aOnSeekableEvent)
 {
--- a/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp
+++ b/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp
@@ -71,17 +71,17 @@ MediaDataDecoderProxy::Drain()
 void
 MediaDataDecoderProxy::Shutdown()
 {
   // Note that this *may* be called from the proxy thread also.
   MOZ_ASSERT(!mIsShutdown);
 #if defined(DEBUG)
   mIsShutdown = true;
 #endif
-  mProxyThread->AsXPCOMThread()->Dispatch(NewRunnableMethod(mProxyDecoder,
+  mProxyThread->AsEventTarget()->Dispatch(NewRunnableMethod(mProxyDecoder,
                                                             &MediaDataDecoder::Shutdown),
                                           NS_DISPATCH_SYNC);
 }
 
 void
 MediaDataDecoderProxy::FlushComplete()
 {
   mFlushComplete.Set(true);
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WMFAudioMFTManager.h"
 #include "MediaInfo.h"
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "nsTArray.h"
 #include "TimeUnits.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Logging.h"
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 static void
@@ -249,16 +250,17 @@ WMFAudioMFTManager::Output(int64_t aStre
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   if (!sample) {
     LOG("Audio MFTDecoder returned success but null output.");
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([]() -> void {
       LOG("Reporting telemetry AUDIO_MFT_OUTPUT_NULL_SAMPLES");
       Telemetry::Accumulate(Telemetry::ID::AUDIO_MFT_OUTPUT_NULL_SAMPLES, 1);
     });
+    // Non-DocGroup version of AbstractThread::MainThread is fine for Telemetry.
     AbstractThread::MainThread()->Dispatch(task.forget());
     return E_FAIL;
   }
 
   RefPtr<IMFMediaBuffer> buffer;
   hr = sample->ConvertToContiguousBuffer(getter_AddRefs(buffer));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -11,16 +11,17 @@
 #include "MediaDecoderReader.h"
 #include "gfxPrefs.h"
 #include "WMFUtils.h"
 #include "ImageContainer.h"
 #include "VideoUtils.h"
 #include "DXVA2Manager.h"
 #include "nsThreadUtils.h"
 #include "Layers.h"
+#include "mozilla/AbstractThread.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "MediaInfo.h"
 #include "mozilla/Logging.h"
 #include "nsWindowsHelpers.h"
 #include "gfx2DGlue.h"
 #include "gfxWindowsPlatform.h"
 #include "IMFYCbCrImage.h"
@@ -125,16 +126,17 @@ WMFVideoMFTManager::~WMFVideoMFTManager(
                        mGotExcessiveNullOutput ? 2 :
                        mGotValidOutputAfterNullOutput ? 3 :
                        4;
 
   nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
     LOG(nsPrintfCString("Reporting telemetry VIDEO_MFT_OUTPUT_NULL_SAMPLES=%d", telemetry).get());
     Telemetry::Accumulate(Telemetry::ID::VIDEO_MFT_OUTPUT_NULL_SAMPLES, telemetry);
   });
+  // Non-DocGroup version of AbstractThread::MainThread is fine for Telemetry.
   AbstractThread::MainThread()->Dispatch(task.forget());
 }
 
 const GUID&
 WMFVideoMFTManager::GetMFTGUID()
 {
   MOZ_ASSERT(mStreamType != Unknown);
   switch (mStreamType) {
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -257,17 +257,18 @@ public:
 
     bool newInputMuted = aInput.IsNull() || aInput.IsMuted();
     if (newInputMuted != mLastInputMuted) {
       mLastInputMuted = newInputMuted;
 
       RefPtr<InputMutedRunnable> runnable =
         new InputMutedRunnable(aStream, newInputMuted);
       aStream->Graph()->
-        DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+        DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                   runnable.forget());
     }
   }
 
   bool IsActive() const override
   {
     // Keep processing to track stream time, which is used for all timelines
     // associated with the same AudioContext.  If there are no other engines
     // for the AudioContext, then this could return false to suspend the
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -49,16 +49,17 @@ AudioNode::AudioNode(AudioContext* aCont
                      ChannelInterpretation aChannelInterpretation)
   : DOMEventTargetHelper(aContext->GetParentObject())
   , mContext(aContext)
   , mChannelCount(aChannelCount)
   , mChannelCountMode(aChannelCountMode)
   , mChannelInterpretation(aChannelInterpretation)
   , mId(gId++)
   , mPassThrough(false)
+  , mAbstractMainThread(aContext->GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other))
 {
   MOZ_ASSERT(aContext);
   DOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
   aContext->RegisterNode(this);
 }
 
 AudioNode::~AudioNode()
 {
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -15,16 +15,18 @@
 #include "MediaStreamGraph.h"
 #include "WebAudioUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsWeakReference.h"
 #include "SelfRef.h"
 
 namespace mozilla {
 
+class AbstractThread;
+
 namespace dom {
 
 class AudioContext;
 class AudioBufferSourceNode;
 class AudioParam;
 class AudioParamTimeline;
 struct ThreeDPoint;
 
@@ -220,16 +222,18 @@ public:
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   // Returns a string from constant static storage identifying the dom node
   // type.
   virtual const char* NodeType() const = 0;
 
+  AbstractThread* AbstractMainThread() const { return mAbstractMainThread; }
+
 private:
   // Given:
   //
   // - a DestinationType, that can be an AudioNode or an AudioParam ;
   // - a Predicate, a function that takes an InputNode& and returns a bool ;
   //
   // This method iterates on the InputNodes() of the node at the index
   // aDestinationIndex, and calls `DisconnectFromOutputIfConnected` with this
@@ -284,14 +288,16 @@ private:
   nsTArray<RefPtr<AudioParam> > mOutputParams;
   uint32_t mChannelCount;
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
   const uint32_t mId;
   // Whether the node just passes through its input.  This is a devtools API that
   // only works for some node types.
   bool mPassThrough;
+  // DocGroup-specifc AbstractThread::MainThread() for MediaStreamGraph operations.
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/AudioNodeEngine.cpp
+++ b/dom/media/webaudio/AudioNodeEngine.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AudioNodeEngine.h"
+
+#include "mozilla/AbstractThread.h"
 #ifdef BUILD_ARM_NEON
 #include "mozilla/arm.h"
 #include "AudioNodeEngineNEON.h"
 #endif
 #ifdef USE_SSE2
 #include "mozilla/SSE.h"
 #include "AlignmentUtils.h"
 #include "AudioNodeEngineSSE2.h"
@@ -370,16 +372,28 @@ AudioBufferSumOfSquares(const float* aIn
 
   while (aLength--) {
     sum += *aInput * *aInput;
     ++aInput;
   }
   return sum;
 }
 
+AudioNodeEngine::AudioNodeEngine(dom::AudioNode* aNode)
+  : mNode(aNode)
+  , mNodeType(aNode ? aNode->NodeType() : nullptr)
+  , mInputCount(aNode ? aNode->NumberOfInputs() : 1)
+  , mOutputCount(aNode ? aNode->NumberOfOutputs() : 0)
+  , mAbstractMainThread(
+      aNode ? aNode->AbstractMainThread() : AbstractThread::MainThread())
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_COUNT_CTOR(AudioNodeEngine);
+}
+
 void
 AudioNodeEngine::ProcessBlock(AudioNodeStream* aStream,
                               GraphTime aFrom,
                               const AudioBlock& aInput,
                               AudioBlock* aOutput,
                               bool* aFinished)
 {
   MOZ_ASSERT(mInputCount <= 1 && mOutputCount <= 1);
--- a/dom/media/webaudio/AudioNodeEngine.h
+++ b/dom/media/webaudio/AudioNodeEngine.h
@@ -15,16 +15,17 @@ namespace mozilla {
 
 namespace dom {
 struct ThreeDPoint;
 class AudioParamTimeline;
 class DelayNodeEngine;
 struct AudioTimelineEvent;
 } // namespace dom
 
+class AbstractThread;
 class AudioBlock;
 class AudioNodeStream;
 
 /**
  * This class holds onto a set of immutable channel buffers. The storage
  * for the buffers must be malloced, but the buffer pointers and the malloc
  * pointers can be different (e.g. if the buffers are contained inside
  * some malloced object).
@@ -250,25 +251,18 @@ AudioBufferSumOfSquares(const float* aIn
  * MediaStreamGraph thread.
  */
 class AudioNodeEngine
 {
 public:
   // This should be compatible with AudioNodeStream::OutputChunks.
   typedef AutoTArray<AudioBlock, 1> OutputChunks;
 
-  explicit AudioNodeEngine(dom::AudioNode* aNode)
-    : mNode(aNode)
-    , mNodeType(aNode ? aNode->NodeType() : nullptr)
-    , mInputCount(aNode ? aNode->NumberOfInputs() : 1)
-    , mOutputCount(aNode ? aNode->NumberOfOutputs() : 0)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_COUNT_CTOR(AudioNodeEngine);
-  }
+  explicit AudioNodeEngine(dom::AudioNode* aNode);
+
   virtual ~AudioNodeEngine()
   {
     MOZ_ASSERT(!mNode, "The node reference must be already cleared");
     MOZ_COUNT_DTOR(AudioNodeEngine);
   }
 
   virtual dom::DelayNodeEngine* AsDelayNodeEngine() { return nullptr; }
 
@@ -398,13 +392,16 @@ public:
     aUsage.mNodeType = mNodeType;
   }
 
 private:
   dom::AudioNode* mNode; // main thread only
   const char* const mNodeType;
   const uint16_t mInputCount;
   const uint16_t mOutputCount;
+
+protected:
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_AUDIONODEENGINE_H_ */
--- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp
@@ -9,37 +9,39 @@
 #include "AudioNodeExternalInputStream.h"
 #include "AudioChannelFormat.h"
 #include "mozilla/dom/MediaStreamAudioSourceNode.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-AudioNodeExternalInputStream::AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
-  : AudioNodeStream(aEngine, NO_STREAM_FLAGS, aSampleRate)
+AudioNodeExternalInputStream::AudioNodeExternalInputStream(
+  AudioNodeEngine* aEngine, TrackRate aSampleRate, AbstractThread* aMainThread)
+  : AudioNodeStream(aEngine, NO_STREAM_FLAGS, aSampleRate, aMainThread)
 {
   MOZ_COUNT_CTOR(AudioNodeExternalInputStream);
 }
 
 AudioNodeExternalInputStream::~AudioNodeExternalInputStream()
 {
   MOZ_COUNT_DTOR(AudioNodeExternalInputStream);
 }
 
 /* static */ already_AddRefed<AudioNodeExternalInputStream>
 AudioNodeExternalInputStream::Create(MediaStreamGraph* aGraph,
-                                     AudioNodeEngine* aEngine)
+                                     AudioNodeEngine* aEngine,
+                                     AbstractThread* aMainThread)
 {
   AudioContext* ctx = aEngine->NodeMainThread()->Context();
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aGraph->GraphRate() == ctx->SampleRate());
 
   RefPtr<AudioNodeExternalInputStream> stream =
-    new AudioNodeExternalInputStream(aEngine, aGraph->GraphRate());
+    new AudioNodeExternalInputStream(aEngine, aGraph->GraphRate(), aMainThread);
   stream->mSuspendedCount += ctx->ShouldSuspendNewStream();
   aGraph->AddStream(stream);
   return stream.forget();
 }
 
 /**
  * Copies the data in aInput to aOffsetInBlock within aBlock.
  * aBlock must have been allocated with AllocateInputBlock and have a channel
--- a/dom/media/webaudio/AudioNodeExternalInputStream.h
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.h
@@ -7,30 +7,33 @@
 #define MOZILLA_AUDIONODEEXTERNALINPUTSTREAM_H_
 
 #include "MediaStreamGraph.h"
 #include "AudioNodeStream.h"
 #include "mozilla/Atomics.h"
 
 namespace mozilla {
 
+class AbstractThread;
+
 /**
  * This is a MediaStream implementation that acts for a Web Audio node but
  * unlike other AudioNodeStreams, supports any kind of MediaStream as an
  * input --- handling any number of audio tracks and handling blocking of
  * the input MediaStream.
  */
 class AudioNodeExternalInputStream final : public AudioNodeStream
 {
 public:
   static already_AddRefed<AudioNodeExternalInputStream>
-  Create(MediaStreamGraph* aGraph, AudioNodeEngine* aEngine);
+  Create(MediaStreamGraph* aGraph, AudioNodeEngine* aEngine, AbstractThread* aMainThread);
 
 protected:
-  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate);
+  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate,
+                               AbstractThread* aMainThread);
   ~AudioNodeExternalInputStream();
 
 public:
   void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
 private:
   /**
    * Determines if this is enabled or not.  Disabled nodes produce silence.
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -24,18 +24,19 @@ namespace mozilla {
  * for regular audio contexts, and the rate requested by the web content
  * for offline audio contexts.
  * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
  * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
  */
 
 AudioNodeStream::AudioNodeStream(AudioNodeEngine* aEngine,
                                  Flags aFlags,
-                                 TrackRate aSampleRate)
-  : ProcessedMediaStream(),
+                                 TrackRate aSampleRate,
+                                 AbstractThread* aMainThread)
+  : ProcessedMediaStream(aMainThread),
     mEngine(aEngine),
     mSampleRate(aSampleRate),
     mFlags(aFlags),
     mNumberOfInputChannels(2),
     mIsActive(aEngine->IsActive()),
     mMarkAsFinishedAfterThisBlock(false),
     mAudioParamStream(false),
     mPassThrough(false)
@@ -72,17 +73,18 @@ AudioNodeStream::Create(AudioContext* aC
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(aGraph);
 
   // MediaRecorders use an AudioNodeStream, but no AudioNode
   AudioNode* node = aEngine->NodeMainThread();
 
   RefPtr<AudioNodeStream> stream =
-    new AudioNodeStream(aEngine, aFlags, aGraph->GraphRate());
+    new AudioNodeStream(aEngine, aFlags, aGraph->GraphRate(),
+      aCtx->GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other));
   stream->mSuspendedCount += aCtx->ShouldSuspendNewStream();
   if (node) {
     stream->SetChannelMixingParametersImpl(node->ChannelCount(),
                                            node->ChannelCountModeValue(),
                                            node->ChannelInterpretationValue());
   }
   aGraph->AddStream(stream);
   return stream.forget();
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -16,16 +16,17 @@
 namespace mozilla {
 
 namespace dom {
 struct ThreeDPoint;
 struct AudioTimelineEvent;
 class AudioContext;
 } // namespace dom
 
+class AbstractThread;
 class ThreadSharedFloatArrayBufferList;
 class AudioNodeEngine;
 
 typedef AlignedAutoTArray<float, GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE, 16> DownmixBufferType;
 
 /**
  * An AudioNodeStream produces one audio track with ID AUDIO_TRACK.
  * The start time of the AudioTrack is aligned to the start time of the
@@ -70,17 +71,18 @@ public:
          MediaStreamGraph* aGraph);
 
 protected:
   /**
    * Transfers ownership of aEngine to the new AudioNodeStream.
    */
   AudioNodeStream(AudioNodeEngine* aEngine,
                   Flags aFlags,
-                  TrackRate aSampleRate);
+                  TrackRate aSampleRate,
+                  AbstractThread* aMainThread);
 
   ~AudioNodeStream();
 
 public:
   // Control API
   /**
    * Sets a parameter that's a time relative to some stream's played time.
    * This time is converted to a time relative to this stream when it's set.
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -155,31 +155,33 @@ public:
       if (!hasTail) {
         if (!mBiquads.IsEmpty()) {
           mBiquads.Clear();
           aStream->ScheduleCheckForInactive();
 
           RefPtr<PlayingRefChangeHandler> refchanged =
             new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
           aStream->Graph()->
-            DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+            DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                       refchanged.forget());
         }
 
         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
         return;
       }
 
       PodArrayZero(inputBuffer);
 
     } else if(mBiquads.Length() != aInput.ChannelCount()){
       if (mBiquads.IsEmpty()) {
         RefPtr<PlayingRefChangeHandler> refchanged =
           new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
         aStream->Graph()->
-          DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
       } else { // Help people diagnose bug 924718
         WebAudioUtils::LogToDeveloperConsole(mWindowID,
                                              "BiquadFilterChannelCountChangeWarning");
       }
 
       // Adjust the number of biquads based on the number of channels
       mBiquads.SetLength(aInput.ChannelCount());
     }
--- a/dom/media/webaudio/BufferDecoder.cpp
+++ b/dom/media/webaudio/BufferDecoder.cpp
@@ -9,18 +9,21 @@
 #include "nsISupports.h"
 #include "MediaResource.h"
 #include "GMPCrashHelper.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS0(BufferDecoder)
 
-BufferDecoder::BufferDecoder(MediaResource* aResource, GMPCrashHelper* aCrashHelper)
+BufferDecoder::BufferDecoder(MediaResource* aResource,
+                             AbstractThread* aMainThread,
+                             GMPCrashHelper* aCrashHelper)
   : mResource(aResource)
+  , mAbstractMainThread(aMainThread)
   , mCrashHelper(aCrashHelper)
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 BufferDecoder::~BufferDecoder()
 {
   // The dtor may run on any thread, we cannot be sure.
@@ -67,9 +70,15 @@ BufferDecoder::GetOwner() const
 }
 
 already_AddRefed<GMPCrashHelper>
 BufferDecoder::GetCrashHelper()
 {
   return do_AddRef(mCrashHelper);
 }
 
+AbstractThread*
+BufferDecoder::AbstractMainThread() const
+{
+  return mAbstractMainThread;
+}
+
 } // namespace mozilla
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -19,17 +19,19 @@ namespace mozilla {
  * This class provides a decoder object which decodes a media file that lives in
  * a memory buffer.
  */
 class BufferDecoder final : public AbstractMediaDecoder
 {
 public:
   // This class holds a weak pointer to MediaResource.  It's the responsibility
   // of the caller to manage the memory of the MediaResource object.
-  explicit BufferDecoder(MediaResource* aResource, GMPCrashHelper* aCrashHelper);
+  explicit BufferDecoder(MediaResource* aResource,
+                         AbstractThread* aMainThread,
+                         GMPCrashHelper* aCrashHelper);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // This has to be called before decoding begins
   void BeginDecoding(TaskQueue* aTaskQueueIdentity);
 
   MediaResource* GetResource() const final override;
 
@@ -37,18 +39,21 @@ public:
 
   VideoFrameContainer* GetVideoFrameContainer() final override;
   layers::ImageContainer* GetImageContainer() final override;
 
   MediaDecoderOwner* GetOwner() const final override;
 
   already_AddRefed<GMPCrashHelper> GetCrashHelper() override;
 
+  AbstractThread* AbstractMainThread() const final override;
+
 private:
   virtual ~BufferDecoder();
   RefPtr<TaskQueue> mTaskQueueIdentity;
   RefPtr<MediaResource> mResource;
+  const RefPtr<AbstractThread> mAbstractMainThread;
   RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif /* BUFFER_DECODER_H_ */
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -121,17 +121,18 @@ public:
         WriteZeroesToAudioBlock(&input, 0, WEBAUDIO_BLOCK_SIZE);
       } else {
         if (mLeftOverData != INT32_MIN) {
           mLeftOverData = INT32_MIN;
           aStream->ScheduleCheckForInactive();
           RefPtr<PlayingRefChanged> refchanged =
             new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
           aStream->Graph()->
-            DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+            DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                       refchanged.forget());
         }
         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
         return;
       }
     } else {
       if (aInput.mVolume != 1.0f) {
         // Pre-multiply the input's volume
         uint32_t numChannels = aInput.ChannelCount();
@@ -142,17 +143,18 @@ public:
           AudioBlockCopyChannelWithScale(src, aInput.mVolume, dest);
         }
       }
 
       if (mLeftOverData <= 0) {
         RefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
         aStream->Graph()->
-          DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
       }
       mLeftOverData = mBufferLength;
       MOZ_ASSERT(mLeftOverData > 0);
     }
     aOutput->AllocateChannels(2);
 
     mReverb->process(&input, aOutput);
   }
--- a/dom/media/webaudio/DelayNode.cpp
+++ b/dom/media/webaudio/DelayNode.cpp
@@ -78,33 +78,35 @@ public:
   {
     MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
 
     if (!aInput.IsSilentOrSubnormal()) {
       if (mLeftOverData <= 0) {
         RefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
         aStream->Graph()->
-          DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
       }
       mLeftOverData = mBuffer.MaxDelayTicks();
     } else if (mLeftOverData > 0) {
       mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
     } else {
       if (mLeftOverData != INT32_MIN) {
         mLeftOverData = INT32_MIN;
         aStream->ScheduleCheckForInactive();
 
         // Delete our buffered data now we no longer need it
         mBuffer.Reset();
 
         RefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
         aStream->Graph()->
-          DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
       }
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
     mBuffer.Write(aInput);
 
     // Skip output update if mLastChunks has already been set by
--- a/dom/media/webaudio/IIRFilterNode.cpp
+++ b/dom/media/webaudio/IIRFilterNode.cpp
@@ -52,30 +52,32 @@ public:
         // as well.
         if (allZero) {
           mIIRFilters.Clear();
           aStream->ScheduleCheckForInactive();
 
           RefPtr<PlayingRefChangeHandler> refchanged =
             new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
           aStream->Graph()->
-            DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+            DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                       refchanged.forget());
 
           aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
           return;
         }
 
         PodZero(alignedInputBuffer, WEBAUDIO_BLOCK_SIZE);
       }
     } else if(mIIRFilters.Length() != aInput.ChannelCount()){
       if (mIIRFilters.IsEmpty()) {
         RefPtr<PlayingRefChangeHandler> refchanged =
           new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
         aStream->Graph()->
-          DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
       } else {
         WebAudioUtils::LogToDeveloperConsole(mWindowID,
                                              "IIRFilterChannelCountChangeWarning");
       }
 
       // Adjust the number of filters based on the number of channels
       mIIRFilters.SetLength(aInput.ChannelCount());
       for (size_t i = 0; i < aInput.ChannelCount(); ++i) {
--- a/dom/media/webaudio/MediaBufferDecoder.cpp
+++ b/dom/media/webaudio/MediaBufferDecoder.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaBufferDecoder.h"
 #include "BufferDecoder.h"
 #include "mozilla/dom/AudioContextBinding.h"
 #include "mozilla/dom/BaseAudioContextBinding.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/AbstractThread.h"
 #include <speex/speex_resampler.h>
 #include "nsXPCOMCIDInternal.h"
 #include "nsComponentManagerUtils.h"
 #include "MediaDecoderReader.h"
 #include "BufferMediaResource.h"
 #include "DecoderTraits.h"
 #include "AudioContext.h"
 #include "AudioBuffer.h"
@@ -181,30 +182,34 @@ private:
   nsWeakPtr mParent;
 };
 
 bool
 MediaDecodeTask::CreateReader()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  nsPIDOMWindowInner* parent = mDecodeJob.mContext->GetParentObject();
+  MOZ_ASSERT(parent);
 
   nsCOMPtr<nsIPrincipal> principal;
-  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mDecodeJob.mContext->GetParentObject());
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(parent);
   if (sop) {
     principal = sop->GetPrincipal();
   }
 
   RefPtr<BufferMediaResource> resource =
     new BufferMediaResource(static_cast<uint8_t*> (mBuffer),
                             mLength, principal, mContainerType);
 
   MOZ_ASSERT(!mBufferDecoder);
-  mBufferDecoder = new BufferDecoder(resource,
-    new BufferDecoderGMPCrashHelper(mDecodeJob.mContext->GetParentObject()));
+  RefPtr<AbstractThread> mainThread =
+    mDecodeJob.mContext->GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other);
+  mBufferDecoder = new BufferDecoder(resource, mainThread,
+                                     new BufferDecoderGMPCrashHelper(parent));
 
   // If you change this list to add support for new decoders, please consider
   // updating HTMLMediaElement::CreateDecoder as well.
 
   mDecoderReader = DecoderTraits::CreateReader(mContainerType, mBufferDecoder);
 
   if (!mDecoderReader) {
     return false;
@@ -281,16 +286,17 @@ MediaDecodeTask::OnMetadataRead(Metadata
 
   nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([codec]() -> void {
     MOZ_ASSERT(!codec.IsEmpty());
     MOZ_LOG(gMediaDecoderLog,
             LogLevel::Debug,
             ("Telemetry (WebAudio) MEDIA_CODEC_USED= '%s'", codec.get()));
     Telemetry::Accumulate(Telemetry::ID::MEDIA_CODEC_USED, codec);
   });
+  // Non-DocGroup version of AbstractThread::MainThread is fine for Telemetry.
   AbstractThread::MainThread()->Dispatch(task.forget());
 
   RequestSample();
 }
 
 void
 MediaDecodeTask::OnMetadataNotRead(const MediaResult& aReason)
 {
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -79,17 +79,19 @@ MediaStreamAudioSourceNode::Init(DOMMedi
   MediaStreamGraph* graph = Context()->Graph();
   if (NS_WARN_IF(graph != inputStream->Graph())) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   mInputStream = aMediaStream;
   AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this);
-  mStream = AudioNodeExternalInputStream::Create(graph, engine);
+  mStream =
+    AudioNodeExternalInputStream::Create(graph, engine,
+                                         aMediaStream->AbstractMainThread());
   mInputStream->AddConsumerToKeepAlive(static_cast<nsIDOMEventTarget*>(this));
 
   mInputStream->RegisterTrackListener(this);
   AttachToFirstTrack(mInputStream);
 }
 
 void
 MediaStreamAudioSourceNode::Destroy()
--- a/dom/media/webaudio/PannerNode.cpp
+++ b/dom/media/webaudio/PannerNode.cpp
@@ -204,27 +204,29 @@ public:
         if (mLeftOverData != INT_MIN) {
           mLeftOverData = INT_MIN;
           aStream->ScheduleCheckForInactive();
           mHRTFPanner->reset();
 
           RefPtr<PlayingRefChangeHandler> refchanged =
             new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
           aStream->Graph()->
-            DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
         }
         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
         return;
       }
     } else if (mPanningModelFunction == &PannerNodeEngine::HRTFPanningFunction) {
       if (mLeftOverData == INT_MIN) {
         RefPtr<PlayingRefChangeHandler> refchanged =
           new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
         aStream->Graph()->
-          DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
+          DispatchToMainThreadAfterStreamStateUpdate(mAbstractMainThread,
+                                                     refchanged.forget());
       }
       mLeftOverData = mHRTFPanner->maxTailFrames();
     }
 
     StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
     (this->*mPanningModelFunction)(aInput, aOutput, tick);
   }
 
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -26,21 +26,23 @@ extern mozilla::LogModule* GetSpeechSynt
 #define AUDIO_TRACK 1
 
 namespace mozilla {
 namespace dom {
 
 class SynthStreamListener : public MediaStreamListener
 {
 public:
-  explicit SynthStreamListener(nsSpeechTask* aSpeechTask,
-                               MediaStream* aStream) :
-    mSpeechTask(aSpeechTask),
-    mStream(aStream),
-    mStarted(false)
+  SynthStreamListener(nsSpeechTask* aSpeechTask,
+                      MediaStream* aStream,
+                      AbstractThread* aMainThread)
+    : mSpeechTask(aSpeechTask)
+    , mStream(aStream)
+    , mStarted(false)
+    , mAbstractMainThread(aMainThread)
   {
   }
 
   void DoNotifyStarted()
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchStartInner();
     }
@@ -57,54 +59,56 @@ public:
   void NotifyEvent(MediaStreamGraph* aGraph,
                    MediaStreamGraphEvent event) override
   {
     switch (event) {
       case MediaStreamGraphEvent::EVENT_FINISHED:
         {
           if (!mStarted) {
             mStarted = true;
-            nsCOMPtr<nsIRunnable> startRunnable =
-              NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
-            aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget());
+            aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+              mAbstractMainThread,
+              NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted));
           }
 
-          nsCOMPtr<nsIRunnable> endRunnable =
-            NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
-          aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
+          aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+            mAbstractMainThread,
+            NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished));
         }
         break;
       case MediaStreamGraphEvent::EVENT_REMOVED:
         mSpeechTask = nullptr;
         // Dereference MediaStream to destroy safety
         mStream = nullptr;
         break;
       default:
         break;
     }
   }
 
   void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) override
   {
     if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
       mStarted = true;
-      nsCOMPtr<nsIRunnable> event =
-        NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+        mAbstractMainThread,
+        NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted));
     }
   }
 
 private:
   // Raw pointer; if we exist, the stream exists,
   // and 'mSpeechTask' exclusively owns it and therefor exists as well.
   nsSpeechTask* mSpeechTask;
   // This is KungFuDeathGrip for MediaStream
   RefPtr<MediaStream> mStream;
 
   bool mStarted;
+
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 // nsSpeechTask
 
 NS_IMPL_CYCLE_COLLECTION(nsSpeechTask, mSpeechSynthesis, mUtterance, mCallback);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSpeechTask)
   NS_INTERFACE_MAP_ENTRY(nsISpeechTask)
@@ -159,17 +163,17 @@ nsSpeechTask::~nsSpeechTask()
   }
 }
 
 void
 nsSpeechTask::InitDirectAudio()
 {
   mStream = MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
                                           AudioChannel::Normal)->
-    CreateSourceStream();
+    CreateSourceStream(AbstractThread::MainThread() /* Non DocGroup-version for the task in parent. */);
   mIndirectAudio = false;
   mInited = true;
 }
 
 void
 nsSpeechTask::InitIndirectAudio()
 {
   mIndirectAudio = true;
@@ -198,17 +202,19 @@ nsSpeechTask::Setup(nsISpeechTaskCallbac
       NS_WARNING("Audio info arguments in Setup() are ignored for indirect audio services.");
     }
     return NS_OK;
   }
 
   // mStream is set up in Init() that should be called before this.
   MOZ_ASSERT(mStream);
 
-  mStream->AddListener(new SynthStreamListener(this, mStream));
+  mStream->AddListener(
+    // Non DocGroup-version of AbstractThread::MainThread for the task in parent.
+    new SynthStreamListener(this, mStream, AbstractThread::MainThread()));
 
   // XXX: Support more than one channel
   if(NS_WARN_IF(!(aChannels == 1))) {
     return NS_ERROR_FAILURE;
   }
 
   mChannels = aChannels;
 
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -163,19 +163,20 @@ nsCSPContext::ShouldLoad(nsContentPolicy
     }
 
     nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aRequestContext);
     if (script && script->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER) {
       parserCreated = true;
     }
   }
 
-  // aExtra is only non-null if the channel got redirected.
-  bool wasRedirected = (aExtra != nullptr);
+  // aExtra holds the original URI of the channel if the
+  // channel got redirected (until we fix Bug 1332422).
   nsCOMPtr<nsIURI> originalURI = do_QueryInterface(aExtra);
+  bool wasRedirected = originalURI;
 
   bool permitted = permitsInternal(dir,
                                    aContentLocation,
                                    originalURI,
                                    nonce,
                                    wasRedirected,
                                    isPreload,
                                    false,     // allow fallback to default-src
--- a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html
+++ b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html
@@ -56,16 +56,17 @@ var policy = {
 
     // make sure we get the right amount of content policy calls
     // e.g. about:blank also gets chrcked by content policies
     if (contentLocation.asciiSpec === EXPECTED_URL) {
       is(contentType, EXPECTED_CONTENT_TYPE,
          "content policy type should TYPESUBDOCUMENT");
       categoryManager.deleteCategoryEntry("content-policy", POLICYNAME, false);
       SimpleTest.finish();
+      return Ci.nsIContentPolicy.REJECT_REQUEST;
     }
     return Ci.nsIContentPolicy.ACCEPT;
   },
 
   shouldProcess: function(contentType, contentLocation, requestOrigin,
                           context, mimeTypeGuess, extra) {
     return Ci.nsIContentPolicy.ACCEPT;
   }
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -19,16 +19,17 @@
 
 #include "AudioChannelService.h"
 #include "AudioManager.h"
 
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsPrintfCString.h"
 
+#include "mozilla/AbstractThread.h"
 #include "mozilla/Hal.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "base/message_loop.h"
 
@@ -1030,17 +1031,18 @@ AudioManager::InitVolumeFromDatabase()
   nsCOMPtr<nsISettingsServiceLock> lock;
   rv = service->CreateLock(nullptr, getter_AddRefs(lock));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   RefPtr<VolumeInitCallback> callback = new VolumeInitCallback();
   MOZ_ASSERT(callback);
-  callback->GetPromise()->Then(AbstractThread::MainThread(), __func__, this,
+  callback->GetPromise()->Then(AbstractThread::MainThread(), // Non DocGroup-version for the task in parent.
+                               __func__, this,
                                &AudioManager::InitDeviceVolumeSucceeded,
                                &AudioManager::InitDeviceVolumeFailed);
 
   for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
     for (uint32_t idx2 = 0; idx2 < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx2) {
       lock->Get(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName,
                                             kAudioDeviceInfos[idx2].value).get(),
                 callback);
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -616,19 +616,19 @@ var interfaceNamesInGlobalScope =
     {name: "ImageCaptureErrorEvent", disabled: true},
 // 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!
     "InputEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "InstallTrigger",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "IntersectionObserver",
+    {name: "IntersectionObserver", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "IntersectionObserverEntry",
+    {name: "IntersectionObserverEntry", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyboardEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "KeyframeEffectReadOnly", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "KeyframeEffect", release: false},
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -157,61 +157,66 @@ nsString
 U2FStatus::GetResponse()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   MOZ_ASSERT(mIsStopped);
   return mResponse;
 }
 
 U2FTask::U2FTask(const nsAString& aOrigin, const nsAString& aAppId,
-                 const Authenticator& aAuthenticator)
+                 const Authenticator& aAuthenticator,
+                 AbstractThread* aMainThread)
   : mOrigin(aOrigin)
   , mAppId(aAppId)
   , mAuthenticator(aAuthenticator)
+  , mAbstractMainThread(aMainThread)
 {}
 
 U2FTask::~U2FTask()
 {}
 
 RefPtr<U2FPromise>
 U2FTask::Execute()
 {
   RefPtr<U2FPromise> p = mPromise.Ensure(__func__);
 
   nsCOMPtr<nsIRunnable> r(this);
 
   // TODO: Use a thread pool here, but we have to solve the PContentChild issues
   // of being in a worker thread.
-  AbstractThread::MainThread()->Dispatch(r.forget());
+  mAbstractMainThread->Dispatch(r.forget());
   return p;
 }
 
-U2FPrepTask::U2FPrepTask(const Authenticator& aAuthenticator)
+U2FPrepTask::U2FPrepTask(const Authenticator& aAuthenticator,
+                         AbstractThread* aMainThread)
   : mAuthenticator(aAuthenticator)
+  , mAbstractMainThread(aMainThread)
 {}
 
 U2FPrepTask::~U2FPrepTask()
 {}
 
 RefPtr<U2FPrepPromise>
 U2FPrepTask::Execute()
 {
   RefPtr<U2FPrepPromise> p = mPromise.Ensure(__func__);
 
   nsCOMPtr<nsIRunnable> r(this);
 
   // TODO: Use a thread pool here, but we have to solve the PContentChild issues
   // of being in a worker thread.
-  AbstractThread::MainThread()->Dispatch(r.forget());
+  mAbstractMainThread->Dispatch(r.forget());
   return p;
 }
 
 U2FIsRegisteredTask::U2FIsRegisteredTask(const Authenticator& aAuthenticator,
-                                         const LocalRegisteredKey& aRegisteredKey)
-  : U2FPrepTask(aAuthenticator)
+                                         const LocalRegisteredKey& aRegisteredKey,
+                                         AbstractThread* aMainThread)
+  : U2FPrepTask(aAuthenticator, aMainThread)
   , mRegisteredKey(aRegisteredKey)
 {}
 
 U2FIsRegisteredTask::~U2FIsRegisteredTask()
 {}
 
 NS_IMETHODIMP
 U2FIsRegisteredTask::Run()
@@ -257,18 +262,19 @@ U2FIsRegisteredTask::Run()
   return NS_OK;
 }
 
 U2FRegisterTask::U2FRegisterTask(const nsAString& aOrigin,
                                  const nsAString& aAppId,
                                  const Authenticator& aAuthenticator,
                                  const CryptoBuffer& aAppParam,
                                  const CryptoBuffer& aChallengeParam,
-                                 const LocalRegisterRequest& aRegisterEntry)
-  : U2FTask(aOrigin, aAppId, aAuthenticator)
+                                 const LocalRegisterRequest& aRegisterEntry,
+                                 AbstractThread* aMainThread)
+  : U2FTask(aOrigin, aAppId, aAuthenticator, aMainThread)
   , mAppParam(aAppParam)
   , mChallengeParam(aChallengeParam)
   , mRegisterEntry(aRegisterEntry)
 {}
 
 U2FRegisterTask::~U2FRegisterTask()
 {}
 
@@ -336,18 +342,19 @@ U2FRegisterTask::Run()
 
 U2FSignTask::U2FSignTask(const nsAString& aOrigin,
                          const nsAString& aAppId,
                          const nsAString& aVersion,
                          const Authenticator& aAuthenticator,
                          const CryptoBuffer& aAppParam,
                          const CryptoBuffer& aChallengeParam,
                          const CryptoBuffer& aClientData,
-                         const CryptoBuffer& aKeyHandle)
-  : U2FTask(aOrigin, aAppId, aAuthenticator)
+                         const CryptoBuffer& aKeyHandle,
+                         AbstractThread* aMainThread)
+  : U2FTask(aOrigin, aAppId, aAuthenticator, aMainThread)
   , mVersion(aVersion)
   , mAppParam(aAppParam)
   , mChallengeParam(aChallengeParam)
   , mClientData(aClientData)
   , mKeyHandle(aKeyHandle)
 {}
 
 U2FSignTask::~U2FSignTask()
@@ -424,19 +431,21 @@ U2FSignTask::Run()
   nsString responseStr;
   if (NS_WARN_IF(!response.ToJSON(responseStr))) {
     return NS_ERROR_FAILURE;
   }
   mPromise.Resolve(responseStr, __func__);
   return NS_OK;
 }
 
-U2FRunnable::U2FRunnable(const nsAString& aOrigin, const nsAString& aAppId)
+U2FRunnable::U2FRunnable(const nsAString& aOrigin, const nsAString& aAppId,
+                         AbstractThread* aMainThread)
   : mOrigin(aOrigin)
   , mAppId(aAppId)
+  , mAbstractMainThread(aMainThread)
 {}
 
 U2FRunnable::~U2FRunnable()
 {}
 
 // EvaluateAppIDAndRunTask determines whether the supplied FIDO AppID is valid for
 // the current FacetID, e.g., the current origin.
 // See https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-appid-and-facets.html
@@ -508,18 +517,19 @@ U2FRunnable::EvaluateAppID()
   return ErrorCode::BAD_REQUEST;
 }
 
 U2FRegisterRunnable::U2FRegisterRunnable(const nsAString& aOrigin,
                                          const nsAString& aAppId,
                                          const Sequence<RegisterRequest>& aRegisterRequests,
                                          const Sequence<RegisteredKey>& aRegisteredKeys,
                                          const Sequence<Authenticator>& aAuthenticators,
-                                         U2FRegisterCallback* aCallback)
-  : U2FRunnable(aOrigin, aAppId)
+                                         U2FRegisterCallback* aCallback,
+                                         AbstractThread* aMainThread)
+  : U2FRunnable(aOrigin, aAppId, aMainThread)
   , mAuthenticators(aAuthenticators)
   // U2FRegisterCallback does not support threadsafe refcounting, and must be
   // used and destroyed on main.
   , mCallback(new nsMainThreadPtrHolder<U2FRegisterCallback>(aCallback))
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The WebIDL dictionary types RegisterRequest and RegisteredKey cannot
@@ -612,25 +622,26 @@ U2FRegisterRunnable::Run()
   }
 
   // First, we must determine if any of the RegisteredKeys are already
   // registered, e.g., in the whitelist.
   for (LocalRegisteredKey key: mRegisteredKeys) {
     nsTArray<RefPtr<U2FPrepPromise>> prepPromiseList;
     for (size_t a = 0; a < mAuthenticators.Length(); ++a) {
       Authenticator token(mAuthenticators[a]);
-      RefPtr<U2FIsRegisteredTask> compTask = new U2FIsRegisteredTask(token, key);
+      RefPtr<U2FIsRegisteredTask> compTask =
+        new U2FIsRegisteredTask(token, key, mAbstractMainThread);
       prepPromiseList.AppendElement(compTask->Execute());
     }
 
     // Treat each call to Promise::All as a work unit, as it completes together
     status->WaitGroupAdd();
 
-    U2FPrepPromise::All(AbstractThread::MainThread(), prepPromiseList)
-    ->Then(AbstractThread::MainThread(), __func__,
+    U2FPrepPromise::All(mAbstractMainThread, prepPromiseList)
+    ->Then(mAbstractMainThread, __func__,
       [status] (const nsTArray<Authenticator>& aTokens) {
         MOZ_LOG(gU2FLog, LogLevel::Debug,
                 ("ALL: None of the RegisteredKeys were recognized. n=%d",
                  aTokens.Length()));
 
         status->WaitGroupDone();
       },
       [status] (ErrorCode aErrorCode) {
@@ -641,17 +652,17 @@ U2FRegisterRunnable::Run()
 
   // Wait for all the IsRegistered tasks to complete
   status->WaitGroupWait();
 
   // Check to see whether we're supposed to stop, because one of the keys was
   // recognized.
   if (status->IsStopped()) {
     status->WaitGroupAdd();
-    AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction(
+    mAbstractMainThread->Dispatch(NS_NewRunnableFunction(
       [status, this] () {
         RegisterResponse response;
         response.mErrorCode.Construct(
             static_cast<uint32_t>(status->GetErrorCode()));
         SendResponse(response);
         status->WaitGroupDone();
       }
     ));
@@ -692,20 +703,21 @@ U2FRegisterRunnable::Run()
       continue;
     }
 
     for (size_t a = 0; a < mAuthenticators.Length(); ++a) {
       Authenticator token(mAuthenticators[a]);
       RefPtr<U2FRegisterTask> registerTask = new U2FRegisterTask(mOrigin, mAppId,
                                                                  token, appParam,
                                                                  challengeParam,
-                                                                 req);
+                                                                 req,
+                                                                 mAbstractMainThread);
       status->WaitGroupAdd();
 
-      registerTask->Execute()->Then(AbstractThread::MainThread(), __func__,
+      registerTask->Execute()->Then(mAbstractMainThread, __func__,
         [status, this] (nsString aResponse) {
           if (status->IsStopped()) {
             return;
           }
           status->Stop(ErrorCode::OK, aResponse);
           status->WaitGroupDone();
         },
         [status, this] (ErrorCode aErrorCode) {
@@ -723,17 +735,17 @@ U2FRegisterRunnable::Run()
 
   // If none of the tasks completed, then nothing could satisfy.
   if (!status->IsStopped()) {
     status->Stop(ErrorCode::BAD_REQUEST);
   }
 
   // Transmit back to the JS engine from the Main Thread
   status->WaitGroupAdd();
-  AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction(
+  mAbstractMainThread->Dispatch(NS_NewRunnableFunction(
     [status, this] () {
       RegisterResponse response;
       if (status->GetErrorCode() == ErrorCode::OK) {
         response.Init(status->GetResponse());
       } else {
         response.mErrorCode.Construct(
             static_cast<uint32_t>(status->GetErrorCode()));
       }
@@ -747,18 +759,19 @@ U2FRegisterRunnable::Run()
   return NS_OK;
 }
 
 U2FSignRunnable::U2FSignRunnable(const nsAString& aOrigin,
                                  const nsAString& aAppId,
                                  const nsAString& aChallenge,
                                  const Sequence<RegisteredKey>& aRegisteredKeys,
                                  const Sequence<Authenticator>& aAuthenticators,
-                                 U2FSignCallback* aCallback)
-  : U2FRunnable(aOrigin, aAppId)
+                                 U2FSignCallback* aCallback,
+                                 AbstractThread* aMainThread)
+  : U2FRunnable(aOrigin, aAppId, aMainThread)
   , mAuthenticators(aAuthenticators)
   // U2FSignCallback does not support threadsafe refcounting, and must be used
   // and destroyed on main.
   , mCallback(new nsMainThreadPtrHolder<U2FSignCallback>(aCallback))
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Convert WebIDL objects to generic structs to pass between threads
@@ -878,20 +891,21 @@ U2FSignRunnable::Run()
     // available devices by preference, but is not an exclusion factor.
 
     for (size_t a = 0; a < mAuthenticators.Length() ; ++a) {
       Authenticator token(mAuthenticators[a]);
 
       RefPtr<U2FSignTask> signTask = new U2FSignTask(mOrigin, mAppId,
                                                      key.mVersion, token,
                                                      appParam, challengeParam,
-                                                     mClientData, keyHandle);
+                                                     mClientData, keyHandle,
+                                                     mAbstractMainThread);
       status->WaitGroupAdd();
 
-      signTask->Execute()->Then(AbstractThread::MainThread(), __func__,
+      signTask->Execute()->Then(mAbstractMainThread, __func__,
         [status, this] (nsString aResponse) {
           if (status->IsStopped()) {
             return;
           }
           status->Stop(ErrorCode::OK, aResponse);
           status->WaitGroupDone();
         },
         [status, this] (ErrorCode aErrorCode) {
@@ -909,17 +923,17 @@ U2FSignRunnable::Run()
 
   // If none of the tasks completed, then nothing could satisfy.
   if (!status->IsStopped()) {
     status->Stop(ErrorCode::DEVICE_INELIGIBLE);
   }
 
   // Transmit back to the JS engine from the Main Thread
   status->WaitGroupAdd();
-  AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction(
+  mAbstractMainThread->Dispatch(NS_NewRunnableFunction(
     [status, this] () {
       SignResponse response;
       if (status->GetErrorCode() == ErrorCode::OK) {
         response.Init(status->GetResponse());
       } else {
         response.mErrorCode.Construct(
           static_cast<uint32_t>(status->GetErrorCode()));
       }
@@ -996,16 +1010,18 @@ U2F::Init(nsPIDOMWindowInner* aParent, E
   if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
     if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
                                        mozilla::fallible)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
   }
 
+  mAbstractMainThread = doc->AbstractMainThreadFor(TaskCategory::Other);
+
   mInitialized = true;
 }
 
 void
 U2F::Register(const nsAString& aAppId,
               const Sequence<RegisterRequest>& aRegisterRequests,
               const Sequence<RegisteredKey>& aRegisteredKeys,
               U2FRegisterCallback& aCallback,
@@ -1019,17 +1035,18 @@ U2F::Register(const nsAString& aAppId,
     return;
   }
 
   RefPtr<SharedThreadPool> pool = SharedThreadPool::Get(kPoolName);
   RefPtr<U2FRegisterRunnable> task = new U2FRegisterRunnable(mOrigin, aAppId,
                                                              aRegisterRequests,
                                                              aRegisteredKeys,
                                                              mAuthenticators,
-                                                             &aCallback);
+                                                             &aCallback,
+                                                             mAbstractMainThread);
   pool->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
 }
 
 void
 U2F::Sign(const nsAString& aAppId,
           const nsAString& aChallenge,
           const Sequence<RegisteredKey>& aRegisteredKeys,
           U2FSignCallback& aCallback,
@@ -1041,14 +1058,15 @@ U2F::Sign(const nsAString& aAppId,
   if (!mInitialized) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   RefPtr<SharedThreadPool> pool = SharedThreadPool::Get(kPoolName);
   RefPtr<U2FSignRunnable> task = new U2FSignRunnable(mOrigin, aAppId, aChallenge,
                                                      aRegisteredKeys,
-                                                     mAuthenticators, &aCallback);
+                                                     mAuthenticators, &aCallback,
+                                                     mAbstractMainThread);
   pool->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -22,16 +22,19 @@
 #include "nsNSSShutDown.h"
 #include "nsPIDOMWindow.h"
 #include "nsProxyRelease.h"
 #include "nsWrapperCache.h"
 
 #include "U2FAuthenticator.h"
 
 namespace mozilla {
+
+class AbstractThread;
+
 namespace dom {
 
 class U2FRegisterCallback;
 class U2FSignCallback;
 
 // Defined in U2FBinding.h by the U2F.webidl; their use requires a JSContext.
 struct RegisterRequest;
 struct RegisteredKey;
@@ -58,54 +61,59 @@ typedef MozPromise<nsString, ErrorCode, 
 typedef MozPromise<Authenticator, ErrorCode, false> U2FPrepPromise;
 
 // U2FPrepTasks return lists of Authenticators that are OK to
 // proceed; they're useful for culling incompatible Authenticators.
 // Currently, only IsRegistered is supported.
 class U2FPrepTask : public Runnable
 {
 public:
-  explicit U2FPrepTask(const Authenticator& aAuthenticator);
+  explicit U2FPrepTask(const Authenticator& aAuthenticator,
+                       AbstractThread* aMainThread);
 
   RefPtr<U2FPrepPromise> Execute();
 
 protected:
   virtual ~U2FPrepTask();
 
   Authenticator mAuthenticator;
   MozPromiseHolder<U2FPrepPromise> mPromise;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 // Determine whether the provided Authenticator already knows
 // of the provided Registered Key.
 class U2FIsRegisteredTask final : public U2FPrepTask
 {
 public:
   U2FIsRegisteredTask(const Authenticator& aAuthenticator,
-                      const LocalRegisteredKey& aRegisteredKey);
+                      const LocalRegisteredKey& aRegisteredKey,
+                      AbstractThread* aMainThread);
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FIsRegisteredTask();
 
   LocalRegisteredKey mRegisteredKey;
 };
 
 class U2FTask : public Runnable
 {
 public:
   U2FTask(const nsAString& aOrigin,
           const nsAString& aAppId,
-          const Authenticator& aAuthenticator);
+          const Authenticator& aAuthenticator,
+          AbstractThread* aMainThread);
 
   RefPtr<U2FPromise> Execute();
 
   nsString mOrigin;
   nsString mAppId;
   Authenticator mAuthenticator;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 
 protected:
   virtual ~U2FTask();
 
   MozPromiseHolder<U2FPromise> mPromise;
 };
 
 // Use the provided Authenticator to Register a new scoped credential
@@ -113,17 +121,18 @@ protected:
 class U2FRegisterTask final : public U2FTask
 {
 public:
   U2FRegisterTask(const nsAString& aOrigin,
                   const nsAString& aAppId,
                   const Authenticator& aAuthenticator,
                   const CryptoBuffer& aAppParam,
                   const CryptoBuffer& aChallengeParam,
-                  const LocalRegisterRequest& aRegisterEntry);
+                  const LocalRegisterRequest& aRegisterEntry,
+                  AbstractThread* aMainThread);
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FRegisterTask();
 
   CryptoBuffer mAppParam;
   CryptoBuffer mChallengeParam;
   LocalRegisterRequest mRegisterEntry;
@@ -136,17 +145,18 @@ class U2FSignTask final : public U2FTask
 public:
   U2FSignTask(const nsAString& aOrigin,
               const nsAString& aAppId,
               const nsAString& aVersion,
               const Authenticator& aAuthenticator,
               const CryptoBuffer& aAppParam,
               const CryptoBuffer& aChallengeParam,
               const CryptoBuffer& aClientData,
-              const CryptoBuffer& aKeyHandle);
+              const CryptoBuffer& aKeyHandle,
+              AbstractThread* aMainThread);
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FSignTask();
 
   nsString mVersion;
   CryptoBuffer mAppParam;
   CryptoBuffer mChallengeParam;
@@ -184,41 +194,44 @@ private:
 };
 
 // U2FRunnables run to completion, performing a single U2F operation such as
 // registering, or signing.
 class U2FRunnable : public Runnable
                   , public nsNSSShutDownObject
 {
 public:
-  U2FRunnable(const nsAString& aOrigin, const nsAString& aAppId);
+  U2FRunnable(const nsAString& aOrigin, const nsAString& aAppId,
+              AbstractThread* aMainThread);
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
 protected:
   virtual ~U2FRunnable();
   ErrorCode EvaluateAppID();
 
   nsString mOrigin;
   nsString mAppId;
+  const RefPtr<AbstractThread> mAbstractMainThread;
 };
 
 // This U2FRunnable completes a single application-requested U2F Register
 // operation.
 class U2FRegisterRunnable : public U2FRunnable
 {
 public:
   U2FRegisterRunnable(const nsAString& aOrigin,
                       const nsAString& aAppId,
                       const Sequence<RegisterRequest>& aRegisterRequests,
                       const Sequence<RegisteredKey>& aRegisteredKeys,
                       const Sequence<Authenticator>& aAuthenticators,
-                      U2FRegisterCallback* aCallback);
+                      U2FRegisterCallback* aCallback,
+                      AbstractThread* aMainThread);
 
   void SendResponse(const RegisterResponse& aResponse);
   void SetTimeout(const int32_t aTimeoutMillis);
 
   NS_DECL_NSIRUNNABLE
 
 private:
   ~U2FRegisterRunnable();
@@ -234,17 +247,18 @@ private:
 class U2FSignRunnable : public U2FRunnable
 {
 public:
   U2FSignRunnable(const nsAString& aOrigin,
                   const nsAString& aAppId,
                   const nsAString& aChallenge,
                   const Sequence<RegisteredKey>& aRegisteredKeys,
                   const Sequence<Authenticator>& aAuthenticators,
-                  U2FSignCallback* aCallback);
+                  U2FSignCallback* aCallback,
+                  AbstractThread* aMainThread);
 
   void SendResponse(const SignResponse& aResponse);
   void SetTimeout(const int32_t aTimeoutMillis);
 
   NS_DECL_NSIRUNNABLE
 
 private:
   ~U2FSignRunnable();
@@ -300,16 +314,17 @@ public:
   virtual
   void virtualDestroyNSSReference() override {};
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   nsString mOrigin;
   Sequence<Authenticator> mAuthenticators;
   bool mInitialized;
+  RefPtr<AbstractThread> mAbstractMainThread;
 
   ~U2F();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_U2F_h
--- a/dom/webauthn/WebAuthentication.cpp
+++ b/dom/webauthn/WebAuthentication.cpp
@@ -847,17 +847,18 @@ WebAuthentication::MakeCredential(JSCont
     // the existence of C.
     U2FAuthMakeCredential(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
                           clientDataHash, aAccount, normalizedParams,
                           aOptions.mExcludeList, aOptions.mExtensions);
   }
 
   requestMonitor->CompleteTask();
 
-  monitorPromise->Then(AbstractThread::MainThread(), __func__,
+  monitorPromise->Then(
+    global->AbstractMainThreadFor(TaskCategory::Other), __func__,
     [promise] (CredentialPtr aInfo) {
       promise->MaybeResolve(aInfo);
     },
     [promise] (nsresult aErrorCode) {
       promise->MaybeReject(aErrorCode);
   });
 
   return promise.forget();
@@ -1037,17 +1038,18 @@ WebAuthentication::GetAssertion(const Ar
     // on this authenticator with rpIdHash, clientDataHash, credentialList, and
     // clientExtensions as parameters.
     U2FAuthGetAssertion(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
                         clientDataHash, credentialList, aOptions.mExtensions);
   }
 
   requestMonitor->CompleteTask();
 
-  monitorPromise->Then(AbstractThread::MainThread(), __func__,
+  monitorPromise->Then(
+    global->AbstractMainThreadFor(TaskCategory::Other), __func__,
     [promise] (AssertionPtr aAssertion) {
       promise->MaybeResolve(aAssertion);
     },
     [promise] (nsresult aErrorCode) {
       promise->MaybeReject(aErrorCode);
   });
 
   return promise.forget();
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -26,48 +26,51 @@ interface nsIXPConnectWrappedNative;
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native JSValPtr(JS::Value);
 [ptr] native JSFreeOpPtr(JSFreeOp);
 [ref] native JSCallArgsRef(const JS::CallArgs);
 [ref] native JSAutoIdVector(JS::AutoIdVector);
 [ptr] native jsClassPtr(const js::Class);
 [ptr] native JSClassPtr(const JSClass);
 
+%{ C++
+    // nsIXPCScriptable flags (only 32 bits available!). They are defined via
+    // #defines so they can be used in #ifndef guards in xpc_map_end.h.
+
+    #define XPC_SCRIPTABLE_WANT_PRECREATE                   (1 <<  0)
+    #define XPC_SCRIPTABLE_WANT_GETPROPERTY                 (1 <<  1)
+    #define XPC_SCRIPTABLE_WANT_SETPROPERTY                 (1 <<  2)
+    #define XPC_SCRIPTABLE_WANT_ENUMERATE                   (1 <<  3)
+    #define XPC_SCRIPTABLE_WANT_NEWENUMERATE                (1 <<  4)
+    #define XPC_SCRIPTABLE_WANT_RESOLVE                     (1 <<  5)
+    #define XPC_SCRIPTABLE_WANT_FINALIZE                    (1 <<  6)
+    #define XPC_SCRIPTABLE_WANT_CALL                        (1 <<  7)
+    #define XPC_SCRIPTABLE_WANT_CONSTRUCT                   (1 <<  8)
+    #define XPC_SCRIPTABLE_WANT_HASINSTANCE                 (1 <<  9)
+    #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY       (1 << 10)
+    #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY       (1 << 11)
+    #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY       (1 << 12)
+    #define XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE        (1 << 13)
+    #define XPC_SCRIPTABLE_DONT_ASK_INSTANCE_FOR_SCRIPTABLE (1 << 14)
+    #define XPC_SCRIPTABLE_CLASSINFO_INTERFACES_ONLY        (1 << 15)
+    #define XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE   (1 << 16)
+    #define XPC_SCRIPTABLE_ALLOW_PROP_MODS_TO_PROTOTYPE     (1 << 17)
+    #define XPC_SCRIPTABLE_IS_GLOBAL_OBJECT                 (1 << 18)
+    #define XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES     (1 << 19)
+%}
+
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
  * boolean to PR_TRUE before making the call.  Implementations may skip writing
  * to *_retval unless they want to return PR_FALSE.
  */
 [uuid(19b70b26-7c3f-437f-a04a-2a8f9e28b617)]
 interface nsIXPCScriptable : nsISupports
 {
-    /* bitflags used for 'flags' (only 32 bits available!) */
-
-    const uint32_t WANT_PRECREATE                   = 1 <<  0;
-    const uint32_t WANT_GETPROPERTY                 = 1 <<  1;
-    const uint32_t WANT_SETPROPERTY                 = 1 <<  2;
-    const uint32_t WANT_ENUMERATE                   = 1 <<  3;
-    const uint32_t WANT_NEWENUMERATE                = 1 <<  4;
-    const uint32_t WANT_RESOLVE                     = 1 <<  5;
-    const uint32_t WANT_FINALIZE                    = 1 <<  6;
-    const uint32_t WANT_CALL                        = 1 <<  7;
-    const uint32_t WANT_CONSTRUCT                   = 1 <<  8;
-    const uint32_t WANT_HASINSTANCE                 = 1 <<  9;
-    const uint32_t USE_JSSTUB_FOR_ADDPROPERTY       = 1 << 10;
-    const uint32_t USE_JSSTUB_FOR_DELPROPERTY       = 1 << 11;
-    const uint32_t USE_JSSTUB_FOR_SETPROPERTY       = 1 << 12;
-    const uint32_t DONT_ENUM_QUERY_INTERFACE        = 1 << 13;
-    const uint32_t DONT_ASK_INSTANCE_FOR_SCRIPTABLE = 1 << 14;
-    const uint32_t CLASSINFO_INTERFACES_ONLY        = 1 << 15;
-    const uint32_t ALLOW_PROP_MODS_DURING_RESOLVE   = 1 << 16;
-    const uint32_t ALLOW_PROP_MODS_TO_PROTOTYPE     = 1 << 17;
-    const uint32_t IS_GLOBAL_OBJECT                 = 1 << 18;
-    const uint32_t DONT_REFLECT_INTERFACE_NAMES     = 1 << 19;
-
     readonly attribute string className;
     [notxpcom,nostdcall] uint32_t getScriptableFlags();
     [notxpcom,nostdcall] jsClassPtr getClass();
     [notxpcom,nostdcall] JSClassPtr getJSClass();
 
     void   preCreate(in nsISupports nativeObj, in JSContextPtr cx,
                      in JSObjectPtr globalObj, out JSObjectPtr parentObj);
 
@@ -103,43 +106,43 @@ interface nsIXPCScriptable : nsISupports
 
     boolean hasInstance(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj,
                        in jsval val, out boolean bp);
 
     void postCreatePrototype(in JSContextPtr cx, in JSObjectPtr proto);
 
 %{ C++
-    #define GET_IT(f_) \
-    { \
-        return 0 != (GetScriptableFlags() & nsIXPCScriptable::f_); \
+    #define GET_IT(f_, c_) \
+    bool f_() { \
+        return 0 != (GetScriptableFlags() & XPC_SCRIPTABLE_##c_); \
     }
 
-    bool WantPreCreate()                GET_IT(WANT_PRECREATE)
-    bool WantGetProperty()              GET_IT(WANT_GETPROPERTY)
-    bool WantSetProperty()              GET_IT(WANT_SETPROPERTY)
-    bool WantEnumerate()                GET_IT(WANT_ENUMERATE)
-    bool WantNewEnumerate()             GET_IT(WANT_NEWENUMERATE)
-    bool WantResolve()                  GET_IT(WANT_RESOLVE)
-    bool WantFinalize()                 GET_IT(WANT_FINALIZE)
-    bool WantCall()                     GET_IT(WANT_CALL)
-    bool WantConstruct()                GET_IT(WANT_CONSTRUCT)
-    bool WantHasInstance()              GET_IT(WANT_HASINSTANCE)
-    bool UseJSStubForAddProperty()      GET_IT(USE_JSSTUB_FOR_ADDPROPERTY)
-    bool UseJSStubForDelProperty()      GET_IT(USE_JSSTUB_FOR_DELPROPERTY)
-    bool UseJSStubForSetProperty()      GET_IT(USE_JSSTUB_FOR_SETPROPERTY)
-    bool DontEnumQueryInterface()       GET_IT(DONT_ENUM_QUERY_INTERFACE)
-    bool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
-    bool ClassInfoInterfacesOnly()      GET_IT(CLASSINFO_INTERFACES_ONLY)
-    bool AllowPropModsDuringResolve()   GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
-    bool AllowPropModsToPrototype()     GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
-    bool IsGlobalObject()               GET_IT(IS_GLOBAL_OBJECT)
-    bool DontReflectInterfaceNames()    GET_IT(DONT_REFLECT_INTERFACE_NAMES)
+    GET_IT(WantPreCreate,                WANT_PRECREATE)
+    GET_IT(WantGetProperty,              WANT_GETPROPERTY)
+    GET_IT(WantSetProperty,              WANT_SETPROPERTY)
+    GET_IT(WantEnumerate,                WANT_ENUMERATE)
+    GET_IT(WantNewEnumerate,             WANT_NEWENUMERATE)
+    GET_IT(WantResolve,                  WANT_RESOLVE)
+    GET_IT(WantFinalize,                 WANT_FINALIZE)
+    GET_IT(WantCall,                     WANT_CALL)
+    GET_IT(WantConstruct,                WANT_CONSTRUCT)
+    GET_IT(WantHasInstance,              WANT_HASINSTANCE)
+    GET_IT(UseJSStubForAddProperty,      USE_JSSTUB_FOR_ADDPROPERTY)
+    GET_IT(UseJSStubForDelProperty,      USE_JSSTUB_FOR_DELPROPERTY)
+    GET_IT(UseJSStubForSetProperty,      USE_JSSTUB_FOR_SETPROPERTY)
+    GET_IT(DontEnumQueryInterface,       DONT_ENUM_QUERY_INTERFACE)
+    GET_IT(DontAskInstanceForScriptable, DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
+    GET_IT(ClassInfoInterfacesOnly,      CLASSINFO_INTERFACES_ONLY)
+    GET_IT(AllowPropModsDuringResolve,   ALLOW_PROP_MODS_DURING_RESOLVE)
+    GET_IT(AllowPropModsToPrototype,     ALLOW_PROP_MODS_TO_PROTOTYPE)
+    GET_IT(IsGlobalObject,               IS_GLOBAL_OBJECT)
+    GET_IT(DontReflectInterfaceNames,    DONT_REFLECT_INTERFACE_NAMES)
 
-#undef GET_IT
+    #undef GET_IT
 %}
 };
 
 %{ C++
 
 #include "nsAutoPtr.h"
 
 #define NS_XPCCLASSINFO_IID \
--- a/js/xpconnect/public/xpc_make_class.h
+++ b/js/xpconnect/public/xpc_make_class.h
@@ -73,103 +73,103 @@ void
 XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj);
 
 extern const js::ClassExtension XPC_WN_JSClassExtension;
 
 extern const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate;
 
 #define XPC_MAKE_CLASS_OPS(_flags) { \
     /* addProperty */ \
-    ((_flags) & nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY) \
+    ((_flags) & XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY) \
     ? nullptr \
-    : ((_flags) & nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE) \
+    : ((_flags) & XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
       ? XPC_WN_MaybeResolvingPropertyStub \
       : XPC_WN_CannotModifyPropertyStub, \
     \
     /* delProperty */ \
-    ((_flags) & nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY) \
+    ((_flags) & XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY) \
     ? nullptr \
-    : ((_flags) & nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE) \
+    : ((_flags) & XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
       ? XPC_WN_MaybeResolvingDeletePropertyStub \
       : XPC_WN_CannotDeletePropertyStub, \
     \
     /* getProperty */ \
-    ((_flags) & nsIXPCScriptable::WANT_GETPROPERTY) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_GETPROPERTY) \
     ? XPC_WN_Helper_GetProperty \
     : nullptr, \
     \
     /* setProperty */ \
-    ((_flags) & nsIXPCScriptable::WANT_SETPROPERTY) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_SETPROPERTY) \
     ? XPC_WN_Helper_SetProperty \
-    : ((_flags) & nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY) \
+    : ((_flags) & XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY) \
       ? nullptr \
-      : ((_flags) & nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE) \
+      : ((_flags) & XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
         ? XPC_WN_MaybeResolvingSetPropertyStub \
         : XPC_WN_CannotModifySetPropertyStub, \
     \
     /* enumerate */ \
-    ((_flags) & nsIXPCScriptable::WANT_NEWENUMERATE) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
     ? nullptr /* We will use oOps->enumerate set below in this case */ \
-    : ((_flags) & nsIXPCScriptable::WANT_ENUMERATE) \
+    : ((_flags) & XPC_SCRIPTABLE_WANT_ENUMERATE) \
       ? XPC_WN_Helper_Enumerate \
       : XPC_WN_Shared_Enumerate, \
     \
     /* resolve */ \
     /* We have to figure out resolve strategy at call time */ \
     XPC_WN_Helper_Resolve, \
     \
     /* mayResolve */ \
     nullptr, \
     \
     /* finalize */ \
-    ((_flags) & nsIXPCScriptable::WANT_FINALIZE) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_FINALIZE) \
     ? XPC_WN_Helper_Finalize \
     : XPC_WN_NoHelper_Finalize, \
     \
     /* call */ \
-    ((_flags) & nsIXPCScriptable::WANT_CALL) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_CALL) \
     ? XPC_WN_Helper_Call \
     : nullptr, \
     \
     /* hasInstance */ \
-    ((_flags) & nsIXPCScriptable::WANT_HASINSTANCE) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_HASINSTANCE) \
     ? XPC_WN_Helper_HasInstance \
     : nullptr, \
     \
     /* construct */ \
-    ((_flags) & nsIXPCScriptable::WANT_CONSTRUCT) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_CONSTRUCT) \
     ? XPC_WN_Helper_Construct \
     : nullptr, \
     \
     /* trace */ \
-    ((_flags) & nsIXPCScriptable::IS_GLOBAL_OBJECT) \
+    ((_flags) & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT) \
     ? JS_GlobalObjectTraceHook \
     : XPCWrappedNative_Trace, \
 }
 
 #define XPC_MAKE_CLASS(_name, _flags, _classOps) { \
     /* name */ \
     _name, \
     \
     /* flags */ \
     XPC_WRAPPER_FLAGS | \
     JSCLASS_PRIVATE_IS_NSISUPPORTS | \
     JSCLASS_IS_WRAPPED_NATIVE | \
-    (((_flags) & nsIXPCScriptable::IS_GLOBAL_OBJECT) \
+    (((_flags) & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT) \
      ? XPCONNECT_GLOBAL_FLAGS \
      : 0), \
     \
     /* cOps */ \
     _classOps, \
     \
     /* spec */ \
     nullptr, \
     \
     /* ext */ \
     &XPC_WN_JSClassExtension, \
     \
     /* oOps */ \
-    ((_flags) & nsIXPCScriptable::WANT_NEWENUMERATE) \
+    ((_flags) & XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
     ? &XPC_WN_ObjectOpsWithEnumerate \
     : nullptr, \
 }
 
 #endif
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -31,51 +31,17 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::GetClas
 }
 
 /**************************************************************/
 
 // virtual
 uint32_t
 XPC_MAP_CLASSNAME::GetScriptableFlags()
 {
-    return
-#ifdef XPC_MAP_WANT_PRECREATE
-    nsIXPCScriptable::WANT_PRECREATE |
-#endif
-#ifdef XPC_MAP_WANT_GETPROPERTY
-    nsIXPCScriptable::WANT_GETPROPERTY |
-#endif
-#ifdef XPC_MAP_WANT_SETPROPERTY
-    nsIXPCScriptable::WANT_SETPROPERTY |
-#endif
-#ifdef XPC_MAP_WANT_ENUMERATE
-    nsIXPCScriptable::WANT_ENUMERATE |
-#endif
-#ifdef XPC_MAP_WANT_NEWENUMERATE
-    nsIXPCScriptable::WANT_NEWENUMERATE |
-#endif
-#ifdef XPC_MAP_WANT_RESOLVE
-    nsIXPCScriptable::WANT_RESOLVE |
-#endif
-#ifdef XPC_MAP_WANT_FINALIZE
-    nsIXPCScriptable::WANT_FINALIZE |
-#endif
-#ifdef XPC_MAP_WANT_CALL
-    nsIXPCScriptable::WANT_CALL |
-#endif
-#ifdef XPC_MAP_WANT_CONSTRUCT
-    nsIXPCScriptable::WANT_CONSTRUCT |
-#endif
-#ifdef XPC_MAP_WANT_HASINSTANCE
-    nsIXPCScriptable::WANT_HASINSTANCE |
-#endif
-#ifdef XPC_MAP_FLAGS
-    XPC_MAP_FLAGS |
-#endif
-    0;
+    return (XPC_MAP_FLAGS);
 }
 
 // virtual
 const js::Class*
 XPC_MAP_CLASSNAME::GetClass()
 {
     static const js::ClassOps classOps =
         XPC_MAKE_CLASS_OPS(GetScriptableFlags());
@@ -89,107 +55,66 @@ XPC_MAP_CLASSNAME::GetClass()
 const JSClass*
 XPC_MAP_CLASSNAME::GetJSClass()
 {
     return Jsvalify(GetClass());
 }
 
 /**************************************************************/
 
-#ifndef XPC_MAP_WANT_PRECREATE
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_PRECREATE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreate(nsISupports* nativeObj, JSContext * cx, JSObject * globalObj, JSObject * *parentObj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_GETPROPERTY
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_GETPROPERTY)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::GetProperty(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, jsid id, JS::Value * vp, bool* _retval)
     {NS_WARNING("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_SETPROPERTY
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_SETPROPERTY)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::SetProperty(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, jsid id, JS::Value * vp, bool* _retval)
     {NS_WARNING("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_NEWENUMERATE
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_NEWENUMERATE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::AutoIdVector& properties, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_ENUMERATE
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_ENUMERATE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_RESOLVE
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_RESOLVE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Resolve(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, jsid id, bool* resolvedp, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_FINALIZE
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_FINALIZE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Finalize(nsIXPConnectWrappedNative* wrapper, JSFreeOp * fop, JSObject * obj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_CALL
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_CALL)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Call(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, const JS::CallArgs& args, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_CONSTRUCT
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_CONSTRUCT)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Construct(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, const JS::CallArgs& args, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
-#ifndef XPC_MAP_WANT_HASINSTANCE
+#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_HASINSTANCE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::HasInstance(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::HandleValue val, bool* bp, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreatePrototype(JSContext* cx, JSObject* proto)
     {return NS_OK;}
 
 /**************************************************************/
 
 #undef XPC_MAP_CLASSNAME
 #undef XPC_MAP_QUOTED_CLASSNAME
-
-#ifdef XPC_MAP_WANT_PRECREATE
-#undef XPC_MAP_WANT_PRECREATE
-#endif
-
-#ifdef XPC_MAP_WANT_GETPROPERTY
-#undef XPC_MAP_WANT_GETPROPERTY
-#endif
-
-#ifdef XPC_MAP_WANT_SETPROPERTY
-#undef XPC_MAP_WANT_SETPROPERTY
-#endif
-
-#ifdef XPC_MAP_WANT_ENUMERATE
-#undef XPC_MAP_WANT_ENUMERATE
-#endif
-
-#ifdef XPC_MAP_WANT_NEWENUMERATE
-#undef XPC_MAP_WANT_NEWENUMERATE
-#endif
-
-#ifdef XPC_MAP_WANT_RESOLVE
-#undef XPC_MAP_WANT_RESOLVE
-#endif
-
-#ifdef XPC_MAP_WANT_FINALIZE
-#undef XPC_MAP_WANT_FINALIZE
-#endif
-
-#ifdef XPC_MAP_WANT_CALL
-#undef XPC_MAP_WANT_CALL
-#endif
-
-#ifdef XPC_MAP_WANT_CONSTRUCT
-#undef XPC_MAP_WANT_CONSTRUCT
-#endif
-
-#ifdef XPC_MAP_WANT_HASINSTANCE
-#undef XPC_MAP_WANT_HASINSTANCE
-#endif
-
 #undef XPC_MAP_FLAGS
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -611,21 +611,20 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_u
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
 NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
 
 // We use the nsIXPScriptable macros to generate lots of stuff for us.
-#define XPC_MAP_CLASSNAME           nsXPCComponents_utils_Sandbox
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_utils_Sandbox"
-#define                             XPC_MAP_WANT_CALL
-#define                             XPC_MAP_WANT_CONSTRUCT
-#define XPC_MAP_FLAGS               0
+#define XPC_MAP_CLASSNAME         nsXPCComponents_utils_Sandbox
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
+                       XPC_SCRIPTABLE_WANT_CONSTRUCT)
 #include "xpc_map_end.h" /* This #undef's the above. */
 
 const xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
 
 bool
 xpc::IsSandboxPrototypeProxy(JSObject* obj)
 {
     return js::IsProxy(obj) &&
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -209,21 +209,21 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_I
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Interfaces)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_Interfaces)
 NS_IMPL_RELEASE(nsXPCComponents_Interfaces)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_Interfaces
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_Interfaces"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_NEWENUMERATE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_Interfaces
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Interfaces"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                          JSContext* cx, JSObject* obj,
                                          JS::AutoIdVector& properties,
                                          bool* _retval)
@@ -433,21 +433,21 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_I
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_InterfacesByID)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_InterfacesByID)
 NS_IMPL_RELEASE(nsXPCComponents_InterfacesByID)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_InterfacesByID
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_InterfacesByID"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_NEWENUMERATE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_InterfacesByID
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_InterfacesByID"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                              JSContext* cx, JSObject* obj,
                                              JS::AutoIdVector& properties,
                                              bool* _retval)
 {
@@ -658,21 +658,21 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_C
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Classes)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_Classes)
 NS_IMPL_RELEASE(nsXPCComponents_Classes)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_Classes
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_Classes"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_NEWENUMERATE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_Classes
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Classes"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_Classes::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                       JSContext* cx, JSObject* obj,
                                       JS::AutoIdVector& properties,
                                       bool* _retval)
 {
@@ -865,21 +865,21 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_C
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_ClassesByID)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_ClassesByID)
 NS_IMPL_RELEASE(nsXPCComponents_ClassesByID)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_ClassesByID
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_ClassesByID"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_NEWENUMERATE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_ClassesByID
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_ClassesByID"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_ClassesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                           JSContext* cx, JSObject* obj,
                                           JS::AutoIdVector& properties,
                                           bool* _retval)
 {
@@ -1097,21 +1097,21 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_R
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Results)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_Results)
 NS_IMPL_RELEASE(nsXPCComponents_Results)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_Results
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_Results"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_NEWENUMERATE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_Results
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Results"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_Results::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                       JSContext* cx, JSObject* obj,
                                       JS::AutoIdVector& properties,
                                       bool* _retval)
 {
@@ -1285,22 +1285,22 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_I
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_ID)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_ID)
 NS_IMPL_RELEASE(nsXPCComponents_ID)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_ID
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_ID"
-#define                             XPC_MAP_WANT_CALL
-#define                             XPC_MAP_WANT_CONSTRUCT
-#define                             XPC_MAP_WANT_HASINSTANCE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_ID
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_ID"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
+                       XPC_SCRIPTABLE_WANT_CONSTRUCT | \
+                       XPC_SCRIPTABLE_WANT_HASINSTANCE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 NS_IMETHODIMP
 nsXPCComponents_ID::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
                          const CallArgs& args, bool* _retval)
 {
     RootedObject obj(cx, objArg);
@@ -1482,22 +1482,22 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_E
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Exception)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_Exception)
 NS_IMPL_RELEASE(nsXPCComponents_Exception)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_Exception
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_Exception"
-#define                             XPC_MAP_WANT_CALL
-#define                             XPC_MAP_WANT_CONSTRUCT
-#define                             XPC_MAP_WANT_HASINSTANCE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_Exception
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Exception"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
+                       XPC_SCRIPTABLE_WANT_CONSTRUCT | \
+                       XPC_SCRIPTABLE_WANT_HASINSTANCE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 NS_IMETHODIMP
 nsXPCComponents_Exception::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
                                 const CallArgs& args, bool* _retval)
 {
     RootedObject obj(cx, objArg);
@@ -1877,21 +1877,20 @@ NS_INTERFACE_MAP_BEGIN(nsXPCConstructor)
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCConstructor)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCConstructor)
 NS_IMPL_RELEASE(nsXPCConstructor)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCConstructor
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCConstructor"
-#define                             XPC_MAP_WANT_CALL
-#define                             XPC_MAP_WANT_CONSTRUCT
-#define XPC_MAP_FLAGS               0
+#define XPC_MAP_CLASSNAME         nsXPCConstructor
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCConstructor"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
+                       XPC_SCRIPTABLE_WANT_CONSTRUCT)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 NS_IMETHODIMP
 nsXPCConstructor::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
                        const CallArgs& args, bool* _retval)
 {
     RootedObject obj(cx, objArg);
@@ -2072,22 +2071,22 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_C
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Constructor)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_Constructor)
 NS_IMPL_RELEASE(nsXPCComponents_Constructor)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_Constructor
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_Constructor"
-#define                             XPC_MAP_WANT_CALL
-#define                             XPC_MAP_WANT_CONSTRUCT
-#define                             XPC_MAP_WANT_HASINSTANCE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_Constructor
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Constructor"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
+                       XPC_SCRIPTABLE_WANT_CONSTRUCT | \
+                       XPC_SCRIPTABLE_WANT_HASINSTANCE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 NS_IMETHODIMP
 nsXPCComponents_Constructor::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
                                   JSObject* objArg, const CallArgs& args, bool* _retval)
 {
     RootedObject obj(cx, objArg);
@@ -2268,19 +2267,19 @@ NS_INTERFACE_MAP_BEGIN(nsXPCComponents_U
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Utils)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsXPCComponents_Utils)
 NS_IMPL_RELEASE(nsXPCComponents_Utils)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsXPCComponents_Utils
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_Utils"
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsXPCComponents_Utils
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Utils"
+#define XPC_MAP_FLAGS XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox** aSandbox)
 {
     NS_ENSURE_ARG_POINTER(aSandbox);
     if (!mSandbox)
         mSandbox = NewSandboxConstructor();
@@ -3542,20 +3541,19 @@ NS_IMPL_ADDREF_INHERITED(nsXPCComponents
 NS_IMPL_RELEASE_INHERITED(nsXPCComponents, nsXPCComponentsBase)
 NS_INTERFACE_MAP_BEGIN(nsXPCComponents)
     NS_INTERFACE_MAP_ENTRY(nsIXPCComponents)
     NS_IMPL_QUERY_CLASSINFO(nsXPCComponents)
 NS_INTERFACE_MAP_END_INHERITING(nsXPCComponentsBase)
 NS_IMPL_CI_INTERFACE_GETTER(nsXPCComponents, nsIXPCComponents)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us
-#define XPC_MAP_CLASSNAME           ComponentsSH
-#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents"
-#define                             XPC_MAP_WANT_PRECREATE
-#define XPC_MAP_FLAGS               0
+#define XPC_MAP_CLASSNAME ComponentsSH
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents"
+#define XPC_MAP_FLAGS XPC_SCRIPTABLE_WANT_PRECREATE
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 ComponentsSH::PreCreate(nsISupports* nativeObj, JSContext* cx, JSObject* globalObj, JSObject** parentObj)
 {
   nsXPCComponentsBase* self = static_cast<nsXPCComponentsBase*>(nativeObj);
   // this should never happen
   if (!self->GetScope()) {
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -195,17 +195,17 @@ nsJSID::NewID(const nsID& id)
 
 
 /***************************************************************************/
 // Class object support so that we can share prototypes of wrapper
 
 // This class exists just so we can have a shared scriptable helper for
 // the nsJSIID class. The instances implement their own helpers. But we
 // needed to be able to indicate to the shared prototypes this single flag:
-// nsIXPCScriptable::DONT_ENUM_STATIC_PROPS. And having a class to do it is
+// XPC_SCRIPTABLE_DONT_ENUM_STATIC_PROPS. And having a class to do it is
 // the only means we have. Setting this flag on any given instance scriptable
 // helper is not sufficient to convey the information that we don't want
 // static properties enumerated on the shared proto.
 
 class SharedScriptableHelperForJSIID final : public nsIXPCScriptable
 {
     ~SharedScriptableHelperForJSIID() {}
 public:
@@ -218,19 +218,19 @@ NS_INTERFACE_MAP_BEGIN(SharedScriptableH
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(SharedScriptableHelperForJSIID)
 NS_IMPL_RELEASE(SharedScriptableHelperForJSIID)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           SharedScriptableHelperForJSIID
-#define XPC_MAP_QUOTED_CLASSNAME   "JSIID"
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME SharedScriptableHelperForJSIID
+#define XPC_MAP_QUOTED_CLASSNAME "JSIID"
+#define XPC_MAP_FLAGS XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE
 #include "xpc_map_end.h" /* This will #undef the above */
 
 static mozilla::StaticRefPtr<nsIXPCScriptable> gSharedScriptableHelperForJSIID;
 static bool gClassObjectsWereInited = false;
 
 static void EnsureClassObjectsInitialized()
 {
     if (!gClassObjectsWereInited) {
@@ -284,22 +284,22 @@ NS_INTERFACE_MAP_BEGIN(nsJSIID)
   NS_IMPL_QUERY_CLASSINFO(nsJSIID)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsJSIID)
 NS_IMPL_RELEASE(nsJSIID)
 NS_IMPL_CI_INTERFACE_GETTER(nsJSIID, nsIJSID, nsIJSIID)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsJSIID
-#define XPC_MAP_QUOTED_CLASSNAME   "nsJSIID"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_ENUMERATE
-#define                             XPC_MAP_WANT_HASINSTANCE
-#define XPC_MAP_FLAGS               nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_CLASSNAME         nsJSIID
+#define XPC_MAP_QUOTED_CLASSNAME "nsJSIID"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_ENUMERATE | \
+                       XPC_SCRIPTABLE_WANT_HASINSTANCE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 nsJSIID::nsJSIID(nsIInterfaceInfo* aInfo)
     : mInfo(aInfo)
 {
 }
 
@@ -542,21 +542,20 @@ NS_INTERFACE_MAP_BEGIN(nsJSCID)
   NS_IMPL_QUERY_CLASSINFO(nsJSCID)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsJSCID)
 NS_IMPL_RELEASE(nsJSCID)
 NS_IMPL_CI_INTERFACE_GETTER(nsJSCID, nsIJSID, nsIJSCID)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           nsJSCID
-#define XPC_MAP_QUOTED_CLASSNAME   "nsJSCID"
-#define                             XPC_MAP_WANT_CONSTRUCT
-#define                             XPC_MAP_WANT_HASINSTANCE
-#define XPC_MAP_FLAGS               0
+#define XPC_MAP_CLASSNAME         nsJSCID
+#define XPC_MAP_QUOTED_CLASSNAME "nsJSCID"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CONSTRUCT | \
+                       XPC_SCRIPTABLE_WANT_HASINSTANCE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 nsJSCID::nsJSCID()  { mDetails = new nsJSID(); }
 nsJSCID::~nsJSCID() {}
 
 NS_IMETHODIMP nsJSCID::GetName(char * *aName)
     {ResolveName(); return mDetails->GetName(aName);}
 
--- a/js/xpconnect/src/XPCRuntimeService.cpp
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -20,29 +20,28 @@ NS_INTERFACE_MAP_BEGIN(BackstagePass)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(BackstagePass)
 NS_IMPL_RELEASE(BackstagePass)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           BackstagePass
-#define XPC_MAP_QUOTED_CLASSNAME   "BackstagePass"
-#define                             XPC_MAP_WANT_RESOLVE
-#define                             XPC_MAP_WANT_ENUMERATE
-#define                             XPC_MAP_WANT_FINALIZE
-#define                             XPC_MAP_WANT_PRECREATE
-
-#define XPC_MAP_FLAGS       nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY   |  \
-                            nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY   |  \
-                            nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY   |  \
-                            nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE    |  \
-                            nsIXPCScriptable::IS_GLOBAL_OBJECT             |  \
-                            nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
+#define XPC_MAP_CLASSNAME         BackstagePass
+#define XPC_MAP_QUOTED_CLASSNAME "BackstagePass"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_WANT_ENUMERATE | \
+                       XPC_SCRIPTABLE_WANT_FINALIZE | \
+                       XPC_SCRIPTABLE_WANT_PRECREATE | \
+                       XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |  \
+                       XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |  \
+                       XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY |  \
+                       XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE |  \
+                       XPC_SCRIPTABLE_IS_GLOBAL_OBJECT |  \
+                       XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 JSObject*
 BackstagePass::GetGlobalJSObject()
 {
     if (mWrapper)
         return mWrapper->GetFlatJSObject();
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1187,21 +1187,21 @@ public:
     NS_DECL_NSIXPCSCRIPTABLE
 
     TestGlobal(){}
 };
 
 NS_IMPL_ISUPPORTS(TestGlobal, nsIXPCTestNoisy, nsIXPCScriptable)
 
 // The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME           TestGlobal
-#define XPC_MAP_QUOTED_CLASSNAME   "TestGlobal"
-#define XPC_MAP_FLAGS               nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY |\
-                                    nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY |\
-                                    nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY
+#define XPC_MAP_CLASSNAME         TestGlobal
+#define XPC_MAP_QUOTED_CLASSNAME "TestGlobal"
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |\
+                       XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |\
+                       XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP TestGlobal::Squawk() {return NS_OK;}
 
 #endif
 
 // uncomment to install the test 'this' translator
 // #define TEST_TranslateThis
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -153,17 +153,17 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
                                 bool initStandardClasses,
                                 JS::CompartmentOptions& aOptions,
                                 XPCWrappedNative** wrappedGlobal)
 {
     AutoJSContext cx;
     nsISupports* identity = nativeHelper.GetCanonical();
 
     // The object should specify that it's meant to be global.
-    MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
+    MOZ_ASSERT(nativeHelper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT);
 
     // We shouldn't be reusing globals.
     MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
                !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
 
     // Get the nsIXPCScriptable. This will tell us the JSClass of the object
     // we're going to create.
     nsCOMPtr<nsIXPCScriptable> scrProto;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -494,17 +494,17 @@ nsXPConnect::InitClassesWithNewWrappedGl
     // we need to have a principal.
     MOZ_ASSERT(aPrincipal);
 
     InitGlobalObjectOptions(aOptions, aPrincipal);
 
     // Call into XPCWrappedNative to make a new global object, scope, and global
     // prototype.
     xpcObjectHelper helper(aCOMObj);
-    MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
+    MOZ_ASSERT(helper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT);
     RefPtr<XPCWrappedNative> wrappedGlobal;
     nsresult rv =
         XPCWrappedNative::WrapNewGlobal(helper, aPrincipal,
                                         aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES,
                                         aOptions, getter_AddRefs(wrappedGlobal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Grab a copy of the global and enter its compartment.
--- a/layout/ipc/PVsync.ipdl
+++ b/layout/ipc/PVsync.ipdl
@@ -16,17 +16,17 @@ namespace layout {
  * interfaces for content to observe/unobserve vsync event notifications.
  */
 async protocol PVsync
 {
   manager PBackground;
 
 child:
   // Send vsync event from chrome to content process.
-  prio(high) async Notify(TimeStamp aVsyncTimestamp) compress;
+  async Notify(TimeStamp aVsyncTimestamp) compress;
 
   // Send the vsync rate to the content process.
   async VsyncRate(float aVsyncRate);
 
 parent:
   // Content process use these messages to acquire the vsync event.
   async Observe();
   async Unobserve();
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -136,16 +136,17 @@ support-files = file_bug645998-1.css fil
 [test_bug732153.html]
 [test_bug732209.html]
 support-files = bug732209-css.sjs
 [test_bug765590.html]
 [test_bug771043.html]
 [test_bug795520.html]
 [test_bug798567.html]
 [test_bug798843_pref.html]
+skip-if = stylo # 1332969
 [test_bug829816.html]
 [test_bug874919.html]
 support-files = file_bug829816.css
 [test_bug887741_at-rules_in_declaration_lists.html]
 [test_bug892929.html]
 [test_bug1055933.html]
 support-files = file_bug1055933_circle-xxl.png
 [test_bug1089417.html]
@@ -180,16 +181,17 @@ skip-if = toolkit == 'android' #bug 5366
 skip-if = stylo # bug 1323715
 [test_css_supports_variables.html]
 skip-if = stylo # bug 1323715
 [test_default_bidi_css.html]
 [test_default_computed_style.html]
 [test_descriptor_storage.html]
 [test_descriptor_syntax_errors.html]
 [test_dont_use_document_colors.html]
+skip-if = stylo # bug 1332969
 [test_dynamic_change_causing_reflow.html]
 [test_exposed_prop_accessors.html]
 [test_extra_inherit_initial.html]
 [test_flexbox_child_display_values.xhtml]
 [test_flexbox_flex_grow_and_shrink.html]
 [test_flexbox_flex_shorthand.html]
 [test_flexbox_layout.html]
 support-files = flexbox_layout_testcases.js
@@ -222,25 +224,27 @@ skip-if = toolkit == 'android'
 [test_load_events_on_stylesheets.html]
 [test_logical_properties.html]
 [test_media_queries.html]
 skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aws only; bug 1030419
 [test_media_queries_dynamic.html]
 [test_media_queries_dynamic_xbl.html]
 [test_media_query_list.html]
 [test_moz_device_pixel_ratio.html]
+skip-if = stylo # bug 1332969
 [test_namespace_rule.html]
 [test_of_type_selectors.xhtml]
 [test_page_parser.html]
 [test_parse_eof.html]
 [test_parse_ident.html]
 [test_parse_rule.html]
 [test_parse_url.html]
 [test_parser_diagnostics_unprintables.html]
 [test_pixel_lengths.html]
+skip-if = stylo # bug 1332969
 [test_pointer-events.html]
 [test_position_float_display.html]
 [test_position_sticky.html]
 [test_priority_preservation.html]
 [test_property_database.html]
 [test_property_syntax_errors.html]
 [test_pseudoelement_state.html]
 [test_pseudoelement_parsing.html]
@@ -292,18 +296,20 @@ support-files = file_transitions_with_di
 [test_unicode_range_loading.html]
 skip-if = stylo # timeout bug 1328507
 support-files = ../../reftests/fonts/markA.woff ../../reftests/fonts/markB.woff ../../reftests/fonts/markC.woff ../../reftests/fonts/markD.woff
 [test_units_angle.html]
 [test_units_frequency.html]
 [test_units_length.html]
 [test_units_time.html]
 [test_unprefixing_service.html]
+skip-if = stylo # bug 1332969
 support-files = unprefixing_service_iframe.html unprefixing_service_utils.js
 [test_unprefixing_service_prefs.html]
+skip-if = stylo # bug 1332969
 support-files = unprefixing_service_iframe.html unprefixing_service_utils.js
 [test_value_cloning.html]
 skip-if = toolkit == 'android' # bug 775227 for android
 [test_value_computation.html]
 skip-if = toolkit == 'android'
 [test_value_storage.html]
 [test_variable_serialization_computed.html]
 [test_variable_serialization_specified.html]
@@ -317,9 +323,10 @@ skip-if = (toolkit == 'android' || stylo
 skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
 [test_visited_lying.html]
 skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
 [test_visited_pref.html]
 skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
 [test_visited_reftests.html]
 skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
 [test_webkit_device_pixel_ratio.html]
+skip-if = stylo # bug 1332969
 [test_webkit_flex_display.html]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5553,9 +5553,9 @@ pref("dom.storageManager.enabled", false
 // a single web page in a row, all following authentication dialogs will
 // be blocked (automatically canceled) for that page. The counter resets
 // when the page is reloaded. To turn this feature off, just set the limit to 0.
 pref("prompts.authentication_dialog_abuse_limit", 3);
 
 // Enable the Storage management in about:preferences and persistent-storage permission request
 // To enable the DOM implementation, turn on "dom.storageManager.enabled"
 pref("browser.storageManager.enabled", false);
-pref("dom.IntersectionObserver.enabled", true);
+pref("dom.IntersectionObserver.enabled", false);
--- a/storage/mozStorageAsyncStatementJSHelper.cpp
+++ b/storage/mozStorageAsyncStatementJSHelper.cpp
@@ -77,20 +77,20 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
 NS_INTERFACE_MAP_BEGIN(AsyncStatementJSHelper)
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIXPCScriptable
 
-#define XPC_MAP_CLASSNAME AsyncStatementJSHelper
+#define XPC_MAP_CLASSNAME         AsyncStatementJSHelper
 #define XPC_MAP_QUOTED_CLASSNAME "AsyncStatementJSHelper"
-#define XPC_MAP_WANT_GETPROPERTY
-#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_GETPROPERTY | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h"
 
 NS_IMETHODIMP
 AsyncStatementJSHelper::GetProperty(nsIXPConnectWrappedNative *aWrapper,
                                     JSContext *aCtx,
                                     JSObject *aScopeObj,
                                     jsid aId,
                                     JS::Value *_result,
--- a/storage/mozStorageAsyncStatementParams.cpp
+++ b/storage/mozStorageAsyncStatementParams.cpp
@@ -34,21 +34,21 @@ NS_IMPL_ISUPPORTS(
   AsyncStatementParams
 , mozIStorageStatementParams
 , nsIXPCScriptable
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIXPCScriptable
 
-#define XPC_MAP_CLASSNAME AsyncStatementParams
+#define XPC_MAP_CLASSNAME         AsyncStatementParams
 #define XPC_MAP_QUOTED_CLASSNAME "AsyncStatementParams"
-#define XPC_MAP_WANT_SETPROPERTY
-#define XPC_MAP_WANT_RESOLVE
-#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_SETPROPERTY | \
+                       XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h"
 
 NS_IMETHODIMP
 AsyncStatementParams::SetProperty(
   nsIXPConnectWrappedNative *aWrapper,
   JSContext *aCtx,
   JSObject *aScopeObj,
   jsid aId,
--- a/storage/mozStorageStatementJSHelper.cpp
+++ b/storage/mozStorageStatementJSHelper.cpp
@@ -175,21 +175,21 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
 NS_INTERFACE_MAP_BEGIN(StatementJSHelper)
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIXPCScriptable
 
-#define XPC_MAP_CLASSNAME StatementJSHelper
+#define XPC_MAP_CLASSNAME         StatementJSHelper
 #define XPC_MAP_QUOTED_CLASSNAME "StatementJSHelper"
-#define XPC_MAP_WANT_GETPROPERTY
-#define XPC_MAP_WANT_RESOLVE
-#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_GETPROPERTY | \
+                       XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h"
 
 NS_IMETHODIMP
 StatementJSHelper::GetProperty(nsIXPConnectWrappedNative *aWrapper,
                                JSContext *aCtx,
                                JSObject *aScopeObj,
                                jsid aId,
                                JS::Value *_result,
--- a/storage/mozStorageStatementParams.cpp
+++ b/storage/mozStorageStatementParams.cpp
@@ -32,22 +32,22 @@ NS_IMPL_ISUPPORTS(
   StatementParams,
   mozIStorageStatementParams,
   nsIXPCScriptable
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIXPCScriptable
 
-#define XPC_MAP_CLASSNAME StatementParams
+#define XPC_MAP_CLASSNAME         StatementParams
 #define XPC_MAP_QUOTED_CLASSNAME "StatementParams"
-#define XPC_MAP_WANT_SETPROPERTY
-#define XPC_MAP_WANT_NEWENUMERATE
-#define XPC_MAP_WANT_RESOLVE
-#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_SETPROPERTY | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
+                       XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h"
 
 NS_IMETHODIMP
 StatementParams::SetProperty(nsIXPConnectWrappedNative *aWrapper,
                              JSContext *aCtx,
                              JSObject *aScopeObj,
                              jsid aId,
                              JS::Value *_vp,
--- a/storage/mozStorageStatementRow.cpp
+++ b/storage/mozStorageStatementRow.cpp
@@ -29,21 +29,21 @@ NS_IMPL_ISUPPORTS(
   StatementRow,
   mozIStorageStatementRow,
   nsIXPCScriptable
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIXPCScriptable
 
-#define XPC_MAP_CLASSNAME StatementRow
+#define XPC_MAP_CLASSNAME         StatementRow
 #define XPC_MAP_QUOTED_CLASSNAME "StatementRow"
-#define XPC_MAP_WANT_GETPROPERTY
-#define XPC_MAP_WANT_RESOLVE
-#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_GETPROPERTY | \
+                       XPC_SCRIPTABLE_WANT_RESOLVE | \
+                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h"
 
 NS_IMETHODIMP
 StatementRow::GetProperty(nsIXPConnectWrappedNative *aWrapper,
                           JSContext *aCtx,
                           JSObject *aScopeObj,
                           jsid aId,
                           JS::Value *_vp,
--- a/toolkit/components/ctypes/ctypes.cpp
+++ b/toolkit/components/ctypes/ctypes.cpp
@@ -52,18 +52,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Module)
 NS_IMPL_ISUPPORTS(Module, nsIXPCScriptable)
 
 Module::Module() = default;
 
 Module::~Module() = default;
 
 #define XPC_MAP_CLASSNAME Module
 #define XPC_MAP_QUOTED_CLASSNAME "Module"
-#define XPC_MAP_WANT_CALL
-#define XPC_MAP_FLAGS 0
+#define XPC_MAP_FLAGS XPC_SCRIPTABLE_WANT_CALL
 #include "xpc_map_end.h"
 
 static bool
 SealObjectAndPrototype(JSContext* cx, JS::Handle<JSObject *> parent, const char* name)
 {
   JS::Rooted<JS::Value> prop(cx);
   if (!JS_GetProperty(cx, parent, name, &prop))
     return false;
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_permission.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_permission.html
@@ -10,27 +10,28 @@
 </head>
 <body>
 
 <script type="text/javascript">
 "use strict";
 
 add_task(function* test_contentscript() {
   function background() {
-    browser.test.onMessage.addListener(url => {
-      browser.tabs.create({url}).then(tab => {
-        return browser.tabs.executeScript(tab.id, {code: "true;"})
-                      .then(() => {
-                        browser.test.sendMessage("executed", true);
-                        browser.tabs.remove([tab.id]);
-                      }, err => {
-                        browser.test.sendMessage("executed", false);
-                        browser.tabs.remove([tab.id]);
-                      });
-      });
+    browser.test.onMessage.addListener(async url => {
+      let tab = await browser.tabs.create({url});
+
+      let executed = true;
+      try {
+        await browser.tabs.executeScript(tab.id, {code: "true;"});
+      } catch (e) {
+        executed = false;
+      }
+
+      await browser.tabs.remove([tab.id]);
+      browser.test.sendMessage("executed", executed);
     });
   }
 
   let extensionData = {
     manifest: {
       permissions: ["<all_urls>"],
     },
     background,
--- a/toolkit/components/perf/PerfMeasurement.cpp
+++ b/toolkit/components/perf/PerfMeasurement.cpp
@@ -27,18 +27,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Module)
 NS_IMPL_ISUPPORTS(Module, nsIXPCScriptable)
 
 Module::Module() = default;
 
 Module::~Module() = default;
 
 #define XPC_MAP_CLASSNAME Module
 #define XPC_MAP_QUOTED_CLASSNAME "Module"
-#define XPC_MAP_WANT_CALL
-#define XPC_MAP_FLAGS 0
+#define XPC_MAP_FLAGS XPC_SCRIPTABLE_WANT_CALL
 #include "xpc_map_end.h"
 
 static bool
 SealObjectAndPrototype(JSContext* cx, JS::Handle<JSObject *> parent, const char* name)
 {
   JS::Rooted<JS::Value> prop(cx);
   if (!JS_GetProperty(cx, parent, name, &prop))
     return false;
--- a/toolkit/components/reflect/reflect.cpp
+++ b/toolkit/components/reflect/reflect.cpp
@@ -25,18 +25,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Module)
 NS_IMPL_ISUPPORTS(Module, nsIXPCScriptable)
 
 Module::Module() = default;
 
 Module::~Module() = default;
 
 #define XPC_MAP_CLASSNAME Module
 #define XPC_MAP_QUOTED_CLASSNAME "Module"
-#define XPC_MAP_WANT_CALL
-#define XPC_MAP_FLAGS 0
+#define XPC_MAP_FLAGS XPC_SCRIPTABLE_WANT_CALL
 #include "xpc_map_end.h"
 
 NS_IMETHODIMP
 Module::Call(nsIXPConnectWrappedNative* wrapper,
              JSContext* cx,
              JSObject* obj,
              const JS::CallArgs& args,
              bool* _retval)
--- a/toolkit/modules/addons/WebRequestContent.js
+++ b/toolkit/modules/addons/WebRequestContent.js
@@ -75,16 +75,26 @@ var ContentPolicy = {
 
   unregister() {
     let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
     catMan.deleteCategoryEntry("content-policy", this._contractID, false);
   },
 
   shouldLoad(policyType, contentLocation, requestOrigin,
              node, mimeTypeGuess, extra, requestPrincipal) {
+
+    // Loads of TYPE_DOCUMENT and TYPE_SUBDOCUMENT perform a ConPol check
+    // within docshell as well as within the ContentSecurityManager. To avoid
+    // duplicate evaluations we ignore ConPol checks performed within docShell.
+    if (extra instanceof Ci.nsISupportsString) {
+      if (extra.data === "conPolCheckFromDocShell") {
+        return Ci.nsIContentPolicy.ACCEPT;
+      }
+    }
+
     if (requestPrincipal &&
         Services.scriptSecurityManager.isSystemPrincipal(requestPrincipal)) {
       return Ci.nsIContentPolicy.ACCEPT;
     }
     let url = contentLocation.spec;
     if (IS_HTTP.test(url)) {
       // We'll handle this in our parent process HTTP observer.
       return Ci.nsIContentPolicy.ACCEPT;
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -458,17 +458,23 @@ bool is_main_thread_name(const char* aNa
 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo,bar)
 #elif defined(HAVE_VA_LIST_AS_ARRAY)
 #define VARARGS_ASSIGN(foo, bar)     foo[0] = bar[0]
 #else
 #define VARARGS_ASSIGN(foo, bar)     (foo) = (bar)
 #endif
 
 void
-mozilla_sampler_log(const char *fmt, va_list args)
+profiler_log(const char* str)
+{
+  profiler_tracing("log", str, TRACING_EVENT);
+}
+
+void
+profiler_log(const char* fmt, va_list args)
 {
   if (profiler_is_active()) {
     // nsAutoCString AppendPrintf would be nicer but
     // this is mozilla external code
     char buf[2048];
     va_list argsCpy;
     VARARGS_ASSIGN(argsCpy, args);
     int required = VsprintfLiteral(buf, fmt, argsCpy);
@@ -491,28 +497,29 @@ mozilla_sampler_log(const char *fmt, va_
       delete[] heapBuf;
     }
   }
 }
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN externally visible functions
 
-void mozilla_sampler_init(void* stackTop)
+void
+profiler_init(void* stackTop)
 {
   sInitCount++;
 
   if (stack_key_initialized)
     return;
 
 #ifdef MOZ_TASK_TRACER
   mozilla::tasktracer::InitTaskTracer();
 #endif
 
-  LOG("BEGIN mozilla_sampler_init");
+  LOG("BEGIN profiler_init");
   if (!tlsPseudoStack.init() || !tlsTicker.init()) {
     LOG("Failed to init.");
     return;
   }
   bool ignore;
   sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
 
   stack_key_initialized = true;
@@ -529,17 +536,17 @@ void mozilla_sampler_init(void* stackTop
 
   // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan
   // threshhold from MOZ_PROFILER_STACK_SCAN.
   read_profiler_env_vars();
 
   // platform specific initialization
   OS::Startup();
 
-  set_stderr_callback(mozilla_sampler_log);
+  set_stderr_callback(profiler_log);
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   if (mozilla::jni::IsFennec()) {
     GeckoJavaSampler::Init();
   }
 #endif
 
   // We can't open pref so we use an environment variable
@@ -563,20 +570,21 @@ void mozilla_sampler_init(void* stackTop
 #endif
                          };
 
   const char* threadFilters[] = { "GeckoMain", "Compositor" };
 
   profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
                          features, MOZ_ARRAY_LENGTH(features),
                          threadFilters, MOZ_ARRAY_LENGTH(threadFilters));
-  LOG("END   mozilla_sampler_init");
+  LOG("END   profiler_init");
 }
 
-void mozilla_sampler_shutdown()
+void
+profiler_shutdown()
 {
   sInitCount--;
 
   if (sInitCount > 0)
     return;
 
   // Save the profile on shutdown if requested.
   GeckoSampler *t = tlsTicker.get();
@@ -602,65 +610,69 @@ void mozilla_sampler_shutdown()
   stack->deref();
   tlsPseudoStack.set(nullptr);
 
 #ifdef MOZ_TASK_TRACER
   mozilla::tasktracer::ShutdownTaskTracer();
 #endif
 }
 
-mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime)
+mozilla::UniquePtr<char[]>
+profiler_get_profile(double aSinceTime)
 {
   GeckoSampler *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSON(aSinceTime);
 }
 
-JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime)
+JSObject*
+profiler_get_profile_jsobject(JSContext *aCx, double aSinceTime)
 {
   GeckoSampler *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSObject(aCx, aSinceTime);
 }
 
-void mozilla_sampler_get_profile_data_async(double aSinceTime,
-                                            mozilla::dom::Promise* aPromise)
+void
+profiler_get_profile_jsobject_async(double aSinceTime,
+                                    mozilla::dom::Promise* aPromise)
 {
   GeckoSampler *t = tlsTicker.get();
   if (NS_WARN_IF(!t)) {
     return;
   }
 
   t->ToJSObjectAsync(aSinceTime, aPromise);
 }
 
-void mozilla_sampler_save_profile_to_file_async(double aSinceTime,
-                                                const char* aFileName)
+void
+profiler_save_profile_to_file_async(double aSinceTime, const char* aFileName)
 {
   nsCString filename(aFileName);
   NS_DispatchToMainThread(NS_NewRunnableFunction([=] () {
     GeckoSampler *t = tlsTicker.get();
     if (NS_WARN_IF(!t)) {
       return;
     }
 
     t->ToFileAsync(filename, aSinceTime);
   }));
 }
 
-void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
-                                               double* aInterval,
-                                               mozilla::Vector<const char*>* aFilters,
-                                               mozilla::Vector<const char*>* aFeatures)
+void
+profiler_get_start_params(int* aEntrySize,
+                          double* aInterval,
+                          mozilla::Vector<const char*>* aFilters,
+                          mozilla::Vector<const char*>* aFeatures)
 {
   if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) ||
       NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) {
     return;
   }
 
   GeckoSampler *t = tlsTicker.get();
   if (NS_WARN_IF(!t)) {
@@ -678,17 +690,18 @@ void mozilla_sampler_get_profiler_start_
 
   const FeatureList& featureList = t->Features();
   MOZ_ALWAYS_TRUE(aFeatures->resize(featureList.length()));
   for (size_t i = 0; i < featureList.length(); ++i) {
     (*aFeatures)[i] = featureList[i].c_str();
   }
 }
 
-void mozilla_sampler_get_gatherer(nsISupports** aRetVal)
+void
+profiler_get_gatherer(nsISupports** aRetVal)
 {
   if (!aRetVal) {
     return;
   }
 
   if (NS_WARN_IF(!profiler_is_active())) {
     *aRetVal = nullptr;
     return;
@@ -698,17 +711,18 @@ void mozilla_sampler_get_gatherer(nsISup
   if (NS_WARN_IF(!t)) {
     *aRetVal = nullptr;
     return;
   }
 
   t->GetGatherer(aRetVal);
 }
 
-void mozilla_sampler_save_profile_to_file(const char* aFilename)
+void
+profiler_save_profile_to_file(const char* aFilename)
 {
   GeckoSampler *t = tlsTicker.get();
   if (!t) {
     return;
   }
 
   std::ofstream stream;
   stream.open(aFilename);
@@ -716,18 +730,18 @@ void mozilla_sampler_save_profile_to_fil
     t->ToStreamAsJSON(stream);
     stream.close();
     LOGF("Saved to %s", aFilename);
   } else {
     LOG("Fail to open profile log file.");
   }
 }
 
-
-const char** mozilla_sampler_get_features()
+const char**
+profiler_get_features()
 {
   static const char* features[] = {
 #if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
     // Walk the C++ stack.
     "stackwalk",
 #endif
 #if defined(ENABLE_SPS_LEAF_DATA)
     // Include the C++ leaf node if not stackwalking. DevTools
@@ -763,40 +777,42 @@ const char** mozilla_sampler_get_feature
     "tasktracer",
 #endif
     nullptr
   };
 
   return features;
 }
 
-void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
-                                     uint32_t *aGeneration)
+void
+profiler_get_buffer_info_helper(uint32_t *aCurrentPosition,
+                                uint32_t *aTotalSize,
+                                uint32_t *aGeneration)
 {
-  *aCurrentPosition = 0;
-  *aTotalSize = 0;
-  *aGeneration = 0;
+  // This function is called by profiler_get_buffer_info(), which has already
+  // zeroed the outparams.
 
   if (!stack_key_initialized)
     return;
 
   GeckoSampler *t = tlsTicker.get();
   if (!t)
     return;
 
   t->GetBufferInfo(aCurrentPosition, aTotalSize, aGeneration);
 }
 
 // Values are only honored on the first start
-void mozilla_sampler_start(int aProfileEntries, double aInterval,
-                           const char** aFeatures, uint32_t aFeatureCount,
-                           const char** aThreadNameFilters, uint32_t aFilterCount)
+void
+profiler_start(int aProfileEntries, double aInterval,
+               const char** aFeatures, uint32_t aFeatureCount,
+               const char** aThreadNameFilters, uint32_t aFilterCount)
 
 {
-  LOG("BEGIN mozilla_sampler_start");
+  LOG("BEGIN profiler_start");
 
   if (!stack_key_initialized)
     profiler_init(nullptr);
 
   /* If the sampling interval was set using env vars, use that
      in preference to anything else. */
   if (sUnwindInterval > 0)
     aInterval = sUnwindInterval;
@@ -882,29 +898,30 @@ void mozilla_sampler_start(int aProfileE
       nsCOMPtr<nsIProfilerStartParams> params =
         new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray,
                                   threadNameFiltersArray);
 
       os->NotifyObservers(params, "profiler-started", nullptr);
     }
   }
 
-  LOG("END   mozilla_sampler_start");
+  LOG("END   profiler_start");
 }
 
-void mozilla_sampler_stop()
+void
+profiler_stop()
 {
-  LOG("BEGIN mozilla_sampler_stop");
+  LOG("BEGIN profiler_stop");
 
   if (!stack_key_initialized)
     return;
 
   GeckoSampler *t = tlsTicker.get();
   if (!t) {
-    LOG("END   mozilla_sampler_stop-early");
+    LOG("END   profiler_stop-early");
     return;
   }
 
   bool disableJS = t->ProfileJS();
 
   t->Stop();
   delete t;
   tlsTicker.set(nullptr);
@@ -926,50 +943,57 @@ void mozilla_sampler_stop()
   sIsRestyleProfiling = false;
 
   if (Sampler::CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os)
       os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
   }
 
-  LOG("END   mozilla_sampler_stop");
+  LOG("END   profiler_stop");
 }
 
-bool mozilla_sampler_is_paused() {
+bool
+profiler_is_paused()
+{
   if (Sampler::GetActiveSampler()) {
     return Sampler::GetActiveSampler()->IsPaused();
   } else {
     return false;
   }
 }
 
-void mozilla_sampler_pause() {
+void
+profiler_pause()
+{
   if (Sampler::GetActiveSampler()) {
     Sampler::GetActiveSampler()->SetPaused(true);
     if (Sampler::CanNotifyObservers()) {
       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
       if (os)
         os->NotifyObservers(nullptr, "profiler-paused", nullptr);
     }
   }
 }
 
-void mozilla_sampler_resume() {
+void
+profiler_resume()
+{
   if (Sampler::GetActiveSampler()) {
     Sampler::GetActiveSampler()->SetPaused(false);
     if (Sampler::CanNotifyObservers()) {
       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
       if (os)
         os->NotifyObservers(nullptr, "profiler-resumed", nullptr);
     }
   }
 }
 
-bool mozilla_sampler_feature_active(const char* aName)
+bool
+profiler_feature_active(const char* aName)
 {
   if (!profiler_is_active()) {
     return false;
   }
 
   if (strcmp(aName, "gpu") == 0) {
     return sIsGPUProfiling;
   }
@@ -984,70 +1008,77 @@ bool mozilla_sampler_feature_active(cons
 
   if (strcmp(aName, "restyle") == 0) {
     return sIsRestyleProfiling;
   }
 
   return false;
 }
 
-bool mozilla_sampler_is_active()
+bool
+profiler_is_active()
 {
   return sIsProfiling;
 }
 
-void mozilla_sampler_responsiveness(const mozilla::TimeStamp& aTime)
+void
+profiler_responsiveness(const mozilla::TimeStamp& aTime)
 {
   sLastTracerEvent = aTime;
 }
 
-void mozilla_sampler_frame_number(int frameNumber)
+void
+profiler_set_frame_number(int frameNumber)
 {
   sFrameNumber = frameNumber;
 }
 
-void mozilla_sampler_lock()
+void
+profiler_lock()
 {
   profiler_stop();
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-locked", nullptr);
 }
 
-void mozilla_sampler_unlock()
+void
+profiler_unlock()
 {
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
 }
 
-bool mozilla_sampler_register_thread(const char* aName, void* aGuessStackTop)
+void
+profiler_register_thread(const char* aName, void* aGuessStackTop)
 {
   if (sInitCount == 0) {
-    return false;
+    return;
   }
 
 #if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING)
   // The only way to profile secondary threads on b2g
   // is to build with profiling OR have the profiler
   // running on startup.
   if (!profiler_is_active()) {
-    return false;
+    return;
   }
 #endif
 
   MOZ_ASSERT(tlsPseudoStack.get() == nullptr);
   PseudoStack* stack = PseudoStack::create();
   tlsPseudoStack.set(stack);
   bool isMainThread = is_main_thread_name(aName);
   void* stackTop = GetStackTop(aGuessStackTop);
-  return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
+  Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
 }
 
-void mozilla_sampler_unregister_thread()
+void
+profiler_unregister_thread()
 {
   // Don't check sInitCount count here -- we may be unregistering the
   // thread after the sampler was shut down.
   if (!stack_key_initialized) {
     return;
   }
 
   PseudoStack *stack = tlsPseudoStack.get();
@@ -1055,63 +1086,93 @@ void mozilla_sampler_unregister_thread()
     return;
   }
   stack->deref();
   tlsPseudoStack.set(nullptr);
 
   Sampler::UnregisterCurrentThread();
 }
 
-void mozilla_sampler_sleep_start() {
+void
+profiler_sleep_start()
+{
   if (sInitCount == 0) {
     return;
   }
 
   PseudoStack *stack = tlsPseudoStack.get();
   if (stack == nullptr) {
     return;
   }
   stack->setSleeping(1);
 }
 
-void mozilla_sampler_sleep_end() {
+void
+profiler_sleep_end()
+{
   if (sInitCount == 0) {
     return;
   }
 
   PseudoStack *stack = tlsPseudoStack.get();
   if (stack == nullptr) {
     return;
   }
   stack->setSleeping(0);
 }
 
-bool mozilla_sampler_is_sleeping() {
+bool
+profiler_is_sleeping()
+{
   if (sInitCount == 0) {
     return false;
   }
   PseudoStack *stack = tlsPseudoStack.get();
   if (stack == nullptr) {
     return false;
   }
   return stack->isSleeping();
 }
 
-double mozilla_sampler_time(const mozilla::TimeStamp& aTime)
+void
+profiler_js_operation_callback()
+{
+  PseudoStack *stack = tlsPseudoStack.get();
+  if (!stack) {
+    return;
+  }
+
+  stack->jsOperationCallback();
+}
+
+double
+profiler_time(const mozilla::TimeStamp& aTime)
 {
   mozilla::TimeDuration delta = aTime - sStartTime;
   return delta.ToMilliseconds();
 }
 
-double mozilla_sampler_time()
+double
+profiler_time()
 {
-  return mozilla_sampler_time(mozilla::TimeStamp::Now());
+  return profiler_time(mozilla::TimeStamp::Now());
 }
 
-UniqueProfilerBacktrace mozilla_sampler_get_backtrace()
+bool
+profiler_in_privacy_mode()
+{
+  PseudoStack *stack = tlsPseudoStack.get();
+  if (!stack) {
+    return false;
+  }
+  return stack->mPrivacyMode;
+}
+
+UniqueProfilerBacktrace
+profiler_get_backtrace()
 {
   if (!stack_key_initialized)
     return nullptr;
 
   // Don't capture a stack if we're not profiling
   if (!profiler_is_active()) {
     return nullptr;
   }
@@ -1133,17 +1194,18 @@ void
 ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace)
 {
   delete aBacktrace;
 }
 
 // Fill the output buffer with the following pattern:
 // "Lable 1" "\0" "Label 2" "\0" ... "Label N" "\0" "\0"
 // TODO: use the unwinder instead of pseudo stack.
-void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize)
+void
+profiler_get_backtrace_noalloc(char *output, size_t outputSize)
 {
   MOZ_ASSERT(outputSize >= 2);
   char *bound = output + outputSize - 2;
   output[0] = output[1] = '\0';
   PseudoStack *pseudoStack = tlsPseudoStack.get();
   if (!pseudoStack) {
     return;
   }
@@ -1157,31 +1219,45 @@ void mozilla_sampler_get_backtrace_noall
       break;
     strcpy(output, pseudoFrames[i].label());
     output += len;
     *output++ = '\0';
     *output = '\0';
   }
 }
 
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
-                             TracingMetadata aMetaData)
+void
+profiler_tracing(const char* aCategory, const char* aInfo,
+                 TracingMetadata aMetaData)
 {
-  mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData));
+  // Don't insert a marker if we're not profiling, to avoid the heap copy
+  // (malloc).
+  if (!stack_key_initialized || !profiler_is_active()) {
+    return;
+  }
+
+  profiler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData));
 }
 
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
-                             UniqueProfilerBacktrace aCause,
-                             TracingMetadata aMetaData)
+void
+profiler_tracing(const char* aCategory, const char* aInfo,
+                 UniqueProfilerBacktrace aCause, TracingMetadata aMetaData)
 {
-  mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData,
-                                                              mozilla::Move(aCause)));
+  // Don't insert a marker if we're not profiling, to avoid the heap copy
+  // (malloc).
+  if (!stack_key_initialized || !profiler_is_active()) {
+    return;
+  }
+
+  profiler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData,
+                                                       mozilla::Move(aCause)));
 }
 
-void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
+void
+profiler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
 {
   // Note that aPayload may be allocated by the caller, so we need to make sure
   // that we free it at some point.
   mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
 
   if (!stack_key_initialized)
     return;
 
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -85,16 +85,23 @@ struct ProfilerBacktraceDestructor
 {
   void operator()(ProfilerBacktrace*);
 };
 using UniqueProfilerBacktrace =
   mozilla::UniquePtr<ProfilerBacktrace, ProfilerBacktraceDestructor>;
 
 #if !defined(MOZ_ENABLE_PROFILER_SPS)
 
+// Use these for functions below that must be visible whether the profiler is
+// enabled or not. When the profiler is disabled they are static inline
+// functions (with a simple return value if they are non-void) that should be
+// optimized away during compilation.
+#define PROFILER_FUNC(decl, rv)  static inline decl { return rv; }
+#define PROFILER_FUNC_VOID(decl) static inline void decl {}
+
 // Insert a RAII in this scope to active a pseudo label. Any samples collected
 // in this scope will contain this annotation. For dynamic strings use
 // PROFILER_LABEL_PRINTF. Arguments must be string literals.
 #define PROFILER_LABEL(name_space, info, category) do {} while (0)
 
 // Similar to PROFILER_LABEL, PROFILER_LABEL_FUNC will push/pop the enclosing
 // functon name as the pseudostack label.
 #define PROFILER_LABEL_FUNC(category) do {} while (0)
@@ -107,151 +114,187 @@ using UniqueProfilerBacktrace =
 
 // Insert a marker in the profile timeline. This is useful to delimit something
 // important happening such as the first paint. Unlike profiler_label that are
 // only recorded if a sample is collected while it is active, marker will always
 // be collected.
 #define PROFILER_MARKER(info) do {} while (0)
 #define PROFILER_MARKER_PAYLOAD(info, payload) do { mozilla::UniquePtr<ProfilerMarkerPayload> payloadDeletor(payload); } while (0)
 
-static inline void profiler_tracing(const char* aCategory, const char* aInfo,
-                                    TracingMetadata metaData = TRACING_DEFAULT) {}
-static inline void profiler_tracing(const char* aCategory, const char* aInfo,
+#else   // defined(MOZ_ENABLE_PROFILER_SPS)
+
+#define PROFILER_FUNC(decl, rv)  decl;
+#define PROFILER_FUNC_VOID(decl) void decl;
+
+// we want the class and function name but can't easily get that using preprocessor macros
+// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
+
+#define PROFILER_LABEL(name_space, info, category) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__)
+
+#define PROFILER_LABEL_FUNC(category) MOZ_PLATFORM_TRACING(SAMPLE_FUNCTION_NAME) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(SAMPLE_FUNCTION_NAME, category, __LINE__)
+
+#define PROFILER_LABEL_PRINTF(name_space, info, category, ...) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__)
+
+#define PROFILER_MARKER(info) profiler_add_marker(info)
+#define PROFILER_MARKER_PAYLOAD(info, payload) profiler_add_marker(info, payload)
+
+#endif  // defined(MOZ_ENABLE_PROFILER_SPS)
+
+// These functions are defined whether the profiler is enabled or not.
+
+PROFILER_FUNC_VOID(profiler_tracing(const char* aCategory, const char* aInfo,
+                                    TracingMetadata metaData = TRACING_DEFAULT))
+PROFILER_FUNC_VOID(profiler_tracing(const char* aCategory, const char* aInfo,
                                     UniqueProfilerBacktrace aCause,
-                                    TracingMetadata metaData = TRACING_DEFAULT) {}
+                                    TracingMetadata metaData = TRACING_DEFAULT))
 
 // Initilize the profiler TLS, signal handlers on linux. If MOZ_PROFILER_STARTUP
 // is set the profiler will be started. This call must happen before any other
 // sampler calls. Particularly sampler_label/sampler_marker.
-static inline void profiler_init(void* stackTop) {};
+PROFILER_FUNC_VOID(profiler_init(void* stackTop))
 
 // Clean up the profiler module, stopping it if required. This function may
 // also save a shutdown profile if requested. No profiler calls should happen
 // after this point and all pseudo labels should have been popped.
-static inline void profiler_shutdown() {};
+PROFILER_FUNC_VOID(profiler_shutdown())
 
 // Start the profiler with the selected options. The samples will be
 // recorded in a circular buffer.
 //   "aProfileEntries" is an abstract size indication of how big
 //       the profile's circular buffer should be. Multiply by 4
 //       words to get the cost.
 //   "aInterval" the sampling interval. The profiler will do its
 //       best to sample at this interval. The profiler visualization
 //       should represent the actual sampling accuracy.
-static inline void profiler_start(int aProfileEntries, double aInterval,
+PROFILER_FUNC_VOID(profiler_start(int aProfileEntries, double aInterval,
                               const char** aFeatures, uint32_t aFeatureCount,
-                              const char** aThreadNameFilters, uint32_t aFilterCount) {}
+                              const char** aThreadNameFilters, uint32_t aFilterCount))
 
 // Stop the profiler and discard the profile. Call 'profiler_save' before this
 // to retrieve the profile.
-static inline void profiler_stop() {}
+PROFILER_FUNC_VOID(profiler_stop())
 
 // These functions pause and resume the profiler. While paused the profile will not
 // take any samples and will not record any data into its buffers. The profiler
 // remains fully initialized in this state. Timeline markers will still be stored.
 // This feature will keep javascript profiling enabled, thus allowing toggling the
 // profiler without invalidating the JIT.
-static inline bool profiler_is_paused() { return false; }
-static inline void profiler_pause() {}
-static inline void profiler_resume() {}
-
+PROFILER_FUNC(bool profiler_is_paused(), false)
+PROFILER_FUNC_VOID(profiler_pause())
+PROFILER_FUNC_VOID(profiler_resume())
 
 // Immediately capture the current thread's call stack and return it
-static inline UniqueProfilerBacktrace profiler_get_backtrace() { return nullptr; }
-static inline void profiler_get_backtrace_noalloc(char *output, size_t outputSize) { return; }
+PROFILER_FUNC(UniqueProfilerBacktrace profiler_get_backtrace(), nullptr)
+PROFILER_FUNC_VOID(profiler_get_backtrace_noalloc(char *output,
+                                                  size_t outputSize))
 
 // Free a ProfilerBacktrace returned by profiler_get_backtrace()
+#if !defined(MOZ_ENABLE_PROFILER_SPS)
 inline void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {}
+#endif
 
-static inline bool profiler_is_active() { return false; }
+PROFILER_FUNC(bool profiler_is_active(), false)
 
 // Check if an external profiler feature is active.
 // Supported:
 //  * gpu
-static inline bool profiler_feature_active(const char*) { return false; }
+PROFILER_FUNC(bool profiler_feature_active(const char*), false)
 
 // Internal-only. Used by the event tracer.
-static inline void profiler_responsiveness(const mozilla::TimeStamp& aTime) {}
+PROFILER_FUNC_VOID(profiler_responsiveness(const mozilla::TimeStamp& aTime))
 
 // Internal-only.
-static inline void profiler_set_frame_number(int frameNumber) {}
+PROFILER_FUNC_VOID(profiler_set_frame_number(int frameNumber))
 
 // Get the profile encoded as a JSON string.
-static inline mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0) {
-  return nullptr;
-}
+PROFILER_FUNC(mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0),
+              nullptr)
 
 // Get the profile encoded as a JSON object.
-static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx,
-                                                      double aSinceTime = 0) {
-  return nullptr;
-}
+PROFILER_FUNC(JSObject* profiler_get_profile_jsobject(JSContext* aCx,
+                                                       double aSinceTime = 0),
+	       nullptr)
 
 // Get the profile encoded as a JSON object.
-static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0,
-                                                       mozilla::dom::Promise* = 0) {}
-static inline void profiler_get_start_params(int* aEntrySize,
+PROFILER_FUNC_VOID(profiler_get_profile_jsobject_async(double aSinceTime = 0,
+                                                       mozilla::dom::Promise* = 0))
+
+PROFILER_FUNC_VOID(profiler_get_start_params(int* aEntrySize,
                                              double* aInterval,
                                              mozilla::Vector<const char*>* aFilters,
-                                             mozilla::Vector<const char*>* aFeatures) {}
+                                             mozilla::Vector<const char*>* aFeatures))
 
-// Get the profile and write it into a file
-static inline void profiler_save_profile_to_file(char* aFilename) { }
+// Get the profile and write it into a file. It is 'extern "C"' so that it is
+// easily callable from a debugger in a build without debugging information
+// (work around http://llvm.org/bugs/show_bug.cgi?id=22211).
+extern "C" {
+PROFILER_FUNC_VOID(profiler_save_profile_to_file(const char* aFilename))
+}
 
 // Get the features supported by the profiler that are accepted by profiler_init.
 // Returns a null terminated char* array.
-static inline char** profiler_get_features() { return nullptr; }
+PROFILER_FUNC(const char** profiler_get_features(), nullptr)
+
+PROFILER_FUNC_VOID(profiler_get_buffer_info_helper(uint32_t* aCurrentPosition,
+                                                   uint32_t* aTotalSize,
+                                                   uint32_t* aGeneration))
 
 // Get information about the current buffer status.
 // Retursn (using outparams) the current write position in the buffer,
 // the total size of the buffer, and the generation of the buffer.
 // This information may be useful to a user-interface displaying the
 // current status of the profiler, allowing the user to get a sense
 // for how fast the buffer is being written to, and how much
 // data is visible.
-static inline void profiler_get_buffer_info(uint32_t *aCurrentPosition,
-                                            uint32_t *aTotalSize,
-                                            uint32_t *aGeneration)
+static inline void profiler_get_buffer_info(uint32_t* aCurrentPosition,
+                                            uint32_t* aTotalSize,
+                                            uint32_t* aGeneration)
 {
   *aCurrentPosition = 0;
   *aTotalSize = 0;
   *aGeneration = 0;
+
+  profiler_get_buffer_info_helper(aCurrentPosition, aTotalSize, aGeneration);
 }
 
-// Discard the profile, throw away the profile and notify 'profiler-locked'.
-// This function is to be used when entering private browsing to prevent
-// the profiler from collecting sensitive data.
-static inline void profiler_lock() {}
+// Lock the profiler. When locked the profiler is (1) stopped,
+// (2) profile data is cleared, (3) 'profiler-locked' is fired.
+// This is used to lock down the profiler during private browsing.
+PROFILER_FUNC_VOID(profiler_lock())
 
-// Re-enable the profiler and notify 'profiler-unlocked'.
-static inline void profiler_unlock() {}
+// Unlock the profiler, leaving it stopped, and fire 'profiler-unlocked'.
+PROFILER_FUNC_VOID(profiler_unlock())
 
-static inline void profiler_register_thread(const char* name, void* guessStackTop) {}
-static inline void profiler_unregister_thread() {}
+// Register/unregister threads with the profiler.
+PROFILER_FUNC_VOID(profiler_register_thread(const char* name,
+                                            void* guessStackTop))
+PROFILER_FUNC_VOID(profiler_unregister_thread())
 
 // These functions tell the profiler that a thread went to sleep so that we can avoid
 // sampling it while it's sleeping. Calling profiler_sleep_start() twice without
 // profiler_sleep_end() is an error.
-static inline void profiler_sleep_start() {}
-static inline void profiler_sleep_end() {}
-static inline bool profiler_is_sleeping() { return false; }
+PROFILER_FUNC_VOID(profiler_sleep_start())
+PROFILER_FUNC_VOID(profiler_sleep_end())
+PROFILER_FUNC(bool profiler_is_sleeping(), false)
 
 // Call by the JSRuntime's operation callback. This is used to enable
 // profiling on auxilerary threads.
-static inline void profiler_js_operation_callback() {}
+PROFILER_FUNC_VOID(profiler_js_operation_callback())
 
-static inline double profiler_time() { return 0; }
-static inline double profiler_time(const mozilla::TimeStamp& aTime) { return 0; }
+PROFILER_FUNC(double profiler_time(), 0)
+PROFILER_FUNC(double profiler_time(const mozilla::TimeStamp& aTime), 0)
+
+PROFILER_FUNC(bool profiler_in_privacy_mode(), false)
 
-static inline bool profiler_in_privacy_mode() { return false; }
+PROFILER_FUNC_VOID(profiler_log(const char *str))
+PROFILER_FUNC_VOID(profiler_log(const char *fmt, va_list args))
 
-static inline void profiler_log(const char *str) {}
-static inline void profiler_log(const char *fmt, va_list args) {}
+// End of the functions defined whether the profiler is enabled or not.
 
-#else
+#if defined(MOZ_ENABLE_PROFILER_SPS)
 
 #include <stdlib.h>
 #include <signal.h>
 #include "js/ProfilingStack.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/ThreadLocal.h"
 #include "nscore.h"
 #include "PseudoStack.h"
@@ -261,45 +304,36 @@ static inline void profiler_log(const ch
 #ifdef min
 #undef min
 #endif
 
 class GeckoSampler;
 class nsISupports;
 class ProfilerMarkerPayload;
 
-namespace mozilla {
-class TimeStamp;
-
-namespace dom {
-class Promise;
-} // namespace dom
-} // namespace mozilla
-
-
 extern MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
 extern MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker;
 extern bool stack_key_initialized;
 
 #ifndef SAMPLE_FUNCTION_NAME
 # ifdef __GNUC__
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # elif defined(_MSC_VER)
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # else
 #  define SAMPLE_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
 # endif
 #endif
 
 // Returns a handle to pass on exit. This can check that we are popping the
 // correct callstack.
-inline void*
-mozilla_sampler_call_enter(const char *aInfo,
-                           js::ProfileEntry::Category aCategory,
-                           void *aFrameAddress, bool aCopy, uint32_t line)
+static inline void*
+profiler_call_enter(const char* aInfo,
+                    js::ProfileEntry::Category aCategory,
+                    void *aFrameAddress, bool aCopy, uint32_t line)
 {
   // check if we've been initialized to avoid calling pthread_getspecific
   // with a null tlsStack which will return undefined results.
   if (!stack_key_initialized)
     return nullptr;
 
   PseudoStack *stack = tlsPseudoStack.get();
   // we can't infer whether 'stack' has been initialized
@@ -314,342 +348,33 @@ mozilla_sampler_call_enter(const char *a
   // The handle is meant to support future changes
   // but for now it is simply use to save a call to
   // pthread_getspecific on exit. It also supports the
   // case where the sampler is initialized between
   // enter and exit.
   return stack;
 }
 
-inline void
-mozilla_sampler_call_exit(void *aHandle)
+static inline void
+profiler_call_exit(void* aHandle)
 {
   if (!aHandle)
     return;
 
   PseudoStack *stack = (PseudoStack*)aHandle;
   stack->popAndMaybeDelete();
 }
 
-void mozilla_sampler_add_marker(const char *aMarker,
-                                ProfilerMarkerPayload *aPayload = nullptr);
-
-void mozilla_sampler_start(int aEntries, double aInterval,
-                           const char** aFeatures, uint32_t aFeatureCount,
-                           const char** aThreadNameFilters, uint32_t aFilterCount);
-
-void mozilla_sampler_stop();
-
-bool mozilla_sampler_is_paused();
-void mozilla_sampler_pause();
-void mozilla_sampler_resume();
-
-UniqueProfilerBacktrace mozilla_sampler_get_backtrace();
-void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize);
-
-bool mozilla_sampler_is_active();
-
-bool mozilla_sampler_feature_active(const char* aName);
-
-void mozilla_sampler_responsiveness(const mozilla::TimeStamp& time);
-
-void mozilla_sampler_frame_number(int frameNumber);
-
-mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
-
-JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
-void mozilla_sampler_get_profile_data_async(double aSinceTime,
-                                            mozilla::dom::Promise* aPromise);
-MOZ_EXPORT
-void mozilla_sampler_save_profile_to_file_async(double aSinceTime,
-                                                const char* aFileName);
-void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
-                                               double* aInterval,
-                                               mozilla::Vector<const char*>* aFilters,
-                                               mozilla::Vector<const char*>* aFeatures);
-void mozilla_sampler_get_gatherer(nsISupports** aRetVal);
-
-// Make this function easily callable from a debugger in a build without
-// debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
-extern "C" {
-  void mozilla_sampler_save_profile_to_file(const char* aFilename);
-}
-
-const char** mozilla_sampler_get_features();
-
-void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
-                                     uint32_t *aGeneration);
-
-void mozilla_sampler_init(void* stackTop);
-
-void mozilla_sampler_shutdown();
-
-// Lock the profiler. When locked the profiler is (1) stopped,
-// (2) profile data is cleared, (3) profiler-locked is fired.
-// This is used to lock down the profiler during private browsing
-void mozilla_sampler_lock();
-
-// Unlock the profiler, leaving it stopped and fires profiler-unlocked.
-void mozilla_sampler_unlock();
-
-// Register/unregister threads with the profiler
-bool mozilla_sampler_register_thread(const char* name, void* stackTop);
-void mozilla_sampler_unregister_thread();
-
-void mozilla_sampler_sleep_start();
-void mozilla_sampler_sleep_end();
-bool mozilla_sampler_is_sleeping();
-
-double mozilla_sampler_time();
-double mozilla_sampler_time(const mozilla::TimeStamp& aTime);
-
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
-                             TracingMetadata aMetaData);
-
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
-                             UniqueProfilerBacktrace aCause,
-                             TracingMetadata aMetaData);
-
-void mozilla_sampler_log(const char *fmt, va_list args);
-
-static inline
-void profiler_init(void* stackTop)
-{
-  mozilla_sampler_init(stackTop);
-}
-
-static inline
-void profiler_shutdown()
-{
-  mozilla_sampler_shutdown();
-}
-
-static inline
-void profiler_start(int aProfileEntries, double aInterval,
-                    const char** aFeatures, uint32_t aFeatureCount,
-                    const char** aThreadNameFilters, uint32_t aFilterCount)
-{
-  mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount);
-}
-
-static inline
-void profiler_stop()
-{
-  mozilla_sampler_stop();
-}
-
-static inline
-bool profiler_is_paused()
-{
-  return mozilla_sampler_is_paused();
-}
-
-static inline
-void profiler_pause()
-{
-  mozilla_sampler_pause();
-}
-
-static inline
-void profiler_resume()
-{
-  mozilla_sampler_resume();
-}
-
-static inline
-UniqueProfilerBacktrace profiler_get_backtrace()
-{
-  return mozilla_sampler_get_backtrace();
-}
-
-static inline
-void profiler_get_backtrace_noalloc(char *output, size_t outputSize)
-{
-  return mozilla_sampler_get_backtrace_noalloc(output, outputSize);
-}
-
-static inline
-bool profiler_is_active()
-{
-  return mozilla_sampler_is_active();
-}
-
-static inline
-bool profiler_feature_active(const char* aName)
-{
-  return mozilla_sampler_feature_active(aName);
-}
-
-static inline
-void profiler_responsiveness(const mozilla::TimeStamp& aTime)
-{
-  mozilla_sampler_responsiveness(aTime);
-}
+void profiler_add_marker(const char *aMarker,
+                         ProfilerMarkerPayload *aPayload = nullptr);
 
-static inline
-void profiler_set_frame_number(int frameNumber)
-{
-  return mozilla_sampler_frame_number(frameNumber);
-}
-
-static inline
-mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0)
-{
-  return mozilla_sampler_get_profile(aSinceTime);
-}
-
-static inline
-JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0)
-{
-  return mozilla_sampler_get_profile_data(aCx, aSinceTime);
-}
-
-static inline
-void profiler_get_profile_jsobject_async(double aSinceTime = 0,
-                                         mozilla::dom::Promise* aPromise = 0)
-{
-  mozilla_sampler_get_profile_data_async(aSinceTime, aPromise);
-}
-
-static inline
-void profiler_get_start_params(int* aEntrySize,
-                               double* aInterval,
-                               mozilla::Vector<const char*>* aFilters,
-                               mozilla::Vector<const char*>* aFeatures)
-{
-  mozilla_sampler_get_profiler_start_params(aEntrySize, aInterval, aFilters, aFeatures);
-}
-
-static inline
-void profiler_get_gatherer(nsISupports** aRetVal)
-{
-  mozilla_sampler_get_gatherer(aRetVal);
-}
-
-static inline
-void profiler_save_profile_to_file(const char* aFilename)
-{
-  return mozilla_sampler_save_profile_to_file(aFilename);
-}
-
-static inline
-const char** profiler_get_features()
-{
-  return mozilla_sampler_get_features();
-}
-
-static inline
-void profiler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
-                              uint32_t *aGeneration)
-{
-  return mozilla_sampler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
-}
-
-static inline
-void profiler_lock()
-{
-  return mozilla_sampler_lock();
-}
-
-static inline
-void profiler_unlock()
-{
-  return mozilla_sampler_unlock();
-}
-
-static inline
-void profiler_register_thread(const char* name, void* guessStackTop)
-{
-  mozilla_sampler_register_thread(name, guessStackTop);
-}
-
-static inline
-void profiler_unregister_thread()
-{
-  mozilla_sampler_unregister_thread();
-}
-
-static inline
-void profiler_sleep_start()
-{
-  mozilla_sampler_sleep_start();
-}
-
-static inline
-void profiler_sleep_end()
-{
-  mozilla_sampler_sleep_end();
-}
-
-static inline
-bool profiler_is_sleeping()
-{
-  return mozilla_sampler_is_sleeping();
-}
-
-static inline
-void profiler_js_operation_callback()
-{
-  PseudoStack *stack = tlsPseudoStack.get();
-  if (!stack) {
-    return;
-  }
-
-  stack->jsOperationCallback();
-}
-
-static inline
-double profiler_time()
-{
-  return mozilla_sampler_time();
-}
-
-static inline
-double profiler_time(const mozilla::TimeStamp& aTime)
-{
-  return mozilla_sampler_time(aTime);
-}
-
-static inline
-bool profiler_in_privacy_mode()
-{
-  PseudoStack *stack = tlsPseudoStack.get();
-  if (!stack) {
-    return false;
-  }
-  return stack->mPrivacyMode;
-}
-
-static inline void profiler_tracing(const char* aCategory, const char* aInfo,
-                                    UniqueProfilerBacktrace aCause,
-                                    TracingMetadata aMetaData = TRACING_DEFAULT)
-{
-  // Don't insert a marker if we're not profiling to avoid
-  // the heap copy (malloc).
-  if (!stack_key_initialized || !profiler_is_active()) {
-    return;
-  }
-
-  mozilla_sampler_tracing(aCategory, aInfo, mozilla::Move(aCause), aMetaData);
-}
-
-static inline void profiler_tracing(const char* aCategory, const char* aInfo,
-                                    TracingMetadata aMetaData = TRACING_DEFAULT)
-{
-  if (!stack_key_initialized)
-    return;
-
-  // Don't insert a marker if we're not profiling to avoid
-  // the heap copy (malloc).
-  if (!profiler_is_active()) {
-    return;
-  }
-
-  mozilla_sampler_tracing(aCategory, aInfo, aMetaData);
-}
+MOZ_EXPORT  // XXX: should this be 'extern "C"' as well?
+void profiler_save_profile_to_file_async(double aSinceTime,
+                                         const char* aFileName);
+void profiler_get_gatherer(nsISupports** aRetVal);
 
 #define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
 #define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line)
 #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
 
 // Uncomment this to turn on systrace or build with
 // ac_add_options --enable-systace
 //#define MOZ_USE_SYSTRACE
@@ -675,28 +400,16 @@ static inline void profiler_tracing(cons
 # ifdef REMOVE_HAVE_ANDROID_OS
 #  undef HAVE_ANDROID_OS
 #  undef REMOVE_HAVE_ANDROID_OS
 # endif
 #else
 # define MOZ_PLATFORM_TRACING(name)
 #endif
 
-// we want the class and function name but can't easily get that using preprocessor macros
-// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
-
-#define PROFILER_LABEL(name_space, info, category) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__)
-
-#define PROFILER_LABEL_FUNC(category) MOZ_PLATFORM_TRACING(SAMPLE_FUNCTION_NAME) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(SAMPLE_FUNCTION_NAME, category, __LINE__)
-
-#define PROFILER_LABEL_PRINTF(name_space, info, category, ...) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__)
-
-#define PROFILER_MARKER(info) mozilla_sampler_add_marker(info)
-#define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload)
-
 /* FIXME/bug 789667: memory constraints wouldn't much of a problem for
  * this small a sample buffer size, except that serializing the
  * profile data is extremely, unnecessarily memory intensive. */
 #ifdef MOZ_WIDGET_GONK
 # define PLATFORM_LIKELY_MEMORY_CONSTRAINED
 #endif
 
 #if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6)
@@ -738,20 +451,20 @@ namespace mozilla {
 class MOZ_RAII SamplerStackFrameRAII {
 public:
   // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
   SamplerStackFrameRAII(const char *aInfo,
     js::ProfileEntry::Category aCategory, uint32_t line
     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line);
+    mHandle = profiler_call_enter(aInfo, aCategory, this, false, line);
   }
   ~SamplerStackFrameRAII() {
-    mozilla_sampler_call_exit(mHandle);
+    profiler_call_exit(mHandle);
   }
 private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   void* mHandle;
 };
 
 static const int SAMPLER_MAX_STRING = 128;
 class MOZ_RAII SamplerStackFramePrintfRAII {
@@ -766,53 +479,41 @@ public:
       va_start(args, aFormat);
       char buff[SAMPLER_MAX_STRING];
 
       // We have to use seperate printf's because we're using
       // the vargs.
       VsprintfLiteral(buff, aFormat, args);
       SprintfLiteral(mDest, "%s %s", aInfo, buff);
 
-      mHandle = mozilla_sampler_call_enter(mDest, aCategory, this, true, line);
+      mHandle = profiler_call_enter(mDest, aCategory, this, true, line);
       va_end(args);
     } else {
-      mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line);
+      mHandle = profiler_call_enter(aInfo, aCategory, this, false, line);
     }
   }
   ~SamplerStackFramePrintfRAII() {
-    mozilla_sampler_call_exit(mHandle);
+    profiler_call_exit(mHandle);
   }
 private:
   char mDest[SAMPLER_MAX_STRING];
   void* mHandle;
 };
 
 } // namespace mozilla
 
 inline PseudoStack*
 profiler_get_pseudo_stack(void)
 {
   if (!stack_key_initialized)
     return nullptr;
   return tlsPseudoStack.get();
 }
 
-static inline
-void profiler_log(const char *str)
-{
-  profiler_tracing("log", str, TRACING_EVENT);
-}
-
-static inline
-void profiler_log(const char *fmt, va_list args)
-{
-  mozilla_sampler_log(fmt, args);
-}
-
-#endif
+#endif  // defined(MOZ_ENABLE_PROFILER_SPS)
 
 namespace mozilla {
 
 class MOZ_RAII GeckoProfilerInitRAII {
 public:
   explicit GeckoProfilerInitRAII(void* stackTop) {
     profiler_init(stackTop);
   }
--- a/xpcom/threads/AbstractThread.cpp
+++ b/xpcom/threads/AbstractThread.cpp
@@ -23,92 +23,177 @@
 namespace mozilla {
 
 LazyLogModule gMozPromiseLog("MozPromise");
 LazyLogModule gStateWatchingLog("StateWatching");
 
 StaticRefPtr<AbstractThread> sMainThread;
 MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;
 
-class XPCOMThreadWrapper : public AbstractThread
+class EventTargetWrapper : public AbstractThread
 {
 public:
-  explicit XPCOMThreadWrapper(nsIThread* aTarget, bool aRequireTailDispatch)
+  explicit EventTargetWrapper(nsIEventTarget* aTarget, bool aRequireTailDispatch)
     : AbstractThread(aRequireTailDispatch)
     , mTarget(aTarget)
   {
     // Our current mechanism of implementing tail dispatch is appshell-specific.
     // This is because a very similar mechanism already exists on the main
     // thread, and we want to avoid making event dispatch on the main thread
     // more complicated than it already is.
     //
     // If you need to use tail dispatch on other XPCOM threads, you'll need to
     // implement an nsIThreadObserver to fire the tail dispatcher at the
     // appropriate times.
+    nsCOMPtr<nsIThread> thread(do_QueryInterface(aTarget));
+    bool isOnCurrentThread = false;
+    aTarget->IsOnCurrentThread(&isOnCurrentThread);
+
     MOZ_ASSERT_IF(aRequireTailDispatch,
-                  NS_IsMainThread() && NS_GetCurrentThread() == aTarget);
+      (thread && NS_IsMainThread() && NS_GetCurrentThread() == thread) ||
+      (!thread && NS_IsMainThread() && isOnCurrentThread));
+
+    // XXX Bug 1323742:
+    // We hold mRunningThread for IsCurrentThreadIn() for now.
+    // This shall be replaced by this == GetCurrent() in the future in
+    // AbstractThread perspective instead of PR_Thread perspective.
+    mRunningThread = thread ? thread.get() : NS_GetCurrentThread();
+    MOZ_ASSERT(mRunningThread);
   }
 
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) override
   {
-    nsCOMPtr<nsIRunnable> r = aRunnable;
     AbstractThread* currentThread;
     if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
-      currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
+      currentThread->TailDispatcher().AddTask(this, Move(aRunnable), aFailureHandling);
       return;
     }
 
-    nsresult rv = mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
+    RefPtr<nsIRunnable> runner(new Runner(this, Move(aRunnable), false /* already drained by TaskGroupRunnable  */));
+    nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
     MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
     Unused << rv;
   }
 
   virtual bool IsCurrentThreadIn() override
   {
     // Compare NSPR threads so that this works after shutdown when
     // NS_GetCurrentThread starts returning null.
     PRThread* thread = nullptr;
-    mTarget->GetPRThread(&thread);
+    mRunningThread->GetPRThread(&thread);
     bool in = PR_GetCurrentThread() == thread;
     return in;
   }
 
   void FireTailDispatcher()
   {
     MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
     mTailDispatcher.ref().DrainDirectTasks();
     mTailDispatcher.reset();
   }
 
   virtual TaskDispatcher& TailDispatcher() override
   {
-    MOZ_ASSERT(this == sMainThread); // See the comment in the constructor.
+    // See the comment in the constructor.
+    MOZ_ASSERT(mRunningThread ==
+      static_cast<EventTargetWrapper*>(sMainThread.get())->mRunningThread);
     MOZ_ASSERT(IsCurrentThreadIn());
     if (!mTailDispatcher.isSome()) {
       mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
 
-      nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher);
+      nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &EventTargetWrapper::FireTailDispatcher);
       nsContentUtils::RunInStableState(event.forget());
     }
 
     return mTailDispatcher.ref();
   }
 
   virtual bool MightHaveTailTasks() override
   {
     return mTailDispatcher.isSome();
   }
 
-  virtual nsIThread* AsXPCOMThread() override { return mTarget; }
+  virtual nsIEventTarget* AsEventTarget() override { return mTarget; }
 
 private:
-  RefPtr<nsIThread> mTarget;
+  nsCOMPtr<nsIThread> mRunningThread;
+  RefPtr<nsIEventTarget> mTarget;
   Maybe<AutoTaskDispatcher> mTailDispatcher;
+
+  virtual already_AddRefed<nsIRunnable>
+  CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable) override
+  {
+    RefPtr<Runner> runner =
+      new Runner(this, Move(aRunnable), /* aDrainDirectTasks */ true);
+    return runner.forget();
+  }
+
+  class Runner : public Runnable {
+  public:
+    explicit Runner(EventTargetWrapper* aThread,
+                    already_AddRefed<nsIRunnable> aRunnable,
+                    bool aDrainDirectTasks)
+      : mThread(aThread)
+      , mRunnable(aRunnable)
+      , mDrainDirectTasks(aDrainDirectTasks)
+    {
+    }
+
+    NS_IMETHOD Run() override
+    {
+      class MOZ_STACK_CLASS AutoTaskGuard final {
+      public:
+        explicit AutoTaskGuard(EventTargetWrapper* aThread)
+          : mLastCurrentThread(nullptr)
+        {
+          MOZ_ASSERT(aThread);
+          mLastCurrentThread = sCurrentThreadTLS.get();
+          sCurrentThreadTLS.set(aThread);
+        }
+
+        ~AutoTaskGuard()
+        {
+          sCurrentThreadTLS.set(mLastCurrentThread);
+        }
+      private:
+        AbstractThread* mLastCurrentThread;
+      } taskGuard(mThread);
+
+      MOZ_ASSERT(mThread == AbstractThread::GetCurrent());
+      MOZ_ASSERT(mThread->IsCurrentThreadIn());
+      nsresult rv = mRunnable->Run();
+
+      if (mDrainDirectTasks) {
+        mThread->TailDispatcher().DrainDirectTasks();
+      }
+
+      return rv;
+    }
+
+    NS_IMETHOD GetName(nsACString& aName) override
+    {
+      aName.AssignLiteral("AbstractThread::Runner");
+      if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
+        nsAutoCString name;
+        named->GetName(name);
+        if (!name.IsEmpty()) {
+          aName.AppendLiteral(" for ");
+          aName.Append(name);
+        }
+      }
+      return NS_OK;
+    }
+
+  private:
+    RefPtr<EventTargetWrapper> mThread;
+    RefPtr<nsIRunnable> mRunnable;
+    bool mDrainDirectTasks;
+  };
 };
 
 void
 AbstractThread::TailDispatchTasksFor(AbstractThread* aThread)
 {
   if (MightHaveTailTasks()) {
     TailDispatcher().DispatchTasksFor(aThread);
   }
@@ -149,17 +234,17 @@ AbstractThread::MainThread()
 void
 AbstractThread::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sMainThread);
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
   MOZ_DIAGNOSTIC_ASSERT(mainThread);
-  sMainThread = new XPCOMThreadWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
+  sMainThread = new EventTargetWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
   ClearOnShutdown(&sMainThread);
 
   if (!sCurrentThreadTLS.init()) {
     MOZ_CRASH();
   }
   sCurrentThreadTLS.set(sMainThread);
 }
 
@@ -174,19 +259,35 @@ AbstractThread::DispatchDirectTask(alrea
 {
   GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
 }
 
 /* static */
 already_AddRefed<AbstractThread>
 AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
 {
-  RefPtr<XPCOMThreadWrapper> wrapper = new XPCOMThreadWrapper(aThread, aRequireTailDispatch);
+  RefPtr<EventTargetWrapper> wrapper = new EventTargetWrapper(aThread, aRequireTailDispatch);
   // Set the thread-local sCurrentThreadTLS to point to the wrapper on the
   // target thread. This ensures that sCurrentThreadTLS is as expected by
   // AbstractThread::GetCurrent() on the target thread.
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableFunction([wrapper]() { sCurrentThreadTLS.set(wrapper); });
   aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
   return wrapper.forget();
 }
 
+/* static  */
+already_AddRefed<AbstractThread>
+AbstractThread::CreateEventTargetWrapper(nsIEventTarget* aEventTarget,
+                                         bool aRequireTailDispatch)
+{
+  MOZ_ASSERT(aEventTarget);
+  nsCOMPtr<nsIThread> thread(do_QueryInterface(aEventTarget));
+  Unused << thread; // simpler than DebugOnly<nsCOMPtr<nsIThread>>
+  MOZ_ASSERT(!thread, "nsIThread should be wrapped by CreateXPCOMThreadWrapper!");
+
+  RefPtr<EventTargetWrapper> wrapper =
+    new EventTargetWrapper(aEventTarget, aRequireTailDispatch);
+
+  return wrapper.forget();
+}
+
 } // namespace mozilla
--- a/xpcom/threads/AbstractThread.h
+++ b/xpcom/threads/AbstractThread.h
@@ -23,33 +23,39 @@ class TaskDispatcher;
 /*
  * We often want to run tasks on a target that guarantees that events will never
  * run in parallel. There are various target types that achieve this - namely
  * nsIThread and TaskQueue. Note that nsIThreadPool (which implements
  * nsIEventTarget) does not have this property, so we do not want to use
  * nsIEventTarget for this purpose. This class encapsulates the specifics of
  * the structures we might use here and provides a consistent interface.
  *
- * At present, the supported AbstractThread implementations are TaskQueue
- * and AbstractThread::MainThread. If you add support for another thread that is
- * not the MainThread, you'll need to figure out how to make it unique such that
- * comparing AbstractThread pointers is equivalent to comparing nsIThread pointers.
+ * At present, the supported AbstractThread implementations are TaskQueue,
+ * AbstractThread::MainThread() and DocGroup::AbstractThreadFor().
+ * If you add support for another thread that is not the MainThread, you'll need
+ * to figure out how to make it unique such that comparing AbstractThread
+ * pointers is equivalent to comparing nsIThread pointers.
  */
 class AbstractThread
 {
 public:
   // Returns the AbstractThread that the caller is currently running in, or null
   // if the caller is not running in an AbstractThread.
   static AbstractThread* GetCurrent() { return sCurrentThreadTLS.get(); }
 
   AbstractThread(bool aSupportsTailDispatch) : mSupportsTailDispatch(aSupportsTailDispatch) {}
 
+  // Returns an AbstractThread wrapper of a nsIThread.
   static already_AddRefed<AbstractThread>
   CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch);
 
+  // Returns an AbstractThread wrapper of a non-nsIThread EventTarget on the main thread.
+  static already_AddRefed<AbstractThread>
+  CreateEventTargetWrapper(nsIEventTarget* aEventTarget, bool aRequireTailDispatch);
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractThread);
 
   enum DispatchFailureHandling { AssertDispatchSuccess, DontAssertDispatchSuccess };
   enum DispatchReason { NormalDispatch, TailDispatch };
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) = 0;
 
@@ -80,28 +86,38 @@ public:
   bool SupportsTailDispatch() const { return mSupportsTailDispatch; }
 
   // Returns true if this thread requires all dispatches originating from
   // aThread go through the tail dispatcher.
   bool RequiresTailDispatch(AbstractThread* aThread) const;
   bool RequiresTailDispatchFromCurrentThread() const;
 
   virtual TaskQueue* AsTaskQueue() { MOZ_CRASH("Not a task queue!"); }
-  virtual nsIThread* AsXPCOMThread() { MOZ_CRASH("Not an XPCOM thread!"); }
+  virtual nsIEventTarget* AsEventTarget() { MOZ_CRASH("Not an event target!"); }
 
-  // Convenience method for getting an AbstractThread for the main thread.
+  // Returns the non-DocGroup version of AbstractThread on the main thread.
+  // A DocGroup-versioned one is available in DispatcherTrait::AbstractThreadFor().
+  // Note: DispatcherTrait::AbstractThreadFor() SHALL be used when possible.
   static AbstractThread* MainThread();
 
   // Must be called exactly once during startup.
   static void InitStatics();
 
   void DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable);
 
   static void DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable);
 
+  // Create a runnable that will run |aRunnable| and drain the direct tasks
+  // generated by it.
+  virtual already_AddRefed<nsIRunnable>
+  CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable)
+  {
+    MOZ_CRASH("Not support!");
+  }
+
 protected:
   virtual ~AbstractThread() {}
   static MOZ_THREAD_LOCAL(AbstractThread*) sCurrentThreadTLS;
 
   // True if we want to require that every task dispatched from tasks running in
   // this queue go through our queue's tail dispatcher.
   const bool mSupportsTailDispatch;
 };