Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 23 Jan 2017 11:42:26 +0100
changeset 465485 0b284d45431ef73097cb313ec9b1390bb5cbba24
parent 465484 4c7d2394a2adf1ad257fb080ebc31b78741245af (current diff)
parent 464954 36486fdc3813ef7943ae5b07b4128866d1938a6c (diff)
child 465486 c61b0e70d0cfadd25f31fe288dbe2fb18cc7da0f
push id42606
push usergsquelart@mozilla.com
push dateTue, 24 Jan 2017 05:22:29 +0000
milestone53.0a1
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;
 };