Bug 1057908 - GeckoMediaPluginService needs to be proxied from Content processes to parent process. Part 2 - support asynchronous GMP API getters. r=jwwang,rjesup.
authorPeter Van der Beken <peterv@propagandism.org>
Tue, 10 Feb 2015 11:48:29 +0100
changeset 237293 223de26bbb6f7501290e1184a1e4c49caf4837ab
parent 237292 809bc8418e5f4c11a44e88bf46a09eed039bf55d
child 237294 d778005df07235b5743764029b8fd60f94223636
push id28531
push userryanvm@gmail.com
push dateThu, 02 Apr 2015 20:21:16 +0000
treeherderautoland@513265a4cbc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang, rjesup
bugs1057908
milestone40.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 1057908 - GeckoMediaPluginService needs to be proxied from Content processes to parent process. Part 2 - support asynchronous GMP API getters. r=jwwang,rjesup.
dom/media/eme/CDMProxy.cpp
dom/media/eme/CDMProxy.h
dom/media/fmp4/gmp/GMPAudioDecoder.cpp
dom/media/fmp4/gmp/GMPAudioDecoder.h
dom/media/fmp4/gmp/GMPVideoDecoder.cpp
dom/media/fmp4/gmp/GMPVideoDecoder.h
dom/media/gmp/GMPService.cpp
dom/media/gmp/GMPService.h
dom/media/gmp/mozIGeckoMediaPluginService.idl
dom/media/gtest/TestGMPCrossOrigin.cpp
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -20,16 +20,17 @@
 
 namespace mozilla {
 
 CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
   : mKeys(aKeys)
   , mKeySystem(aKeySystem)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
+  , mShutdownCalled(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(CDMProxy);
 }
 
 CDMProxy::~CDMProxy()
 {
   MOZ_COUNT_DTOR(CDMProxy);
@@ -65,69 +66,107 @@ CDMProxy::Init(PromiseId aPromiseId,
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
   data->mInPrivateBrowsing = aInPrivateBrowsing;
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
                                                      &CDMProxy::gmp_Init,
-                                                     data));
+                                                     Move(data)));
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 #ifdef DEBUG
 bool
 CDMProxy::IsOnGMPThread()
 {
   return NS_GetCurrentThread() == mGMPThread;
 }
 #endif
 
 void
-CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
+CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
+{
+  EME_LOG("CDMProxy::gmp_InitDone");
+  if (!aCDM || mShutdownCalled) {
+    if (aCDM) {
+      aCDM->Close();
+    }
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  mCDM = aCDM;
+  mCallback = new CDMCallbackProxy(this);
+  mCDM->Init(mCallback);
+  nsCOMPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<uint32_t>(this,
+                                          &CDMProxy::OnCDMCreated,
+                                          aData->mPromiseId));
+  NS_DispatchToMainThread(task);
+}
+
+class gmp_InitDoneCallback : public GetGMPDecryptorCallback
+{
+public:
+  gmp_InitDoneCallback(CDMProxy* aCDMProxy,
+                       nsAutoPtr<CDMProxy::InitData>&& aData)
+    : mCDMProxy(aCDMProxy),
+      mData(Move(aData))
+  {
+  }
+
+  void Done(GMPDecryptorProxy* aCDM)
+  {
+    mCDMProxy->gmp_InitDone(aCDM, Move(mData));
+  }
+
+private:
+  nsRefPtr<CDMProxy> mCDMProxy;
+  nsAutoPtr<CDMProxy::InitData> mData;
+};
+
+void
+CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
+  uint32_t promiseID = aData->mPromiseId;
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   nsresult rv = mps->GetNodeId(aData->mOrigin,
                                aData->mTopLevelOrigin,
                                aData->mInPrivateBrowsing,
                                mNodeId);
   MOZ_ASSERT(!GetNodeId().IsEmpty());
   if (NS_FAILED(rv)) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
-  rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
-  if (NS_FAILED(rv) || !mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
-  } else {
-    mCallback = new CDMCallbackProxy(this);
-    mCDM->Init(mCallback);
-    nsCOMPtr<nsIRunnable> task(
-      NS_NewRunnableMethodWithArg<uint32_t>(this,
-                                            &CDMProxy::OnCDMCreated,
-                                            aData->mPromiseId));
-    NS_DispatchToMainThread(task);
+
+  UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
+                                                                       Move(aData)));
+  rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
+  if (NS_FAILED(rv)) {
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
@@ -336,16 +375,18 @@ CDMProxy::Shutdown()
   }
 }
 
 void
 CDMProxy::gmp_Shutdown()
 {
   MOZ_ASSERT(IsOnGMPThread());
 
+  mShutdownCalled = true;
+
   // Abort any pending decrypt jobs, to awaken any clients waiting on a job.
   for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
     DecryptJob* job = mDecryptionJobs[i];
     job->mClient->Decrypted(GMPAbortedErr, nullptr);
   }
   mDecryptionJobs.Clear();
 
   if (mCDM) {
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -169,26 +169,28 @@ public:
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds);
 
 #ifdef DEBUG
   bool IsOnGMPThread();
 #endif
 
 private:
+  friend class gmp_InitDoneCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsAutoString mOrigin;
     nsAutoString mTopLevelOrigin;
     bool mInPrivateBrowsing;
   };
 
   // GMP thread only.
-  void gmp_Init(nsAutoPtr<InitData> aData);
+  void gmp_Init(nsAutoPtr<InitData>&& aData);
+  void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
 
   // GMP thread only.
   void gmp_Shutdown();
 
   // Main thread only.
   void OnCDMCreated(uint32_t aPromiseId);
 
   struct CreateSessionData {
@@ -315,14 +317,18 @@ private:
   nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
 
   // Number of buffers we've decrypted. Used to uniquely identify
   // decryption jobs sent to CDM. Note we can't just use the length of
   // mDecryptionJobs as that shrinks as jobs are completed and removed
   // from it.
   // GMP thread only.
   uint32_t mDecryptionJobCount;
+
+  // True if CDMProxy::gmp_Shutdown was called.
+  // GMP thread only.
+  bool mShutdownCalled;
 };
 
 
 } // namespace mozilla
 
 #endif // CDMProxy_h_
--- a/dom/media/fmp4/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPAudioDecoder.cpp
@@ -128,43 +128,71 @@ GMPAudioDecoder::InitTags(nsTArray<nsCSt
 }
 
 nsCString
 GMPAudioDecoder::GetNodeId()
 {
   return NS_LITERAL_CSTRING("");
 }
 
+void
+GMPAudioDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+
+  nsTArray<nsCString> tags;
+  InitTags(tags);
+  UniquePtr<GetGMPAudioDecoderCallback> callback(
+    new GMPInitDoneCallback(this, aInitDone));
+  if (NS_FAILED(mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), Move(callback)))) {
+    aInitDone->Dispatch();
+  }
+}
+
+void
+GMPAudioDecoder::GMPInitDone(GMPAudioDecoderProxy* aGMP)
+{
+  MOZ_ASSERT(aGMP);
+  nsTArray<uint8_t> codecSpecific;
+  codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
+                               mConfig.audio_specific_config->Length());
+
+  nsresult rv = aGMP->InitDecode(kGMPAudioCodecAAC,
+                                 mConfig.channel_count,
+                                 mConfig.bits_per_sample,
+                                 mConfig.samples_per_second,
+                                 codecSpecific,
+                                 mAdapter);
+  if (NS_SUCCEEDED(rv)) {
+    mGMP = aGMP;
+  }
+}
+
 nsresult
 GMPAudioDecoder::Init()
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
-  nsTArray<nsCString> tags;
-  InitTags(tags);
-  nsresult rv = mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), &mGMP);
-  NS_ENSURE_SUCCESS(rv, rv);
-  MOZ_ASSERT(mGMP);
-
-  nsTArray<uint8_t> codecSpecific;
-  codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
-                               mConfig.audio_specific_config->Length());
+  nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
 
-  rv = mGMP->InitDecode(kGMPAudioCodecAAC,
-                        mConfig.channel_count,
-                        mConfig.bits_per_sample,
-                        mConfig.samples_per_second,
-                        codecSpecific,
-                        mAdapter);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
+  gmpThread->Dispatch(
+    NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
+                                                      &GMPAudioDecoder::GetGMPAPI,
+                                                      initDone),
+    NS_DISPATCH_NORMAL);
 
-  return NS_OK;
+  while (!initDone->IsDone()) {
+    NS_ProcessNextEvent(gmpThread, true);
+  }
+
+  return mGMP ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 GMPAudioDecoder::Input(mp4_demuxer::MP4Sample* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
--- a/dom/media/fmp4/gmp/GMPAudioDecoder.h
+++ b/dom/media/fmp4/gmp/GMPAudioDecoder.h
@@ -75,16 +75,71 @@ public:
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
 
 private:
+  class GMPInitDoneRunnable : public nsRunnable
+  {
+  public:
+    GMPInitDoneRunnable()
+      : mInitDone(false),
+        mThread(do_GetCurrentThread())
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      mInitDone = true;
+      return NS_OK;
+    }
+
+    void Dispatch()
+    {
+      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    bool IsDone()
+    {
+      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
+      return mInitDone;
+    }
+
+  private:
+    bool mInitDone;
+    nsCOMPtr<nsIThread> mThread;
+  };
+  void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
+  class GMPInitDoneCallback : public GetGMPAudioDecoderCallback
+  {
+  public:
+    GMPInitDoneCallback(GMPAudioDecoder* aDecoder,
+                        GMPInitDoneRunnable* aGMPInitDone)
+      : mDecoder(aDecoder)
+      , mGMPInitDone(aGMPInitDone)
+    {
+    }
+
+    virtual void Done(GMPAudioDecoderProxy* aGMP)
+    {
+      if (aGMP) {
+        mDecoder->GMPInitDone(aGMP);
+      }
+      mGMPInitDone->Dispatch();
+    }
+
+  private:
+    nsRefPtr<GMPAudioDecoder> mDecoder;
+    nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
+  };
+  void GMPInitDone(GMPAudioDecoderProxy* aGMP);
+
   const mp4_demuxer::AudioDecoderConfig& mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPAudioDecoderProxy* mGMP;
   nsAutoPtr<AudioCallbackAdapter> mAdapter;
 };
 
 } // namespace mozilla
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
@@ -152,61 +152,91 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer
   frame->SetTimeStamp(aSample->composition_timestamp);
   frame->SetCompleteFrame(true);
   frame->SetDuration(aSample->duration);
   frame->SetFrameType(aSample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
 
   return frame;
 }
 
-nsresult
-GMPVideoDecoder::Init()
+void
+GMPVideoDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
-  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
-  MOZ_ASSERT(mMPS);
-
   nsTArray<nsCString> tags;
   InitTags(tags);
-  nsresult rv = mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), &mHost, &mGMP);
-  NS_ENSURE_SUCCESS(rv, rv);
-  MOZ_ASSERT(mHost && mGMP);
+  UniquePtr<GetGMPVideoDecoderCallback> callback(
+    new GMPInitDoneCallback(this, aInitDone));
+  if (NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), Move(callback)))) {
+    aInitDone->Dispatch();
+  }
+}
 
-  // GMP implementations have interpreted the meaning of GMP_BufferLength32
-  // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
-  // specified in the GMP API, where each buffer is prefixed by a 32-bit
-  // host-endian buffer length that includes the size of the buffer length
-  // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
-  // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
-  // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
-  // and do not include the length of the buffer length field.
-  mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
+void
+GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
+{
+  MOZ_ASSERT(aHost && aGMP);
 
   GMPVideoCodec codec;
   memset(&codec, 0, sizeof(codec));
 
   codec.mGMPApiVersion = kGMPVersion33;
 
   codec.mCodecType = kGMPVideoCodecH264;
   codec.mWidth = mConfig.display_width;
   codec.mHeight = mConfig.display_height;
 
   nsTArray<uint8_t> codecSpecific;
   codecSpecific.AppendElement(0); // mPacketizationMode.
   codecSpecific.AppendElements(mConfig.extra_data->Elements(),
                                mConfig.extra_data->Length());
 
-  rv = mGMP->InitDecode(codec,
-                        codecSpecific,
-                        mAdapter,
-                        PR_GetNumberOfProcessors());
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = aGMP->InitDecode(codec,
+                                 codecSpecific,
+                                 mAdapter,
+                                 PR_GetNumberOfProcessors());
+  if (NS_SUCCEEDED(rv)) {
+    mGMP = aGMP;
+    mHost = aHost;
+
+    // GMP implementations have interpreted the meaning of GMP_BufferLength32
+    // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
+    // specified in the GMP API, where each buffer is prefixed by a 32-bit
+    // host-endian buffer length that includes the size of the buffer length
+    // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
+    // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
+    // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
+    // and do not include the length of the buffer length field.
+    mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
+  }
+}
 
-  return NS_OK;
+nsresult
+GMPVideoDecoder::Init()
+{
+  MOZ_ASSERT(IsOnGMPThread());
+
+  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  MOZ_ASSERT(mMPS);
+
+  nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
+
+  nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
+  gmpThread->Dispatch(
+    NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
+                                                      &GMPVideoDecoder::GetGMPAPI,
+                                                      initDone),
+    NS_DISPATCH_NORMAL);
+
+  while (!initDone->IsDone()) {
+    NS_ProcessNextEvent(gmpThread, true);
+  }
+
+  return mGMP ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 GMPVideoDecoder::Input(mp4_demuxer::MP4Sample* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.h
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.h
@@ -91,16 +91,71 @@ public:
   virtual nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
   virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample);
 
 private:
+  class GMPInitDoneRunnable : public nsRunnable
+  {
+  public:
+    GMPInitDoneRunnable()
+      : mInitDone(false),
+        mThread(do_GetCurrentThread())
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      mInitDone = true;
+      return NS_OK;
+    }
+
+    void Dispatch()
+    {
+      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    bool IsDone()
+    {
+      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
+      return mInitDone;
+    }
+
+  private:
+    bool mInitDone;
+    nsCOMPtr<nsIThread> mThread;
+  };
+  void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
+  class GMPInitDoneCallback : public GetGMPVideoDecoderCallback
+  {
+  public:
+    GMPInitDoneCallback(GMPVideoDecoder* aDecoder,
+                        GMPInitDoneRunnable* aGMPInitDone)
+      : mDecoder(aDecoder)
+      , mGMPInitDone(aGMPInitDone)
+    {
+    }
+
+    virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
+    {
+      if (aGMP) {
+        mDecoder->GMPInitDone(aGMP, aHost);
+      }
+      mGMPInitDone->Dispatch();
+    }
+
+  private:
+    nsRefPtr<GMPVideoDecoder> mDecoder;
+    nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
+  };
+  void GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
+
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPVideoDecoderProxy* mGMP;
   GMPVideoHost* mHost;
   nsAutoPtr<VideoCallbackAdapter> mAdapter;
   bool mConvertNALUnitLengths;
 };
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -276,57 +276,91 @@ GeckoMediaPluginService::GetThread(nsITh
   }
 
   NS_ADDREF(mGMPThread);
   *aThread = mGMPThread;
 
   return NS_OK;
 }
 
+class GetGMPParentForAudioDecoderDone
+{
+public:
+  explicit GetGMPParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  void Done(GMPParent* aGMPParent)
+  {
+    GMPAudioDecoderParent* gmpADP = nullptr;
+    aGMPParent->GetGMPAudioDecoder(&gmpADP);
+    mCallback->Done(gmpADP);
+  }
+
+private:
+  UniquePtr<GetGMPAudioDecoderCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
-                                            GMPAudioDecoderProxy** aGMPAD)
+                                            UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aGMPAD);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
                                                NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
                                                *aTags);
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
-  GMPAudioDecoderParent* gmpADP;
-  nsresult rv = gmp->GetGMPAudioDecoder(&gmpADP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPAD = gmpADP;
+  GetGMPParentForAudioDecoderDone(Move(aCallback)).Done(gmp);
 
   return NS_OK;
 }
 
+class GetGMPParentForVideoDecoderDone
+{
+public:
+  explicit GetGMPParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  void Done(GMPParent* aGMPParent)
+  {
+    GMPVideoDecoderParent* gmpVDP = nullptr;
+    GMPVideoHostImpl* videoHost = nullptr;
+    nsresult rv = aGMPParent->GetGMPVideoDecoder(&gmpVDP);
+    if (NS_SUCCEEDED(rv)) {
+      videoHost = &gmpVDP->Host();
+    }
+    mCallback->Done(gmpVDP, videoHost);
+  }
+
+private:
+  UniquePtr<GetGMPVideoDecoderCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
-                                            GMPVideoHost** aOutVideoHost,
-                                            GMPVideoDecoderProxy** aGMPVD)
+                                            UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutVideoHost);
-  NS_ENSURE_ARG(aGMPVD);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
                                                NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
                                                *aTags);
@@ -334,101 +368,121 @@ GeckoMediaPluginService::GetGMPVideoDeco
   nsCString api = (*aTags)[0];
   LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
 #endif
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
 
-  GMPVideoDecoderParent* gmpVDP;
-  nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPVD = gmpVDP;
-  *aOutVideoHost = &gmpVDP->Host();
+  GetGMPParentForVideoDecoderDone(Move(aCallback)).Done(gmp);
 
   return NS_OK;
 }
 
+class GetGMPParentForVideoEncoderDone
+{
+public:
+  explicit GetGMPParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  void Done(GMPParent* aGMPParent)
+  {
+    GMPVideoEncoderParent* gmpVEP = nullptr;
+    GMPVideoHostImpl* videoHost = nullptr;
+    nsresult rv = aGMPParent->GetGMPVideoEncoder(&gmpVEP);
+    if (NS_SUCCEEDED(rv)) {
+      videoHost = &gmpVEP->Host();
+    }
+    mCallback->Done(gmpVEP, videoHost);
+  }
+
+private:
+  UniquePtr<GetGMPVideoEncoderCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
-                                            GMPVideoHost** aOutVideoHost,
-                                            GMPVideoEncoderProxy** aGMPVE)
+                                            UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutVideoHost);
-  NS_ENSURE_ARG(aGMPVE);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
                                                NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
                                                *aTags);
 #ifdef PR_LOGGING
   nsCString api = (*aTags)[0];
   LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
 #endif
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
-  GMPVideoEncoderParent* gmpVEP;
-  nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPVE = gmpVEP;
-  *aOutVideoHost = &gmpVEP->Host();
+  GetGMPParentForVideoEncoderDone(Move(aCallback)).Done(gmp);
 
   return NS_OK;
 }
 
+class GetGMPParentForDecryptorDone
+{
+public:
+  explicit GetGMPParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  void Done(GMPParent* aGMPParent)
+  {
+    GMPDecryptorParent* ksp = nullptr;
+    aGMPParent->GetGMPDecryptor(&ksp);
+    mCallback->Done(ksp);
+  }
+
+private:
+  UniquePtr<GetGMPDecryptorCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
                                          const nsACString& aNodeId,
-                                         GMPDecryptorProxy** aDecryptor)
+                                         UniquePtr<GetGMPDecryptorCallback>&& aCallback)
 {
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
   if (!SandboxInfo::Get().CanSandboxMedia()) {
     NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
                "EME decryption not available without sandboxing support.");
     return NS_ERROR_NOT_AVAILABLE;
   }
 #endif
 
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aDecryptor);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
                                                NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
                                                *aTags);
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
-  GMPDecryptorParent* ksp;
-  nsresult rv = gmp->GetGMPDecryptor(&ksp);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aDecryptor = static_cast<GMPDecryptorProxy*>(ksp);
+  GetGMPParentForDecryptorDone(Move(aCallback)).Done(gmp);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::HasPluginForAPI(const nsACString& aAPI,
                                          nsTArray<nsCString>* aTags,
                                          bool* aOutHavePlugin)
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -39,28 +39,30 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD GetThread(nsIThread** aThread) override;
   NS_IMETHOD HasPluginForAPI(const nsACString& aAPI, nsTArray<nsCString>* aTags,
                              bool *aRetVal) override;
   NS_IMETHOD GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
-                                GMPVideoHost** aOutVideoHost,
-                                GMPVideoDecoderProxy** aGMPVD) override;
+                                UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+    override;
   NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
-                                GMPVideoHost **aOutVideoHost,
-                                GMPVideoEncoderProxy** aGMPVE) override;
+                                UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
+    override;
   NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
-                                GMPAudioDecoderProxy **aGMPAD) override;
+                                UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
+    override;
   NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags,
                              const nsACString& aNodeId,
-                             GMPDecryptorProxy** aDecryptor) override;
+                             UniquePtr<GetGMPDecryptorCallback>&& aCallback)
+    override;
 
   int32_t AsyncShutdownTimeoutMs();
 
   class PluginCrashCallback
   {
   public:
     NS_INLINE_DECL_REFCOUNTING(PluginCrashCallback)
 
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -2,33 +2,54 @@
 /* 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 "nsISupports.idl"
 #include "nsIThread.idl"
 
 %{C++
+#include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 #include "nsStringGlue.h"
 class GMPAudioDecoderProxy;
 class GMPDecryptorProxy;
 class GMPVideoDecoderProxy;
 class GMPVideoEncoderProxy;
 class GMPVideoHost;
+
+template<class T>
+class GMPGetterCallback
+{
+public:
+  GMPGetterCallback() { MOZ_COUNT_CTOR(GMPGetterCallback<T>); }
+  virtual ~GMPGetterCallback() { MOZ_COUNT_DTOR(GMPGetterCallback<T>); }
+  virtual void Done(T*) = 0;
+};
+template<class T>
+class GMPVideoGetterCallback
+{
+public:
+  GMPVideoGetterCallback() { MOZ_COUNT_CTOR(GMPVideoGetterCallback<T>); }
+  virtual ~GMPVideoGetterCallback() { MOZ_COUNT_DTOR(GMPVideoGetterCallback<T>); }
+  virtual void Done(T*, GMPVideoHost*) = 0;
+};
+typedef GMPGetterCallback<GMPDecryptorProxy> GetGMPDecryptorCallback;
+typedef GMPGetterCallback<GMPAudioDecoderProxy> GetGMPAudioDecoderCallback;
+typedef GMPVideoGetterCallback<GMPVideoDecoderProxy> GetGMPVideoDecoderCallback;
+typedef GMPVideoGetterCallback<GMPVideoEncoderProxy> GetGMPVideoEncoderCallback;
 %}
 
-[ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
-[ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
-[ptr] native GMPVideoHost(GMPVideoHost);
 [ptr] native TagArray(nsTArray<nsCString>);
-[ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
-[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
+native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
+native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
+native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
+native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
 
-[scriptable, uuid(fed4d2d8-87d8-42fe-a141-98c15b5f7a1e)]
+[scriptable, uuid(11bb248b-7a4b-4f62-bebb-8211551d8c20)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
   /**
    * The GMP thread. Callable from any thread.
    */
   readonly attribute nsIThread thread;
 
@@ -47,45 +68,73 @@ interface mozIGeckoMediaPluginService : 
   void getPluginVersionForAPI(in ACString api, in TagArray tags,
                               out boolean hasPlugin, out ACString version);
 
   /**
    * Get a video decoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags such as for EME keysystem.
    * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
    */
   [noscript]
-  GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags,
-                                          [optional] in ACString nodeId,
-                                          out GMPVideoHost outVideoHost);
+  void getGMPVideoDecoder(in TagArray tags,
+                          [optional] in ACString nodeId,
+                          in GetGMPVideoDecoderCallback callback);
 
   /**
    * Get a video encoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags.
    * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
    */
   [noscript]
-  GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags,
-		                                      [optional] in ACString nodeId,
-		                                      out GMPVideoHost outVideoHost);
+  void getGMPVideoEncoder(in TagArray tags,
+		                      [optional] in ACString nodeId,
+		                      in GetGMPVideoEncoderCallback callback);
 
-  // Returns an audio decoder that supports the specified tags.
-  // The array of tags should at least contain a codec tag, and optionally
-  // other tags such as for EME keysystem.
-  // Callable only on GMP thread.
-  GMPAudioDecoderProxy getGMPAudioDecoder(in TagArray tags,
-                                          [optional] in ACString nodeId);
+  /**
+   * Returns an audio decoder that supports the specified tags.
+   * The array of tags should at least contain a codec tag, and optionally
+   * other tags such as for EME keysystem.
+   * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
+   */
+  [noscript]
+  void getGMPAudioDecoder(in TagArray tags,
+                          [optional] in ACString nodeId,
+                          in GetGMPAudioDecoderCallback callback);
 
-  // Returns a decryption session manager that supports the specified tags.
-  // The array of tags should at least contain a key system tag, and optionally
-  // other tags.
-  // Callable only on GMP thread.
-  GMPDecryptorProxy getGMPDecryptor(in TagArray tags, in ACString nodeId);
+  /**
+   * Returns a decryption session manager that supports the specified tags.
+   * The array of tags should at least contain a key system tag, and optionally
+   * other tags.
+   * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
+   */
+  [noscript]
+  void getGMPDecryptor(in TagArray tags, in ACString nodeId,
+                       in GetGMPDecryptorCallback callback);
 
   /**
    * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
    */
   ACString getNodeId(in AString origin,
                      in AString topLevelOrigin,
                      in bool inPrivateBrowsingMode);
 };
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -23,97 +23,244 @@
 #include "mozilla/WindowsVersion.h"
 #endif
 
 using namespace std;
 
 using namespace mozilla;
 using namespace mozilla::gmp;
 
+class GMPTestMonitor
+{
+public:
+  GMPTestMonitor()
+    : mFinished(false)
+  {
+  }
+
+  void AwaitFinished()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    while (!mFinished) {
+      NS_ProcessNextEvent(nullptr, true);
+    }
+    mFinished = false;
+  }
+
+private:
+  void MarkFinished()
+  {
+    mFinished = true;
+  }
+
+public:
+  void SetFinished()
+  {
+    NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this,
+                                                          &GMPTestMonitor::MarkFinished));
+  }
+
+private:
+  bool mFinished;
+};
+
 struct GMPTestRunner
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPTestRunner)
 
-  void DoTest(void (GMPTestRunner::*aTestMethod)());
-  void RunTestGMPTestCodec();
-  void RunTestGMPCrossOrigin();
+  void DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&));
+  void RunTestGMPTestCodec1(GMPTestMonitor& aMonitor);
+  void RunTestGMPTestCodec2(GMPTestMonitor& aMonitor);
+  void RunTestGMPTestCodec3(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor);
 
 private:
   ~GMPTestRunner() { }
 };
 
-void
-GMPTestRunner::RunTestGMPTestCodec()
+template<class T, class Base,
+         nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(nsTArray<nsCString>*,
+                                                                const nsACString&,
+                                                                UniquePtr<Base>&&)>
+class RunTestGMPVideoCodec : public Base
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+public:
+  virtual void Done(T* aGMP, GMPVideoHost* aHost)
+  {
+    EXPECT_TRUE(aGMP);
+    EXPECT_TRUE(aHost);
+    if (aGMP) {
+      aGMP->Close();
+    }
+    mMonitor.SetFinished();
+  }
 
-  GMPVideoHost* host = nullptr;
-  GMPVideoDecoderProxy* decoder = nullptr;
-  GMPVideoDecoderProxy* decoder2 = nullptr;
-  GMPVideoEncoderProxy* encoder = nullptr;
+  static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin)
+  {
+    UniquePtr<GMPCallbackType> callback(new RunTestGMPVideoCodec(aMonitor));
+    Get(aOrigin, Move(callback));
+  }
 
-  nsTArray<nsCString> tags;
-  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+protected:
+  typedef T GMPCodecType;
+  typedef Base GMPCallbackType;
+
+  explicit RunTestGMPVideoCodec(GMPTestMonitor& aMonitor)
+    : mMonitor(aMonitor)
+  {
+  }
 
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("o"), &host, &decoder2);
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING(""), &host, &decoder);
+  static nsresult Get(const nsACString& aNodeId, UniquePtr<Base>&& aCallback)
+  {
+    nsTArray<nsCString> tags;
+    tags.AppendElement(NS_LITERAL_CSTRING("h264"));
 
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING(""), &host, &encoder);
+    nsRefPtr<GeckoMediaPluginService> service =
+      GeckoMediaPluginService::GetGeckoMediaPluginService();
+    return ((*service).*Getter)(&tags, aNodeId, Move(aCallback));
+  }
+
+protected:
+  GMPTestMonitor& mMonitor;
+};
 
-  EXPECT_TRUE(host);
-  EXPECT_TRUE(decoder);
-  EXPECT_TRUE(decoder2);
-  EXPECT_TRUE(encoder);
+typedef RunTestGMPVideoCodec<GMPVideoDecoderProxy,
+                             GetGMPVideoDecoderCallback,
+                             &GeckoMediaPluginService::GetGMPVideoDecoder>
+  RunTestGMPVideoDecoder;
+typedef RunTestGMPVideoCodec<GMPVideoEncoderProxy,
+                             GetGMPVideoEncoderCallback,
+                             &GeckoMediaPluginService::GetGMPVideoEncoder>
+  RunTestGMPVideoEncoder;
 
-  if (decoder) decoder->Close();
-  if (decoder2) decoder2->Close();
-  if (encoder) encoder->Close();
+void
+GMPTestRunner::RunTestGMPTestCodec1(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING("o"));
+}
+
+void
+GMPTestRunner::RunTestGMPTestCodec2(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
 }
 
 void
-GMPTestRunner::RunTestGMPCrossOrigin()
+GMPTestRunner::RunTestGMPTestCodec3(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoEncoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
+}
+
+template<class Base>
+class RunTestGMPCrossOrigin : public Base
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+public:
+  virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
+  {
+    EXPECT_TRUE(aGMP);
 
-  GMPVideoHost* host = nullptr;
-  nsTArray<nsCString> tags;
-  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+    UniquePtr<typename Base::GMPCallbackType> callback(
+      new Step2(Base::mMonitor, aGMP, mShouldBeEqual));
+    nsresult rv = Base::Get(mOrigin2, Move(callback));
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    if (NS_FAILED(rv)) {
+      Base::mMonitor.SetFinished();
+    }
+  }
 
-  GMPVideoDecoderProxy* decoder1 = nullptr;
-  GMPVideoDecoderProxy* decoder2 = nullptr;
-  GMPVideoEncoderProxy* encoder1 = nullptr;
-  GMPVideoEncoderProxy* encoder2 = nullptr;
+  static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
+                  const nsCString& aOrigin2)
+  {
+    UniquePtr<typename Base::GMPCallbackType> callback(
+      new RunTestGMPCrossOrigin<Base>(aMonitor, aOrigin1, aOrigin2));
+    nsresult rv = Base::Get(aOrigin1, Move(callback));
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    if (NS_FAILED(rv)) {
+      aMonitor.SetFinished();
+    }
+  }
 
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder1);
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &decoder2);
-  EXPECT_TRUE(!!decoder1 && !!decoder2 &&
-              decoder1->ParentID() != decoder2->ParentID());
+private:
+  RunTestGMPCrossOrigin(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
+                        const nsCString& aOrigin2)
+    : Base(aMonitor),
+      mGMP(nullptr),
+      mOrigin2(aOrigin2),
+      mShouldBeEqual(aOrigin1.Equals(aOrigin2))
+  {
+  }
 
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder1);
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &encoder2);
-  EXPECT_TRUE(!!encoder1 && !!encoder2 &&
-              encoder1->ParentID() != encoder2->ParentID());
+  class Step2 : public Base
+  {
+  public:
+    Step2(GMPTestMonitor& aMonitor,
+          typename Base::GMPCodecType* aGMP,
+          bool aShouldBeEqual)
+      : Base(aMonitor),
+        mGMP(aGMP),
+        mShouldBeEqual(aShouldBeEqual)
+    {
+    }
+    virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
+    {
+      EXPECT_TRUE(aGMP);
+      if (aGMP) {
+        EXPECT_TRUE(mGMP &&
+                    (mGMP->ParentID() == aGMP->ParentID()) == mShouldBeEqual);
+      }
+      if (mGMP) {
+        mGMP->Close();
+      }
+      Base::Done(aGMP, aHost);
+    }
 
-  if (decoder2) decoder2->Close();
-  if (encoder2) encoder2->Close();
+  private:
+    typename Base::GMPCodecType* mGMP;
+    bool mShouldBeEqual;
+  };
 
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder2);
-  EXPECT_TRUE(!!decoder1 && !!decoder2 &&
-              decoder1->ParentID() == decoder2->ParentID());
+  typename Base::GMPCodecType* mGMP;
+  nsCString mOrigin2;
+  bool mShouldBeEqual;
+};
+
+typedef RunTestGMPCrossOrigin<RunTestGMPVideoDecoder>
+  RunTestGMPVideoDecoderCrossOrigin;
+typedef RunTestGMPCrossOrigin<RunTestGMPVideoEncoder>
+  RunTestGMPVideoEncoderCrossOrigin;
+
+void
+GMPTestRunner::RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
+}
 
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder2);
-  EXPECT_TRUE(!!encoder1 && !!encoder2 &&
-              encoder1->ParentID() == encoder2->ParentID());
+void
+GMPTestRunner::RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoEncoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
+}
 
-  if (decoder1) decoder1->Close();
-  if (decoder2) decoder2->Close();
-  if (encoder1) encoder1->Close();
-  if (encoder2) encoder2->Close();
+void
+GMPTestRunner::RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
+}
+
+void
+GMPTestRunner::RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoEncoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
 }
 
 static already_AddRefed<nsIThread>
 GetGMPThread()
 {
   nsRefPtr<GeckoMediaPluginService> service =
     GeckoMediaPluginService::GetGeckoMediaPluginService();
   nsCOMPtr<nsIThread> thread;
@@ -433,90 +580,152 @@ class GMPStorageTest : public GMPDecrypt
     const nsString origin1 = NS_LITERAL_STRING("example1.com");
     const nsString origin2 = NS_LITERAL_STRING("example2.org");
     nsCString nodeId3 = GetNodeId(origin1, origin2, false);
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
+  class CreateDecryptorDone : public GetGMPDecryptorCallback
+  {
+  public:
+    CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
+      : mRunner(aRunner),
+        mContinuation(aContinuation)
+    {
+    }
+
+    virtual void Done(GMPDecryptorProxy* aDecryptor) override
+    {
+      mRunner->mDecryptor = aDecryptor;
+      EXPECT_TRUE(!!mRunner->mDecryptor);
+
+      if (mRunner->mDecryptor) {
+        mRunner->mDecryptor->Init(mRunner);
+      }
+      nsCOMPtr<nsIThread> thread(GetGMPThread());
+      thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
+    }
+
+  private:
+    nsRefPtr<GMPStorageTest> mRunner;
+    nsCOMPtr<nsIRunnable> mContinuation;
+  };
+
   void CreateDecryptor(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
-                       bool aInPBMode) {
+                       bool aInPBMode,
+                       const nsCString& aUpdate)
+  {
+    nsTArray<nsCString> updates;
+    updates.AppendElement(aUpdate);
+    CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, Move(updates));
+  }
+  class Updates : public nsRunnable
+  {
+  public:
+    Updates(GMPStorageTest* aRunner, nsTArray<nsCString>&& aUpdates)
+      : mRunner(aRunner),
+        mUpdates(aUpdates)
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      for (auto& update : mUpdates) {
+        mRunner->Update(update);
+      }
+      return NS_OK;
+    }
+
+  private:
+    nsRefPtr<GMPStorageTest> mRunner;
+    nsTArray<nsCString> mUpdates;
+  };
+  void CreateDecryptor(const nsAString& aOrigin,
+                       const nsAString& aTopLevelOrigin,
+                       bool aInPBMode,
+                       nsTArray<nsCString>&& aUpdates) {
+    nsCOMPtr<nsIRunnable> updates(new Updates(this, Move(aUpdates)));
+    CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, updates);
+  }
+  void CreateDecryptor(const nsAString& aOrigin,
+                       const nsAString& aTopLevelOrigin,
+                       bool aInPBMode,
+                       nsIRunnable* aContinuation) {
     nsRefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
     EXPECT_TRUE(service);
 
     mNodeId = GetNodeId(aOrigin, aTopLevelOrigin, aInPBMode);
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
-    nsresult rv = service->GetGMPDecryptor(&tags, mNodeId, &mDecryptor);
+    UniquePtr<GetGMPDecryptorCallback> callback(
+      new CreateDecryptorDone(this, aContinuation));
+    nsresult rv =
+      service->GetGMPDecryptor(&tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    EXPECT_TRUE(!!mDecryptor);
-
-    if (mDecryptor) {
-      mDecryptor->Init(this);
-    }
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     nsRefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     // Send a message to the fake GMP for it to run its own tests internally.
     // It sends us a "test-storage complete" message when its passed, or
     // some other message if its tests fail.
     Expect(NS_LITERAL_CSTRING("test-storage complete"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("test-storage"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate storage data for some sites.
    * 2. Forget about one of the sites.
    * 3. Check if the storage data for the forgotten site are erased correctly.
    * 4. Check if the storage data for other sites remain unchanged.
    */
   void TestForgetThisSite() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   void TestForgetThisSite_AnotherSite() {
     Shutdown();
 
     // Generate storage data for another site.
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
+                    NS_LITERAL_STRING("example4.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   struct NodeInfo {
     explicit NodeInfo(const nsACString& aSite) : siteToForget(aSite) {}
     nsCString siteToForget;
     nsTArray<nsCString> expectedRemainingNodeIds;
   };
 
@@ -618,71 +827,68 @@ class GMPStorageTest : public GMPDecrypt
    * 4. Check if all directories in $profileDir/gmp/id/ and
    *    $profileDir/gmp/storage are removed.
    */
   void TestClearRecentHistory1() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory1_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
 
-  }
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
+}
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t| to clear recent history.
    * 4. Check if all directories in $profileDir/gmp/id/ and
    *    $profileDir/gmp/storage are removed.
    */
   void TestClearRecentHistory2() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory2_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
 
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t+1| to clear recent history.
    * 4. Check if all directories in $profileDir/gmp/id/ and
    *    $profileDir/gmp/storage remain unchanged.
    */
   void TestClearRecentHistory3() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory3_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
 
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   class MaxMTimeFinder {
   public:
     MaxMTimeFinder() : mMaxTime(0) {}
     void operator()(nsIFile* aFile) {
       PRTime lastModified;
       nsresult rv = aFile->GetLastModifiedTime(&lastModified);
@@ -770,228 +976,251 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_EQ(c2.GetCount(), 1);
 
     SetFinished();
   }
 
   void TestCrossOriginStorage() {
     EXPECT_TRUE(!mDecryptor);
 
-    // Open decryptor on one, origin, write a record, and test that that
-    // record can't be read on another origin.
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
-                    false);
-
     // Send the decryptor the message "store recordid $time"
     // Wait for the decrytor to send us "stored recordid $time"
     auto t = time(0);
     nsCString response("stored crossOriginTestRecordId ");
     response.AppendInt((int64_t)t);
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestCrossOriginStorage_RecordStoredContinuation));
 
     nsCString update("store crossOriginTestRecordId ");
     update.AppendInt((int64_t)t);
-    Update(update);
+
+    // Open decryptor on one, origin, write a record, and test that that
+    // record can't be read on another origin.
+    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
+                    NS_LITERAL_STRING("example4.com"),
+                    false,
+                    update);
   }
 
   void TestCrossOriginStorage_RecordStoredContinuation() {
     // Close the old decryptor, and create a new one on a different origin,
     // and try to read the record.
     Shutdown();
 
+    Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+
     CreateDecryptor(NS_LITERAL_STRING("example5.com"),
                     NS_LITERAL_STRING("example6.com"),
-                    false);
-
-    Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
-           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
+                    false,
+                    NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
   }
 
   void TestPBStorage() {
-    // Open decryptor on one, origin, write a record, close decryptor,
-    // open another, and test that record can be read, close decryptor,
-    // then send pb-last-context-closed notification, then open decryptor
-    // and check that it can't read that data; it should have been purged.
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
-                    true);
-
     // Send the decryptor the message "store recordid $time"
     // Wait for the decrytor to send us "stored recordid $time"
     nsCString response("stored pbdata test-pb-data");
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestPBStorage_RecordStoredContinuation));
 
-    nsCString update("store pbdata test-pb-data");
-    Update(update);
+    // Open decryptor on one, origin, write a record, close decryptor,
+    // open another, and test that record can be read, close decryptor,
+    // then send pb-last-context-closed notification, then open decryptor
+    // and check that it can't read that data; it should have been purged.
+    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
+                    NS_LITERAL_STRING("pb2.com"),
+                    true,
+                    NS_LITERAL_CSTRING("store pbdata test-pb-data"));
   }
 
   void TestPBStorage_RecordStoredContinuation() {
     Shutdown();
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
-                    true);
-
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
-    Update(NS_LITERAL_CSTRING("retrieve pbdata"));
+
+    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
+                    NS_LITERAL_STRING("pb2.com"),
+                    true,
+                    NS_LITERAL_CSTRING("retrieve pbdata"));
   }
 
   void TestPBStorage_RecordRetrievedContinuation() {
     Shutdown();
     SimulatePBModeExit();
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
-                    true);
-
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve pbdata"));
+
+    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
+                    NS_LITERAL_STRING("pb2.com"),
+                    true,
+                    NS_LITERAL_CSTRING("retrieve pbdata"));
+  }
+
+  void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
+  {
+    if (mDecryptor) {
+      Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
+      Shutdown();
+    }
+    nsCOMPtr<nsIThread> thread(GetGMPThread());
+    thread->Dispatch(aContinuation, NS_DISPATCH_NORMAL);
   }
 
   void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
-                                     const nsAString& aOrigin2) {
-    CreateDecryptor(aOrigin1, aOrigin2, false);
-    Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
-    Shutdown();
+                                     const nsAString& aOrigin2,
+                                     void (GMPStorageTest::*aCallback)()) {
+    nsCOMPtr<nsIRunnable> continuation(
+      NS_NewRunnableMethodWithArg<nsCOMPtr<nsIRunnable>>(
+        this,
+        &GMPStorageTest::NextAsyncShutdownTimeoutTest,
+        NS_NewRunnableMethod(this, aCallback)));
+
+    CreateDecryptor(aOrigin1, aOrigin2, false, continuation);
   }
 
   void TestAsyncShutdownTimeout() {
     // Create decryptors that timeout in their async shutdown.
     // If the gtest hangs on shutdown, test fails!
     CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
-                                  NS_LITERAL_STRING("example8.com"));
+                                  NS_LITERAL_STRING("example8.com"),
+                                  &GMPStorageTest::TestAsyncShutdownTimeout2);
+  };
+
+  void TestAsyncShutdownTimeout2() {
     CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
-                                  NS_LITERAL_STRING("example10.com"));
+                                  NS_LITERAL_STRING("example10.com"),
+                                  &GMPStorageTest::TestAsyncShutdownTimeout3);
+  };
+
+  void TestAsyncShutdownTimeout3() {
     CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
-                                  NS_LITERAL_STRING("example12.com"));
-    SetFinished();
+                                  NS_LITERAL_STRING("example12.com"),
+                                  &GMPStorageTest::SetFinished);
   };
 
   void TestAsyncShutdownStorage() {
-    // Test that a GMP can write to storage during shutdown, and retrieve
-    // that written data in a subsequent session.
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
-                    false);
-
     // Instruct the GMP to write a token (the current timestamp, so it's
     // unique) during async shutdown, then shutdown the plugin, re-create
     // it, and check that the token was successfully stored.
     auto t = time(0);
     nsCString update("shutdown-mode token ");
     nsCString token;
     token.AppendInt((int64_t)t);
     update.Append(token);
 
     // Wait for a response from the GMP, so we know it's had time to receive
     // the token.
     nsCString response("shutdown-token received ");
     response.Append(token);
     Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
 
-    Update(update);
+    // Test that a GMP can write to storage during shutdown, and retrieve
+    // that written data in a subsequent session.
+    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
+                    NS_LITERAL_STRING("example14.com"),
+                    false,
+                    update);
   }
 
   void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
     ShutdownThen(NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
   }
 
   void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
     // Create a new instance of the plugin, retrieve the token written
     // during shutdown and verify it is correct.
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
-                    false);
     nsCString response("retrieved shutdown-token ");
     response.Append(aToken);
     Expect(response,
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve-shutdown-token"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
+                    NS_LITERAL_STRING("example14.com"),
+                    false,
+                    NS_LITERAL_CSTRING("retrieve-shutdown-token"));
   }
 
 #if defined(XP_WIN)
   void TestOutputProtection() {
     Shutdown();
 
+    Expect(NS_LITERAL_CSTRING("OP tests completed"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+
     CreateDecryptor(NS_LITERAL_STRING("example15.com"),
                     NS_LITERAL_STRING("example16.com"),
-                    false);
-
-    Expect(NS_LITERAL_CSTRING("OP tests completed"),
-           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("test-op-apis"));
+                    false,
+                    NS_LITERAL_CSTRING("test-op-apis"));
   }
 #endif
 
   void TestPluginVoucher() {
+    Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+
     CreateDecryptor(NS_LITERAL_STRING("example17.com"),
                     NS_LITERAL_STRING("example18.com"),
-                    false);
-    Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
-           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
+                    false,
+                    NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
   }
 
   void TestGetRecordNamesInMemoryStorage() {
     TestGetRecordNames(true);
   }
 
   nsCString mRecordNames;
 
   void AppendIntPadded(nsACString& aString, uint32_t aInt) {
     if (aInt > 0 && aInt < 10) {
       aString.AppendLiteral("0");
     }
     aString.AppendInt(aInt);
   }
 
   void TestGetRecordNames(bool aPrivateBrowsing) {
-    CreateDecryptor(NS_LITERAL_STRING("foo.com"),
-                    NS_LITERAL_STRING("bar.com"),
-                    aPrivateBrowsing);
-
     // Create a number of records of different names.
     const uint32_t num = 100;
+    nsTArray<nsCString> updates(num);
     for (uint32_t i = 0; i < num; i++) {
       nsAutoCString response;
       response.AppendLiteral("stored data");
       AppendIntPadded(response, i);
       response.AppendLiteral(" test-data");
       AppendIntPadded(response, i);
 
       if (i != 0) {
         mRecordNames.AppendLiteral(",");
       }
       mRecordNames.AppendLiteral("data");
       AppendIntPadded(mRecordNames, i);
 
-      nsAutoCString update;
+      nsCString& update = *updates.AppendElement();
       update.AppendLiteral("store data");
       AppendIntPadded(update, i);
       update.AppendLiteral(" test-data");
       AppendIntPadded(update, i);
 
       nsIRunnable* continuation = nullptr;
       if (i + 1 == num) {
         continuation =
           NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames);
       }
       Expect(response, continuation);
-      Update(update);
     }
+
+    CreateDecryptor(NS_LITERAL_STRING("foo.com"),
+                    NS_LITERAL_STRING("bar.com"),
+                    aPrivateBrowsing,
+                    Move(updates));
   }
 
   void TestGetRecordNames_QueryNames() {
     nsCString response("record-names ");
     response.Append(mRecordNames);
     Expect(response,
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
     Update(NS_LITERAL_CSTRING("retrieve-record-names"));
@@ -1021,31 +1250,30 @@ class GMPStorageTest : public GMPDecrypt
       "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
       "long_record_name");
 
     NS_NAMED_LITERAL_CSTRING(data, "Just_some_arbitrary_data.");
 
     MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
     MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
 
-    CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
-                    NS_LITERAL_STRING("baz.com"),
-                    false);
-
     nsCString response("stored ");
     response.Append(longRecordName);
     response.AppendLiteral(" ");
     response.Append(data);
     Expect(response, NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
     nsCString update("store ");
     update.Append(longRecordName);
     update.AppendLiteral(" ");
     update.Append(data);
-    Update(update);
+    CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
+                    NS_LITERAL_STRING("baz.com"),
+                    false,
+                    update);
   }
 
   void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
     mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
   }
 
   void AwaitFinished() {
     while (!mFinished) {
@@ -1142,34 +1370,41 @@ private:
 
   GMPDecryptorProxy* mDecryptor;
   Monitor mMonitor;
   Atomic<bool> mFinished;
   nsCString mNodeId;
 };
 
 void
-GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)())
+GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
-  nsCOMPtr<nsIThread> thread;
+  nsCOMPtr<nsIThread> thread(GetGMPThread());
 
-  service->GetThread(getter_AddRefs(thread));
-  thread->Dispatch(NS_NewRunnableMethod(this, aTestMethod), NS_DISPATCH_SYNC);
+  GMPTestMonitor monitor;
+  thread->Dispatch(NS_NewRunnableMethodWithArg<GMPTestMonitor&>(this,
+                                                                aTestMethod,
+                                                                monitor),
+                   NS_DISPATCH_NORMAL);
+  monitor.AwaitFinished();
 }
 
 TEST(GeckoMediaPlugins, GMPTestCodec) {
   nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
-  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec);
+  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec1);
+  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec2);
+  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec3);
 }
 
 TEST(GeckoMediaPlugins, GMPCrossOrigin) {
   nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
-  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin1);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin2);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin3);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin4);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageGetNodeId) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestGetNodeId);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageBasic) {
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
@@ -146,71 +146,98 @@ WebrtcGmpVideoEncoder::InitEncode(const 
   MOZ_ASSERT(mMPS);
 
   if (!mGMPThread) {
     if (NS_WARN_IF(NS_FAILED(mMPS->GetThread(getter_AddRefs(mGMPThread))))) {
       return WEBRTC_VIDEO_CODEC_ERROR;
     }
   }
 
-  int32_t ret;
-  mGMPThread->Dispatch(WrapRunnableRet(this,
-                                       &WebrtcGmpVideoEncoder::InitEncode_g,
-                                       aCodecSettings,
-                                       aNumberOfCores,
-                                       aMaxPayloadSize,
-                                       &ret),
-                       NS_DISPATCH_SYNC);
+  nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
+  MOZ_ASSERT(currentThread != mGMPThread);
 
-  return ret;
+  nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
+  mGMPThread->Dispatch(WrapRunnable(this,
+                                    &WebrtcGmpVideoEncoder::InitEncode_g,
+                                    aCodecSettings,
+                                    aNumberOfCores,
+                                    aMaxPayloadSize,
+                                    initDone),
+                       NS_DISPATCH_NORMAL);
+
+
+  while (!initDone->IsDone()) {
+    NS_ProcessNextEvent(currentThread, true);
+  }
+
+  return initDone->Result();
 }
 
-int32_t
+void
 WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
                                     int32_t aNumberOfCores,
-                                    uint32_t aMaxPayloadSize)
+                                    uint32_t aMaxPayloadSize,
+                                    InitDoneRunnable* aInitDone)
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
-  if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
-                                                    NS_LITERAL_CSTRING(""),
-                                                    &mHost,
-                                                    &mGMP)))) {
+  UniquePtr<GetGMPVideoEncoderCallback> callback(
+    new InitDoneCallback(this, aInitDone, aCodecSettings, aMaxPayloadSize));
+  nsresult rv = mMPS->GetGMPVideoEncoder(&tags,
+                                         NS_LITERAL_CSTRING(""),
+                                         Move(callback));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     mMPS = nullptr;
     mGMP = nullptr;
     mGMPThread = nullptr;
     mHost = nullptr;
-    return WEBRTC_VIDEO_CODEC_ERROR;
+    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
+    return;
   }
+}
 
+int32_t
+WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
+                                   GMPVideoHost* aHost,
+                                   const webrtc::VideoCodec* aCodecSettings,
+                                   uint32_t aMaxPayloadSize)
+{
+  mGMP = aGMP;
+  mHost = aHost;
   if (!mGMP || !mHost) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
   // Bug XXXXXX: transfer settings from codecSettings to codec.
   memset(&mCodecParams, 0, sizeof(mCodecParams));
 
   mCodecParams.mGMPApiVersion = 33;
-  mCodecParams.mWidth = aCodecSettings->width;
-  mCodecParams.mHeight = aCodecSettings->height;
   mCodecParams.mStartBitrate = aCodecSettings->startBitrate;
   mCodecParams.mMinBitrate = aCodecSettings->minBitrate;
   mCodecParams.mMaxBitrate = aCodecSettings->maxBitrate;
   mCodecParams.mMaxFramerate = aCodecSettings->maxFramerate;
   mMaxPayloadSize = aMaxPayloadSize;
   if (aCodecSettings->codecSpecific.H264.packetizationMode == 1) {
     mMaxPayloadSize = 0; // No limit.
   }
 
   if (aCodecSettings->mode == webrtc::kScreensharing) {
     mCodecParams.mMode = kGMPScreensharing;
   } else {
     mCodecParams.mMode = kGMPRealtimeVideo;
   }
 
+  return InitEncoderForSize(aCodecSettings->width, aCodecSettings->height);
+}
+
+int32_t
+WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth, unsigned short aHeight)
+{
+  mCodecParams.mWidth = aWidth;
+  mCodecParams.mHeight = aHeight;
   // Pass dummy codecSpecific data for now...
   nsTArray<uint8_t> codecSpecific;
 
   GMPErr err = mGMP->InitEncode(mCodecParams, codecSpecific, this, 1, mMaxPayloadSize);
   if (err != GMPNoErr) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
@@ -218,72 +245,90 @@ WebrtcGmpVideoEncoder::InitEncode_g(cons
 }
 
 
 int32_t
 WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
                               const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                               const std::vector<webrtc::VideoFrameType>* aFrameTypes)
 {
+  MOZ_ASSERT(mHost);
+  if (!mGMP) {
+    // destroyed via Terminate()
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  MOZ_ASSERT(aInputImage.width() >= 0 && aInputImage.height() >= 0);
+  if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
+      static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
+    LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
+          mCodecParams.mWidth, mCodecParams.mHeight, aInputImage.width(), aInputImage.height()));
+
+    nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
+    nsCOMPtr<nsIRunnable> task(
+      WrapRunnable(this,
+                   &WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange,
+                   &aInputImage,
+                   initDone));
+    mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+
+    nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
+    while (!initDone->IsDone()) {
+      NS_ProcessNextEvent(currentThread, true);
+    }
+
+    if (initDone->Result() != WEBRTC_VIDEO_CODEC_OK) {
+      return initDone->Result();
+    }
+  }
+
   int32_t ret;
-  MOZ_ASSERT(mGMPThread);
   mozilla::SyncRunnable::DispatchToThread(mGMPThread,
                 WrapRunnableRet(this,
                                 &WebrtcGmpVideoEncoder::Encode_g,
                                 &aInputImage,
                                 aCodecSpecificInfo,
                                 aFrameTypes,
                                 &ret));
 
   return ret;
 }
 
+void
+WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
+                                                       InitDoneRunnable* aInitDone)
+{
+  mGMP->Close();
+
+  UniquePtr<GetGMPVideoEncoderCallback> callback(
+    new InitDoneForResolutionChangeCallback(this, aInitDone,
+                                            aInputImage->width(),
+                                            aInputImage->height()));
+
+  // OpenH264 codec (at least) can't handle dynamic input resolution changes
+  // re-init the plugin when the resolution changes
+  // XXX allow codec to indicate it doesn't need re-init!
+  nsTArray<nsCString> tags;
+  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+  if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
+                                                    NS_LITERAL_CSTRING(""),
+                                                    Move(callback))))) {
+    mGMP = nullptr;
+    mHost = nullptr;
+    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
+  }
+}
+
 int32_t
 WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
                                 const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                                 const std::vector<webrtc::VideoFrameType>* aFrameTypes)
 {
   MOZ_ASSERT(mHost);
-  if (!mGMP) {
-    // destroyed via Terminate()
-    return WEBRTC_VIDEO_CODEC_ERROR;
-  }
-
-  MOZ_ASSERT(aInputImage->width() >= 0 && aInputImage->height() >= 0);
-  if (static_cast<uint32_t>(aInputImage->width()) != mCodecParams.mWidth ||
-      static_cast<uint32_t>(aInputImage->height()) != mCodecParams.mHeight) {
-    LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
-          mCodecParams.mWidth, mCodecParams.mHeight, aInputImage->width(), aInputImage->height()));
-
-    mGMP->Close();
-
-    // OpenH264 codec (at least) can't handle dynamic input resolution changes
-    // re-init the plugin when the resolution changes
-    // XXX allow codec to indicate it doesn't need re-init!
-    nsTArray<nsCString> tags;
-    tags.AppendElement(NS_LITERAL_CSTRING("h264"));
-    if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
-                                                      NS_LITERAL_CSTRING(""),
-                                                      &mHost,
-                                                      &mGMP)))) {
-      mGMP = nullptr;
-      mHost = nullptr;
-      return WEBRTC_VIDEO_CODEC_ERROR;
-    }
-
-    mCodecParams.mWidth = aInputImage->width();
-    mCodecParams.mHeight = aInputImage->height();
-    // Pass dummy codecSpecific data for now...
-    nsTArray<uint8_t> codecSpecific;
-
-    GMPErr err = mGMP->InitEncode(mCodecParams, codecSpecific, this, 1, mMaxPayloadSize);
-    if (err != GMPNoErr) {
-      return WEBRTC_VIDEO_CODEC_ERROR;
-    }
-  }
+  MOZ_ASSERT(mGMP);
 
   GMPVideoFrame* ftmp = nullptr;
   GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp);
   if (err != GMPNoErr) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   GMPUnique<GMPVideoi420Frame>::Ptr frame(static_cast<GMPVideoi420Frame*>(ftmp));
 
@@ -561,43 +606,58 @@ WebrtcGmpVideoDecoder::InitDecode(const 
   MOZ_ASSERT(mMPS);
 
   if (!mGMPThread) {
     if (NS_WARN_IF(NS_FAILED(mMPS->GetThread(getter_AddRefs(mGMPThread))))) {
       return WEBRTC_VIDEO_CODEC_ERROR;
     }
   }
 
-  int32_t ret;
-  mGMPThread->Dispatch(WrapRunnableRet(this,
-                                       &WebrtcGmpVideoDecoder::InitDecode_g,
-                                       aCodecSettings,
-                                       aNumberOfCores,
-                                       &ret),
-                       NS_DISPATCH_SYNC);
+  nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
+  mGMPThread->Dispatch(WrapRunnable(this,
+                                    &WebrtcGmpVideoDecoder::InitDecode_g,
+                                    aCodecSettings,
+                                    aNumberOfCores,
+                                    initDone.get()),
+                       NS_DISPATCH_NORMAL);
 
-  return ret;
+  nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
+  while (!initDone->IsDone()) {
+    NS_ProcessNextEvent(currentThread, true);
+  }
+
+  return initDone->Result();
 }
 
-int32_t
+void
 WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
-                                    int32_t aNumberOfCores)
+                                    int32_t aNumberOfCores,
+                                    InitDoneRunnable* aInitDone)
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+  UniquePtr<GetGMPVideoDecoderCallback> callback(
+    new InitDoneCallback(this, aInitDone));
   if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoder(&tags,
                                                     NS_LITERAL_CSTRING(""),
-                                                    &mHost,
-                                                    &mGMP)))) {
+                                                    Move(callback))))) {
     mMPS = nullptr;
     mGMP = nullptr;
     mGMPThread = nullptr;
     mHost = nullptr;
-    return WEBRTC_VIDEO_CODEC_ERROR;
+    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
   }
+}
+
+int32_t
+WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP,
+                                   GMPVideoHost* aHost)
+{
+  mGMP = aGMP;
+  mHost = aHost;
   mMPS = nullptr;
 
   if (!mGMP || !mHost) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
   // Bug XXXXXX: transfer settings from codecSettings to codec.
   GMPVideoCodec codec;
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
@@ -16,16 +16,17 @@
 
 #ifndef WEBRTCGMPVIDEOCODEC_H_
 #define WEBRTCGMPVIDEOCODEC_H_
 
 #include <iostream>
 #include <queue>
 
 #include "nsThreadUtils.h"
+#include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 
 #include "mozIGeckoMediaPluginService.h"
 #include "MediaConduitInterface.h"
 #include "AudioConduit.h"
 #include "VideoConduit.h"
 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
 
@@ -72,23 +73,141 @@ public:
   // GMPVideoEncoderCallback virtual functions.
   virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
                        const nsTArray<uint8_t>& aCodecSpecificInfo) override;
 
   virtual void Error(GMPErr aError) override {
   }
 
 private:
-  virtual int32_t InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
-                               int32_t aNumberOfCores,
-                               uint32_t aMaxPayloadSize);
+  class InitDoneRunnable : public nsRunnable
+  {
+  public:
+    InitDoneRunnable()
+      : mInitDone(false),
+        mResult(WEBRTC_VIDEO_CODEC_OK),
+        mThread(do_GetCurrentThread())
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
+      mInitDone = true;
+      return NS_OK;
+    }
+
+    void Dispatch(int32_t aResult)
+    {
+      mResult = aResult;
+      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    bool IsDone()
+    {
+      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
+      return mInitDone;
+    }
+
+    int32_t Result()
+    {
+      return mResult;
+    }
+
+  private:
+    bool mInitDone;
+    int32_t mResult;
+    nsCOMPtr<nsIThread> mThread;
+  };
+
+  void InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
+                    int32_t aNumberOfCores,
+                    uint32_t aMaxPayloadSize,
+                    InitDoneRunnable* aInitDone);
+  int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost,
+                      const webrtc::VideoCodec* aCodecSettings,
+                      uint32_t aMaxPayloadSize);
+  int32_t InitEncoderForSize(unsigned short aWidth, unsigned short aHeight);
+
+  class InitDoneCallback : public GetGMPVideoEncoderCallback
+  {
+  public:
+    InitDoneCallback(WebrtcGmpVideoEncoder* aEncoder,
+                     InitDoneRunnable* aInitDone,
+                     const webrtc::VideoCodec* aCodecSettings,
+                     uint32_t aMaxPayloadSize)
+      : mEncoder(aEncoder),
+        mInitDone(aInitDone),
+        mCodecSettings(aCodecSettings),
+        mMaxPayloadSize(aMaxPayloadSize)
+    {
+    }
 
-  virtual int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
-                           const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
-                           const std::vector<webrtc::VideoFrameType>* aFrameTypes);
+    virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
+    {
+      mEncoder->mGMP = aGMP;
+      mEncoder->mHost = aHost;
+      int32_t result;
+      if (aGMP || aHost) {
+        result = mEncoder->GmpInitDone(aGMP, aHost, mCodecSettings,
+                                       mMaxPayloadSize);
+      } else {
+        result = WEBRTC_VIDEO_CODEC_ERROR;
+      }
+
+      mInitDone->Dispatch(result);
+    }
+
+  private:
+    WebrtcGmpVideoEncoder* mEncoder;
+    nsRefPtr<InitDoneRunnable> mInitDone;
+    const webrtc::VideoCodec* mCodecSettings;
+    uint32_t mMaxPayloadSize;
+  };
+
+  int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
+                   const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                   const std::vector<webrtc::VideoFrameType>* aFrameTypes);
+  void RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
+                                       InitDoneRunnable* aInitDone);
+
+  class InitDoneForResolutionChangeCallback : public GetGMPVideoEncoderCallback
+  {
+  public:
+    InitDoneForResolutionChangeCallback(WebrtcGmpVideoEncoder* aEncoder,
+                                        InitDoneRunnable* aInitDone,
+                                        uint32_t aWidth,
+                                        uint32_t aHeight)
+      : mEncoder(aEncoder),
+        mInitDone(aInitDone),
+        mWidth(aWidth),
+        mHeight(aHeight)
+    {
+    }
+
+    virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
+    {
+      mEncoder->mGMP = aGMP;
+      mEncoder->mHost = aHost;
+      int32_t result;
+      if (aGMP && aHost) {
+        result = mEncoder->InitEncoderForSize(mWidth, mHeight);
+      } else {
+        result = WEBRTC_VIDEO_CODEC_ERROR;
+      }
+
+      mInitDone->Dispatch(result);
+    }
+
+  private:
+    WebrtcGmpVideoEncoder* mEncoder;
+    nsRefPtr<InitDoneRunnable> mInitDone;
+    uint32_t mWidth;
+    uint32_t mHeight;
+  };
 
   virtual int32_t SetRates_g(uint32_t aNewBitRate,
                              uint32_t aFrameRate);
 
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   nsCOMPtr<nsIThread> mGMPThread;
   GMPVideoEncoderProxy* mGMP;
   GMPVideoHost* mHost;
@@ -146,18 +265,82 @@ public:
   virtual void ResetComplete() override {
   }
 
   virtual void Error(GMPErr aError) override {
      mDecoderStatus = aError;
   }
 
 private:
-  virtual int32_t InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
-                               int32_t aNumberOfCores);
+  class InitDoneRunnable : public nsRunnable
+  {
+  public:
+    InitDoneRunnable()
+      : mInitDone(false),
+        mResult(WEBRTC_VIDEO_CODEC_OK),
+        mThread(do_GetCurrentThread())
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
+      mInitDone = true;
+      return NS_OK;
+    }
+
+    void Dispatch(int32_t aResult)
+    {
+      mResult = aResult;
+      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    bool IsDone()
+    {
+      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
+      return mInitDone;
+    }
+
+    int32_t Result()
+    {
+      return mResult;
+    }
+
+  private:
+    bool mInitDone;
+    int32_t mResult;
+    nsCOMPtr<nsIThread> mThread;
+  };
+
+  void InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
+                    int32_t aNumberOfCores,
+                    InitDoneRunnable* aInitDone);
+  int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
+
+  class InitDoneCallback : public GetGMPVideoDecoderCallback
+  {
+  public:
+    explicit InitDoneCallback(WebrtcGmpVideoDecoder* aDecoder,
+                              InitDoneRunnable* aInitDone)
+      : mDecoder(aDecoder),
+        mInitDone(aInitDone)
+    {
+    }
+
+    virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
+    {
+      int32_t result = mDecoder->GmpInitDone(aGMP, aHost);
+
+      mInitDone->Dispatch(result);
+    }
+
+  private:
+    WebrtcGmpVideoDecoder* mDecoder;
+    nsRefPtr<InitDoneRunnable> mInitDone;
+};
 
   virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
                            bool aMissingFrames,
                            const webrtc::RTPFragmentationHeader* aFragmentation,
                            const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                            int64_t aRenderTimeMs);
 
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;