Bug 1316215 - Promisify GMPService GetGMPContentParent and GetServiceChild. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Wed, 09 Nov 2016 15:52:22 +1300
changeset 443254 bc61628e97243db55b68d1efc93e7b55f32628e5
parent 443004 47f42f21541b9b98ad7db82edb996b29065debd0
child 443255 59aa7bcfe8b8f44274d136d6147a946542a64fff
push id36941
push usercpearce@mozilla.com
push dateThu, 24 Nov 2016 04:18:29 +0000
reviewersgerald
bugs1316215
milestone53.0a1
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