Bug 1314833 - Part 2.1: Factor out AbstractThread::MainThread() used in Media Playback. r=billm,jwwang
authorBevis Tseng <btseng@mozilla.com>
Tue, 29 Nov 2016 13:03:36 +0800
changeset 377901 0a5d3ec02dcd355cf83eb4b717e8ef2b687f7d0b
parent 377900 1cf74b47953add5094f559f37766b5e99dfb32a7
child 377902 02e5708a75fcdd9e8e4b065995f00b29896fb4b5
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm, jwwang
bugs1314833
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1314833 - Part 2.1: Factor out AbstractThread::MainThread() used in Media Playback. r=billm,jwwang MozReview-Commit-ID: 9yJi3iDtVZG
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/AbstractMediaDecoder.h
dom/media/Benchmark.cpp
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderOwner.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/MediaFormatReader.cpp
dom/media/TextTrackCue.cpp
dom/media/fmp4/MP4Decoder.cpp
dom/media/gmp/GMPParent.cpp
dom/media/gmp/GMPServiceParent.cpp
dom/media/gtest/MockMediaDecoderOwner.h
dom/media/gtest/TestMediaDataDecoder.cpp
dom/media/mediasink/DecodedStream.cpp
dom/media/mediasink/DecodedStream.h
dom/media/mediasource/AutoTaskQueue.h
dom/media/mediasource/MediaSource.cpp
dom/media/mediasource/MediaSource.h
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceDemuxer.cpp
dom/media/mediasource/MediaSourceDemuxer.h
dom/media/mediasource/SourceBuffer.cpp
dom/media/mediasource/SourceBuffer.h
dom/media/mediasource/TrackBuffersManager.cpp
dom/media/mediasource/TrackBuffersManager.h
dom/media/ogg/OggDemuxer.cpp
dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp
dom/media/platforms/wmf/WMFAudioMFTManager.cpp
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
dom/media/webaudio/BufferDecoder.cpp
dom/media/webaudio/BufferDecoder.h
dom/media/webaudio/MediaBufferDecoder.cpp
dom/system/gonk/AudioManager.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3568,17 +3568,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),
@@ -4632,17 +4633,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();
@@ -7131,16 +7132,24 @@ void
 HTMLMediaElement::UpdateCustomPolicyAfterPlayed()
 {
   OpenUnsupportedMediaWithExternalAppIfNeeded();
   if (mAudioChannelWrapper) {
     mAudioChannelWrapper->NotifyPlayStarted();
   }
 }
 
+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/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/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);
 
@@ -624,16 +632,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
@@ -1937,18 +1937,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)
 {
@@ -2472,16 +2476,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),
@@ -2607,17 +2612,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;
@@ -459,16 +460,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/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/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"
@@ -65,17 +66,17 @@ public:
   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()
@@ -89,48 +90,49 @@ private:
   Mutex mMutex;
   // Members below are protected by mMutex.
   RefPtr<MediaStream> mStream;
   // Main thread only.
   MozPromiseHolder<GenericPromise> mFinishPromise;
 };
 
 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();
   void DumpDebugInfo();
 
   /* The following group of fields are protected by the decoder's monitor
    * and can be read or written on any thread.
@@ -154,35 +156,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())
   // 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,
@@ -205,17 +210,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();
 }
@@ -227,22 +232,24 @@ DecodedStreamData::DumpDebugInfo()
     "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)
 {
 }
@@ -293,52 +300,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);
@@ -386,17 +396,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/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/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/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);