Bug 1316215 - Promisify GMPService GetGMPContentParent and GetServiceChild. r=gerald
authorChris Pearce <cpearce@mozilla.com>
Wed, 09 Nov 2016 15:52:22 +1300
changeset 324013 cb29571e849184f019cf7343afb0ac6d36fa0e11
parent 324012 3a60967cd9ed58bf2ec9573b0185e3537a42518c
child 324014 4be3169b9d0257f3e0182dd1ad5d60a9a98eef48
push id30988
push usercbook@mozilla.com
push dateThu, 24 Nov 2016 15:35:58 +0000
treeherdermozilla-central@9aef92f7911d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1316215
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 1316215 - Promisify GMPService GetGMPContentParent and GetServiceChild. r=gerald MozReview-Commit-ID: 5ydGmZWGzWG
dom/media/gmp/GMPParent.cpp
dom/media/gmp/GMPParent.h
dom/media/gmp/GMPService.cpp
dom/media/gmp/GMPService.h
dom/media/gmp/GMPServiceChild.cpp
dom/media/gmp/GMPServiceChild.h
dom/media/gmp/GMPServiceParent.cpp
dom/media/gmp/GMPServiceParent.h
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -1048,94 +1048,85 @@ GMPParent::RecvAsyncShutdownComplete()
     mService->SetAsyncShutdownPluginState(this, 'L',
       NS_LITERAL_CSTRING("Received AsyncShutdownComplete"));
   }
 #endif
   AbortAsyncShutdown();
   return IPC_OK();
 }
 
-class RunCreateContentParentCallbacks : public Runnable
+void
+GMPParent::ResolveGetContentParentPromises()
 {
-public:
-  explicit RunCreateContentParentCallbacks(GMPContentParent* aGMPContentParent)
-    : mGMPContentParent(aGMPContentParent)
-  {
-  }
-
-  void TakeCallbacks(nsTArray<UniquePtr<GetGMPContentParentCallback>>& aCallbacks)
-  {
-    mCallbacks.SwapElements(aCallbacks);
+  nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> promises;
+  promises.SwapElements(mGetContentParentPromises);
+  MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
+  for (auto& holder : promises) {
+    holder->Resolve(mGMPContentParent, __func__);
   }
-
-  NS_IMETHOD
-  Run() override
-  {
-    for (uint32_t i = 0, length = mCallbacks.Length(); i < length; ++i) {
-      mCallbacks[i]->Done(mGMPContentParent);
-    }
-    return NS_OK;
-  }
-
-private:
-  RefPtr<GMPContentParent> mGMPContentParent;
-  nsTArray<UniquePtr<GetGMPContentParentCallback>> mCallbacks;
-};
+}
 
 PGMPContentParent*
 GMPParent::AllocPGMPContentParent(Transport* aTransport, ProcessId aOtherPid)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
   MOZ_ASSERT(!mGMPContentParent);
 
   mGMPContentParent = new GMPContentParent(this);
   mGMPContentParent->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(),
                           ipc::ParentSide);
 
-  RefPtr<RunCreateContentParentCallbacks> runCallbacks =
-    new RunCreateContentParentCallbacks(mGMPContentParent);
-  runCallbacks->TakeCallbacks(mCallbacks);
-  NS_DispatchToCurrentThread(runCallbacks);
-  MOZ_ASSERT(mCallbacks.IsEmpty());
+  ResolveGetContentParentPromises();
 
   return mGMPContentParent;
 }
 
-bool
-GMPParent::GetGMPContentParent(UniquePtr<GetGMPContentParentCallback>&& aCallback)
+void
+GMPParent::RejectGetContentParentPromises()
+{
+  nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> promises;
+  promises.SwapElements(mGetContentParentPromises);
+  MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
+  for (auto& holder : promises) {
+    holder->Reject(NS_ERROR_FAILURE, __func__);
+  }
+}
+
+void
+GMPParent::GetGMPContentParent(UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>&& aPromiseHolder)
 {
   LOGD("%s %p", __FUNCTION__, this);
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   if (mGMPContentParent) {
-    aCallback->Done(mGMPContentParent);
+    aPromiseHolder->Resolve(mGMPContentParent, __func__);
   } else {
-    mCallbacks.AppendElement(Move(aCallback));
+    mGetContentParentPromises.AppendElement(Move(aPromiseHolder));
     // If we don't have a GMPContentParent and we try to get one for the first
-    // time (mCallbacks.Length() == 1) then call PGMPContent::Open. If more
+    // time (mGetContentParentPromises.Length() == 1) then call PGMPContent::Open. If more
     // calls to GetGMPContentParent happen before mGMPContentParent has been
     // set then we should just store them, so that they get called when we set
     // mGMPContentParent as a result of the PGMPContent::Open call.
-    if (mCallbacks.Length() == 1) {
+    if (mGetContentParentPromises.Length() == 1) {
       if (!EnsureProcessLoaded() || !PGMPContent::Open(this)) {
-        return false;
+        RejectGetContentParentPromises();
+        return;
       }
       // We want to increment this as soon as possible, to avoid that we'd try
       // to shut down the GMP process while we're still trying to get a
       // PGMPContentParent actor.
       ++mGMPContentChildCount;
     }
   }
-  return true;
 }
 
 already_AddRefed<GMPContentParent>
 GMPParent::ForgetGMPContentParent()
 {
-  MOZ_ASSERT(mCallbacks.IsEmpty());
+  MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
   return Move(mGMPContentParent.forget());
 }
 
 bool
 GMPParent::EnsureProcessLoaded(base::ProcessId* aID)
 {
   if (!EnsureProcessLoaded()) {
     return false;
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -68,30 +68,16 @@ enum GMPState {
   GMPStateNotLoaded,
   GMPStateLoaded,
   GMPStateUnloading,
   GMPStateClosing
 };
 
 class GMPContentParent;
 
-class GetGMPContentParentCallback
-{
-public:
-  GetGMPContentParentCallback()
-  {
-    MOZ_COUNT_CTOR(GetGMPContentParentCallback);
-  };
-  virtual ~GetGMPContentParentCallback()
-  {
-    MOZ_COUNT_DTOR(GetGMPContentParentCallback);
-  };
-  virtual void Done(GMPContentParent* aGMPContentParent) = 0;
-};
-
 class GMPParent final : public PGMPParent
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPParent)
 
   GMPParent();
 
   RefPtr<GenericPromise> Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
@@ -154,17 +140,17 @@ public:
     return nsCOMPtr<nsIFile>(mDirectory).forget();
   }
 
   void AbortAsyncShutdown();
 
   // Called when the child process has died.
   void ChildTerminated();
 
-  bool GetGMPContentParent(UniquePtr<GetGMPContentParentCallback>&& aCallback);
+  void GetGMPContentParent(UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>&& aPromiseHolder);
   already_AddRefed<GMPContentParent> ForgetGMPContentParent();
 
   bool EnsureProcessLoaded(base::ProcessId* aID);
 
   bool Bridge(GMPServiceParent* aGMPServiceParent);
 
   const nsTArray<GMPCapability>& GetCapabilities() const { return mCapabilities; }
 
@@ -201,16 +187,18 @@ private:
   mozilla::ipc::IPCResult RecvAsyncShutdownRequired() override;
 
   mozilla::ipc::IPCResult RecvPGMPContentChildDestroyed() override;
   bool IsUsed()
   {
     return mGMPContentChildCount > 0;
   }
 
+  void ResolveGetContentParentPromises();
+  void RejectGetContentParentPromises();
 
   static void AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure);
   nsresult EnsureAsyncShutdownTimeoutSet();
 
   GMPState mState;
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
   nsCString mDisplayName; // name of plugin displayed to users
@@ -234,17 +222,17 @@ private:
   nsCOMPtr<nsIThread> mGMPThread;
   nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
   // NodeId the plugin is assigned to, or empty if the the plugin is not
   // assigned to a NodeId.
   nsCString mNodeId;
   // This is used for GMP content in the parent, there may be more of these in
   // the content processes.
   RefPtr<GMPContentParent> mGMPContentParent;
-  nsTArray<UniquePtr<GetGMPContentParentCallback>> mCallbacks;
+  nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> mGetContentParentPromises;
   uint32_t mGMPContentChildCount;
 
   bool mAsyncShutdownRequired;
   bool mAsyncShutdownInProgress;
 
   int mChildPid;
 
   // We hold a self reference to ourself while the child process is alive.
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -290,201 +290,126 @@ GeckoMediaPluginService::GetThread(nsITh
 
 RefPtr<AbstractThread>
 GeckoMediaPluginService::GetAbstractGMPThread()
 {
   MutexAutoLock lock(mMutex);
   return mAbstractGMPThread;
 }
 
-class GetGMPContentParentForAudioDecoderDone : public GetGMPContentParentCallback
-{
-public:
-  explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback,
-                                                  GMPCrashHelper* aHelper)
-   : mCallback(Move(aCallback))
-   , mHelper(aHelper)
-  {
-  }
-
-  void Done(GMPContentParent* aGMPParent) override
-  {
-    GMPAudioDecoderParent* gmpADP = nullptr;
-    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPAudioDecoder(&gmpADP))) {
-      gmpADP->SetCrashHelper(mHelper);
-    }
-    mCallback->Done(gmpADP);
-  }
-
-private:
-  UniquePtr<GetGMPAudioDecoderCallback> mCallback;
-  RefPtr<GMPCrashHelper> mHelper;
-};
-
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPAudioDecoder(GMPCrashHelper* aHelper,
                                             nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForAudioDecoderDone(Move(aCallback), aHelper));
-  if (!GetContentParentFrom(aHelper,
-                            aNodeId,
-                            NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                            *aTags,
-                            Move(callback))) {
-    return NS_ERROR_FAILURE;
-  }
+  GetGMPAudioDecoderCallback* rawCallback = aCallback.release();
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  RefPtr<GMPCrashHelper> helper(aHelper);
+  GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER), *aTags)
+    ->Then(thread, __func__,
+      [rawCallback, helper](RefPtr<GMPContentParent> parent) {
+        UniquePtr<GetGMPAudioDecoderCallback> callback(rawCallback);
+        GMPAudioDecoderParent* actor = nullptr;
+        if (parent && NS_SUCCEEDED(parent->GetGMPAudioDecoder(&actor))) {
+          actor->SetCrashHelper(helper);
+        }
+        callback->Done(actor);
+      },
+      [rawCallback] {
+        UniquePtr<GetGMPAudioDecoderCallback> callback(rawCallback);
+        callback->Done(nullptr);
+      });
 
   return NS_OK;
 }
 
-class GetGMPContentParentForVideoDecoderDone : public GetGMPContentParentCallback
-{
-public:
-  explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
-                                                  GMPCrashHelper* aHelper,
-                                                  uint32_t aDecryptorId)
-   : mCallback(Move(aCallback))
-   , mHelper(aHelper)
-   , mDecryptorId(aDecryptorId)
-  {
-  }
-
-  void Done(GMPContentParent* aGMPParent) override
-  {
-    GMPVideoDecoderParent* gmpVDP = nullptr;
-    GMPVideoHostImpl* videoHost = nullptr;
-    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP, mDecryptorId))) {
-      videoHost = &gmpVDP->Host();
-      gmpVDP->SetCrashHelper(mHelper);
-    }
-    mCallback->Done(gmpVDP, videoHost);
-  }
-
-private:
-  UniquePtr<GetGMPVideoDecoderCallback> mCallback;
-  RefPtr<GMPCrashHelper> mHelper;
-  const uint32_t mDecryptorId;
-};
-
 NS_IMETHODIMP
 GeckoMediaPluginService::GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
                                                       nsTArray<nsCString>* aTags,
                                                       const nsACString& aNodeId,
                                                       UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
                                                       uint32_t aDecryptorId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper, aDecryptorId));
-  if (!GetContentParentFrom(aHelper,
-                            aNodeId,
-                            NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                            *aTags,
-                            Move(callback))) {
-    return NS_ERROR_FAILURE;
-  }
+  GetGMPVideoDecoderCallback* rawCallback = aCallback.release();
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  RefPtr<GMPCrashHelper> helper(aHelper);
+  GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER), *aTags)
+    ->Then(thread, __func__,
+      [rawCallback, helper, aDecryptorId](RefPtr<GMPContentParent> parent) {
+        UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
+        GMPVideoDecoderParent* actor = nullptr;
+        GMPVideoHostImpl* host = nullptr;
+        if (parent && NS_SUCCEEDED(parent->GetGMPVideoDecoder(&actor, aDecryptorId))) {
+          host = &(actor->Host());
+          actor->SetCrashHelper(helper);
+        }
+        callback->Done(actor, host);
+      },
+      [rawCallback] {
+        UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
+        callback->Done(nullptr, nullptr);
+      });
 
   return NS_OK;
 }
 
-class GetGMPContentParentForVideoEncoderDone : public GetGMPContentParentCallback
-{
-public:
-  explicit GetGMPContentParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback,
-                                                  GMPCrashHelper* aHelper)
-   : mCallback(Move(aCallback))
-   , mHelper(aHelper)
-  {
-  }
-
-  void Done(GMPContentParent* aGMPParent) override
-  {
-    GMPVideoEncoderParent* gmpVEP = nullptr;
-    GMPVideoHostImpl* videoHost = nullptr;
-    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoEncoder(&gmpVEP))) {
-      videoHost = &gmpVEP->Host();
-      gmpVEP->SetCrashHelper(mHelper);
-    }
-    mCallback->Done(gmpVEP, videoHost);
-  }
-
-private:
-  UniquePtr<GetGMPVideoEncoderCallback> mCallback;
-  RefPtr<GMPCrashHelper> mHelper;
-};
-
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoEncoder(GMPCrashHelper* aHelper,
                                             nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoEncoderDone(Move(aCallback), aHelper));
-  if (!GetContentParentFrom(aHelper,
-                            aNodeId,
-                            NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
-                            *aTags,
-                            Move(callback))) {
-    return NS_ERROR_FAILURE;
-  }
+  GetGMPVideoEncoderCallback* rawCallback = aCallback.release();
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  RefPtr<GMPCrashHelper> helper(aHelper);
+  GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER), *aTags)
+    ->Then(thread, __func__,
+      [rawCallback, helper](RefPtr<GMPContentParent> parent) {
+        UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
+        GMPVideoEncoderParent* actor = nullptr;
+        GMPVideoHostImpl* host = nullptr;
+        if (parent && NS_SUCCEEDED(parent->GetGMPVideoEncoder(&actor))) {
+          host = &(actor->Host());
+          actor->SetCrashHelper(helper);
+        }
+        callback->Done(actor, host);
+      },
+      [rawCallback] {
+        UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
+        callback->Done(nullptr, nullptr);
+      });
 
   return NS_OK;
 }
 
-class GetGMPContentParentForDecryptorDone : public GetGMPContentParentCallback
-{
-public:
-  explicit GetGMPContentParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback,
-                                               GMPCrashHelper* aHelper)
-   : mCallback(Move(aCallback))
-   , mHelper(aHelper)
-  {
-  }
-
-  void Done(GMPContentParent* aGMPParent) override
-  {
-    GMPDecryptorParent* ksp = nullptr;
-    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPDecryptor(&ksp))) {
-      ksp->SetCrashHelper(mHelper);
-    }
-    mCallback->Done(ksp);
-  }
-
-private:
-  UniquePtr<GetGMPDecryptorCallback> mCallback;
-  RefPtr<GMPCrashHelper> mHelper;
-};
-
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPDecryptor(GMPCrashHelper* aHelper,
                                          nsTArray<nsCString>* aTags,
                                          const nsACString& aNodeId,
                                          UniquePtr<GetGMPDecryptorCallback>&& aCallback)
 {
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
   if (!SandboxInfo::Get().CanSandboxMedia()) {
@@ -497,25 +422,33 @@ GeckoMediaPluginService::GetGMPDecryptor
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForDecryptorDone(Move(aCallback), aHelper));
-  if (!GetContentParentFrom(aHelper,
-                            aNodeId,
-                            NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                            *aTags,
-                            Move(callback))) {
-    return NS_ERROR_FAILURE;
-  }
+  GetGMPDecryptorCallback* rawCallback = aCallback.release();
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  RefPtr<GMPCrashHelper> helper(aHelper);
+  GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_DECRYPTOR), *aTags)
+    ->Then(thread, __func__,
+      [rawCallback, helper](RefPtr<GMPContentParent> parent) {
+        UniquePtr<GetGMPDecryptorCallback> callback(rawCallback);
+        GMPDecryptorParent* actor = nullptr;
+        if (parent && NS_SUCCEEDED(parent->GetGMPDecryptor(&actor))) {
+          actor->SetCrashHelper(helper);
+        }
+        callback->Done(actor);
+      },
+      [rawCallback] {
+        UniquePtr<GetGMPDecryptorCallback> callback(rawCallback);
+        callback->Done(nullptr);
+      });
 
   return NS_OK;
 }
 
 void
 GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper)
 {
   if (!aHelper) {
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -16,28 +16,30 @@
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsIDocument.h"
 #include "nsIWeakReference.h"
 #include "mozilla/AbstractThread.h"
 #include "nsClassHashtable.h"
 #include "nsISupportsImpl.h"
+#include "mozilla/MozPromise.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 
 class GMPCrashHelper;
 
 extern LogModule* GetGMPLog();
 
 namespace gmp {
 
-class GetGMPContentParentCallback;
+class GMPContentParent;
+typedef MozPromise<RefPtr<GMPContentParent>, nsresult, /* IsExclusive = */ true> GetGMPContentParentPromise;
 
 class GeckoMediaPluginService : public mozIGeckoMediaPluginService
                               , public nsIObserver
 {
 public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
 
   virtual nsresult Init();
@@ -88,21 +90,22 @@ public:
   void ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper);
   void DisconnectCrashHelper(GMPCrashHelper* aHelper);
 
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
   virtual void InitializePlugins(AbstractThread* aAbstractGMPThread) = 0;
-  virtual bool GetContentParentFrom(GMPCrashHelper* aHelper,
-                                    const nsACString& aNodeId,
-                                    const nsCString& aAPI,
-                                    const nsTArray<nsCString>& aTags,
-                                    UniquePtr<GetGMPContentParentCallback>&& aCallback) = 0;
+
+  virtual RefPtr<GetGMPContentParentPromise>
+  GetContentParent(GMPCrashHelper* aHelper,
+                   const nsACString& aNodeId,
+                   const nsCString& aAPI,
+                   const nsTArray<nsCString>& aTags) = 0;
 
   nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
   nsresult GMPDispatch(already_AddRefed<nsIRunnable> event, uint32_t flags = NS_DISPATCH_NORMAL);
   void ShutdownGMPThread();
 
   Mutex mMutex; // Protects mGMPThread, mAbstractGMPThread, mPluginCrashHelpers,
                 // mGMPThreadShutdown and some members in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -47,95 +47,77 @@ GeckoMediaPluginServiceChild::GetSinglet
     nsCOMPtr<mozIGeckoMediaPluginChromeService> chromeService;
     CallQueryInterface(service.get(), getter_AddRefs(chromeService));
     MOZ_ASSERT(!chromeService);
   }
 #endif
   return service.forget().downcast<GeckoMediaPluginServiceChild>();
 }
 
-class GetContentParentFromDone : public GetServiceChildCallback
-{
-public:
-  GetContentParentFromDone(GMPCrashHelper* aHelper, const nsACString& aNodeId, const nsCString& aAPI,
-                           const nsTArray<nsCString>& aTags,
-                           UniquePtr<GetGMPContentParentCallback>&& aCallback)
-    : mHelper(aHelper),
-      mNodeId(aNodeId),
-      mAPI(aAPI),
-      mTags(aTags),
-      mCallback(Move(aCallback))
-  {
-  }
-
-  void Done(GMPServiceChild* aGMPServiceChild) override
-  {
-    if (!aGMPServiceChild) {
-      mCallback->Done(nullptr);
-      return;
-    }
-
-    uint32_t pluginId;
-    nsresult rv;
-    bool ok = aGMPServiceChild->SendSelectGMP(mNodeId, mAPI, mTags, &pluginId, &rv);
-    if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
-      mCallback->Done(nullptr);
-      return;
-    }
-
-    if (mHelper) {
-      RefPtr<GeckoMediaPluginService> gmps(GeckoMediaPluginService::GetGeckoMediaPluginService());
-      gmps->ConnectCrashHelper(pluginId, mHelper);
-    }
-
-    nsTArray<base::ProcessId> alreadyBridgedTo;
-    aGMPServiceChild->GetAlreadyBridgedTo(alreadyBridgedTo);
-
-    base::ProcessId otherProcess;
-    nsCString displayName;
-    ok = aGMPServiceChild->SendLaunchGMP(pluginId, alreadyBridgedTo, &otherProcess,
-                                         &displayName, &rv);
-    if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
-      mCallback->Done(nullptr);
-      return;
-    }
-
-    RefPtr<GMPContentParent> parent;
-    aGMPServiceChild->GetBridgedGMPContentParent(otherProcess,
-                                                 getter_AddRefs(parent));
-    if (!alreadyBridgedTo.Contains(otherProcess)) {
-      parent->SetDisplayName(displayName);
-      parent->SetPluginId(pluginId);
-    }
-
-    mCallback->Done(parent);
-  }
-
-private:
-  RefPtr<GMPCrashHelper> mHelper;
-  nsCString mNodeId;
-  nsCString mAPI;
-  const nsTArray<nsCString> mTags;
-  UniquePtr<GetGMPContentParentCallback> mCallback;
-};
-
-bool
-GeckoMediaPluginServiceChild::GetContentParentFrom(GMPCrashHelper* aHelper,
-                                                   const nsACString& aNodeId,
-                                                   const nsCString& aAPI,
-                                                   const nsTArray<nsCString>& aTags,
-                                                   UniquePtr<GetGMPContentParentCallback>&& aCallback)
+RefPtr<GetGMPContentParentPromise>
+GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
+                                               const nsACString& aNodeId,
+                                               const nsCString& aAPI,
+                                               const nsTArray<nsCString>& aTags)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
-  UniquePtr<GetServiceChildCallback> callback(
-    new GetContentParentFromDone(aHelper, aNodeId, aAPI, aTags, Move(aCallback)));
-  GetServiceChild(Move(callback));
+  MozPromiseHolder<GetGMPContentParentPromise>* rawHolder = new MozPromiseHolder<GetGMPContentParentPromise>();
+  RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+
+  nsCString nodeId(aNodeId);
+  nsCString api(aAPI);
+  nsTArray<nsCString> tags(aTags);
+  RefPtr<GMPCrashHelper> helper(aHelper);
+  RefPtr<GeckoMediaPluginServiceChild> self(this);
+  GetServiceChild()->Then(thread, __func__,
+    [self, nodeId, api, tags, helper, rawHolder](GMPServiceChild* child) {
+      UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
+      nsresult rv;
+      uint32_t pluginId = 0;
+      bool ok = child->SendSelectGMP(nodeId, api, tags, &pluginId, &rv);
+      if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+        holder->Reject(rv, __func__);
+        return;
+      }
+
+      if (helper) {
+        self->ConnectCrashHelper(pluginId, helper);
+      }
+
+      nsTArray<base::ProcessId> alreadyBridgedTo;
+      child->GetAlreadyBridgedTo(alreadyBridgedTo);
 
-  return true;
+      base::ProcessId otherProcess;
+      nsCString displayName;
+      ok = child->SendLaunchGMP(pluginId,
+                                alreadyBridgedTo,
+                                &otherProcess,
+                                &displayName,
+                                &rv);
+      if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+        holder->Reject(rv, __func__);
+        return;
+      }
+
+      RefPtr<GMPContentParent> parent;
+      child->GetBridgedGMPContentParent(otherProcess, getter_AddRefs(parent));
+      if (!alreadyBridgedTo.Contains(otherProcess)) {
+        parent->SetDisplayName(displayName);
+        parent->SetPluginId(pluginId);
+      }
+      holder->Resolve(parent, __func__);
+    },
+    [rawHolder](nsresult rv) {
+      UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
+      holder->Reject(rv, __func__);
+    });
+
+  return promise;
 }
 
 typedef mozilla::dom::GMPCapabilityData GMPCapabilityData;
 typedef mozilla::dom::GMPAPITags GMPAPITags;
 
 struct GMPCapabilityAndVersion
 {
   explicit GMPCapabilityAndVersion(const GMPCapabilityData& aCapabilities)
@@ -239,66 +221,49 @@ GeckoMediaPluginServiceChild::HasPluginF
       return NS_OK;
     }
   }
 
   *aHasPlugin = false;
   return NS_OK;
 }
 
-class GetNodeIdDone : public GetServiceChildCallback
-{
-public:
-  GetNodeIdDone(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
-                const nsAString& aGMPName,
-                bool aInPrivateBrowsing, UniquePtr<GetNodeIdCallback>&& aCallback)
-    : mOrigin(aOrigin),
-      mTopLevelOrigin(aTopLevelOrigin),
-      mGMPName(aGMPName),
-      mInPrivateBrowsing(aInPrivateBrowsing),
-      mCallback(Move(aCallback))
-  {
-  }
-
-  void Done(GMPServiceChild* aGMPServiceChild) override
-  {
-    if (!aGMPServiceChild) {
-      mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
-      return;
-    }
-
-    nsCString outId;
-    if (!aGMPServiceChild->SendGetGMPNodeId(mOrigin, mTopLevelOrigin,
-                                            mGMPName,
-                                            mInPrivateBrowsing, &outId)) {
-      mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
-      return;
-    }
-
-    mCallback->Done(NS_OK, outId);
-  }
-
-private:
-  nsString mOrigin;
-  nsString mTopLevelOrigin;
-  nsString mGMPName;
-  bool mInPrivateBrowsing;
-  UniquePtr<GetNodeIdCallback> mCallback;
-};
-
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
                                         const nsAString& aTopLevelOrigin,
                                         const nsAString& aGMPName,
                                         bool aInPrivateBrowsing,
                                         UniquePtr<GetNodeIdCallback>&& aCallback)
 {
-  UniquePtr<GetServiceChildCallback> callback(
-    new GetNodeIdDone(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, Move(aCallback)));
-  GetServiceChild(Move(callback));
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  GetNodeIdCallback* rawCallback = aCallback.release();
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  nsString origin(aOrigin);
+  nsString topLevelOrigin(aTopLevelOrigin);
+  nsString gmpName(aGMPName);
+  bool pb = aInPrivateBrowsing;
+  GetServiceChild()->Then(thread, __func__,
+    [rawCallback, origin, topLevelOrigin, gmpName, pb](GMPServiceChild* child) {
+      UniquePtr<GetNodeIdCallback> callback(rawCallback);
+      nsCString outId;
+      if (!child->SendGetGMPNodeId(origin, topLevelOrigin,
+                                   gmpName,
+                                   pb, &outId)) {
+        callback->Done(NS_ERROR_FAILURE, EmptyCString());
+        return;
+      }
+
+      callback->Done(NS_OK, outId);
+    },
+    [rawCallback](nsresult rv) {
+      UniquePtr<GetNodeIdCallback> callback(rawCallback);
+      callback->Done(NS_ERROR_FAILURE, EmptyCString());
+    });
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::Observe(nsISupports* aSubject,
                                       const char* aTopic,
                                       const char16_t* aSomeData)
 {
@@ -311,51 +276,56 @@ GeckoMediaPluginServiceChild::Observe(ns
       mServiceChild = nullptr;
     }
     ShutdownGMPThread();
   }
 
   return NS_OK;
 }
 
-void
-GeckoMediaPluginServiceChild::GetServiceChild(UniquePtr<GetServiceChildCallback>&& aCallback)
+RefPtr<GeckoMediaPluginServiceChild::GetServiceChildPromise>
+GeckoMediaPluginServiceChild::GetServiceChild()
 {
-  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   if (!mServiceChild) {
     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
     if (!contentChild) {
-      return;
+      return GetServiceChildPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
     }
-    mGetServiceChildCallbacks.AppendElement(Move(aCallback));
-    if (mGetServiceChildCallbacks.Length() == 1) {
+    MozPromiseHolder<GetServiceChildPromise>* holder = mGetServiceChildPromises.AppendElement();
+    RefPtr<GetServiceChildPromise> promise = holder->Ensure(__func__);
+    if (mGetServiceChildPromises.Length() == 1) {
         NS_DispatchToMainThread(WrapRunnable(contentChild,
                                              &dom::ContentChild::SendCreateGMPService));
     }
-    return;
+    return promise;
   }
-
-  aCallback->Done(mServiceChild.get());
+  return GetServiceChildPromise::CreateAndResolve(mServiceChild.get(), __func__);
 }
 
 void
 GeckoMediaPluginServiceChild::SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild)
 {
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
   mServiceChild = Move(aServiceChild);
-  nsTArray<UniquePtr<GetServiceChildCallback>> getServiceChildCallbacks;
-  getServiceChildCallbacks.SwapElements(mGetServiceChildCallbacks);
-  for (uint32_t i = 0, length = getServiceChildCallbacks.Length(); i < length; ++i) {
-    getServiceChildCallbacks[i]->Done(mServiceChild.get());
+
+  nsTArray<MozPromiseHolder<GetServiceChildPromise>> holders;
+  holders.SwapElements(mGetServiceChildPromises);
+  for (MozPromiseHolder<GetServiceChildPromise>& holder : holders) {
+    holder.Resolve(mServiceChild.get(), __func__);
   }
 }
 
 void
 GeckoMediaPluginServiceChild::RemoveGMPContentParent(GMPContentParent* aGMPContentParent)
 {
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
   if (mServiceChild) {
     mServiceChild->RemoveGMPContentParent(aGMPContentParent);
   }
 }
 
 GMPServiceChild::GMPServiceChild()
 {
 }
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -14,30 +14,16 @@
 #include "mozilla/dom/ContentChild.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 class GMPServiceChild;
 
-class GetServiceChildCallback
-{
-public:
-  GetServiceChildCallback()
-  {
-    MOZ_COUNT_CTOR(GetServiceChildCallback);
-  }
-  virtual ~GetServiceChildCallback()
-  {
-    MOZ_COUNT_DTOR(GetServiceChildCallback);
-  }
-  virtual void Done(GMPServiceChild* aGMPServiceChild) = 0;
-};
-
 class GeckoMediaPluginServiceChild : public GeckoMediaPluginService
 {
   friend class GMPServiceChild;
 
 public:
   static already_AddRefed<GeckoMediaPluginServiceChild> GetSingleton();
 
   NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
@@ -57,30 +43,31 @@ public:
 
   static void UpdateGMPCapabilities(nsTArray<mozilla::dom::GMPCapabilityData>&& aCapabilities);
 
 protected:
   void InitializePlugins(AbstractThread*) override
   {
     // Nothing to do here.
   }
-  bool GetContentParentFrom(GMPCrashHelper* aHelper,
-                            const nsACString& aNodeId,
-                            const nsCString& aAPI,
-                            const nsTArray<nsCString>& aTags,
-                            UniquePtr<GetGMPContentParentCallback>&& aCallback)
-    override;
+
+  virtual RefPtr<GetGMPContentParentPromise>
+  GetContentParent(GMPCrashHelper* aHelper,
+                   const nsACString& aNodeId,
+                   const nsCString& aAPI,
+                   const nsTArray<nsCString>& aTags) override;
 
 private:
   friend class OpenPGMPServiceChild;
 
-  void GetServiceChild(UniquePtr<GetServiceChildCallback>&& aCallback);
+  typedef MozPromise<GMPServiceChild*, nsresult, /* IsExclusive = */ true> GetServiceChildPromise;
+  RefPtr<GetServiceChildPromise> GetServiceChild();
 
+  nsTArray<MozPromiseHolder<GetServiceChildPromise>> mGetServiceChildPromises;
   UniquePtr<GMPServiceChild> mServiceChild;
-  nsTArray<UniquePtr<GetServiceChildCallback>> mGetServiceChildCallbacks;
 };
 
 class GMPServiceChild : public PGMPServiceChild
 {
 public:
   explicit GMPServiceChild();
   virtual ~GMPServiceChild();
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -524,53 +524,55 @@ GeckoMediaPluginServiceParent::EnsureIni
   if (mLoadPluginsFromDiskComplete) {
     return GenericPromise::CreateAndResolve(true, __func__);
   }
   // We should have an init promise in flight.
   MOZ_ASSERT(!mInitPromise.IsEmpty());
   return mInitPromise.Ensure(__func__);
 }
 
-bool
-GeckoMediaPluginServiceParent::GetContentParentFrom(GMPCrashHelper* aHelper,
-                                                    const nsACString& aNodeId,
-                                                    const nsCString& aAPI,
-                                                    const nsTArray<nsCString>& aTags,
-                                                    UniquePtr<GetGMPContentParentCallback>&& aCallback)
+RefPtr<GetGMPContentParentPromise>
+GeckoMediaPluginServiceParent::GetContentParent(GMPCrashHelper* aHelper,
+                                               const nsACString& aNodeId,
+                                               const nsCString& aAPI,
+                                               const nsTArray<nsCString>& aTags)
 {
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
   if (!thread) {
-    return false;
+    return GetGMPContentParentPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
+  typedef MozPromiseHolder<GetGMPContentParentPromise> PromiseHolder;
+  PromiseHolder* rawHolder = new PromiseHolder();
   RefPtr<GeckoMediaPluginServiceParent> self(this);
+  RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
   nsCString nodeId(aNodeId);
   nsTArray<nsCString> tags(aTags);
   nsCString api(aAPI);
-  GetGMPContentParentCallback* rawCallback = aCallback.release();
   RefPtr<GMPCrashHelper> helper(aHelper);
   EnsureInitialized()->Then(thread, __func__,
-    [self, tags, api, nodeId, rawCallback, helper]() -> void {
-      UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
+    [self, tags, api, nodeId, helper, rawHolder]() -> void {
+      UniquePtr<PromiseHolder> holder(rawHolder);
       RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeId, api, tags);
       LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)self, (void *)gmp, api.get()));
       if (!gmp) {
         NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
-        callback->Done(nullptr);
+        holder->Reject(NS_ERROR_FAILURE, __func__);
         return;
       }
       self->ConnectCrashHelper(gmp->GetPluginId(), helper);
-      gmp->GetGMPContentParent(Move(callback));
+      gmp->GetGMPContentParent(Move(holder));
     },
-    [rawCallback]() -> void {
-      UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
+    [rawHolder]() -> void {
+      UniquePtr<PromiseHolder> holder(rawHolder);
       NS_WARNING("GMPService::EnsureInitialized failed.");
-      callback->Done(nullptr);
+      holder->Reject(NS_ERROR_FAILURE, __func__);
     });
-  return true;
+
+  return promise;
 }
 
 void
 GeckoMediaPluginServiceParent::InitializePlugins(
   AbstractThread* aAbstractGMPThread)
 {
   MOZ_ASSERT(aAbstractGMPThread);
   MonitorAutoLock lock(mInitPromiseMonitor);
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -121,22 +121,23 @@ private:
 
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
   void PluginTerminated(const RefPtr<GMPParent>& aOld);
   void InitializePlugins(AbstractThread* aAbstractGMPThread) override;
   RefPtr<GenericPromise::AllPromiseType> LoadFromEnvironment();
   RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
-  bool GetContentParentFrom(GMPCrashHelper* aHelper,
-                            const nsACString& aNodeId,
-                            const nsCString& aAPI,
-                            const nsTArray<nsCString>& aTags,
-                            UniquePtr<GetGMPContentParentCallback>&& aCallback)
-    override;
+
+  virtual RefPtr<GetGMPContentParentPromise>
+  GetContentParent(GMPCrashHelper* aHelper,
+                   const nsACString& aNodeId,
+                   const nsCString& aAPI,
+                   const nsTArray<nsCString>& aTags) override;
+
 private:
   // Creates a copy of aOriginal. Note that the caller is responsible for
   // adding this to GeckoMediaPluginServiceParent::mPlugins.
   already_AddRefed<GMPParent> ClonePlugin(const GMPParent* aOriginal);
   nsresult EnsurePluginsOnDiskScanned();
   nsresult InitStorage();
 
   class PathRunnable : public Runnable