Bug 1267918 - Add GMPCrashHelper to GMPService::GetGMP* functions. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Wed, 29 Jun 2016 11:41:56 +1200
changeset 383036 4eee756f6abf7a6212b6a3ce31dcb8182f12999b
parent 383035 e67160643c724bf1f90cd62843cbb76de65a989f
child 383037 18f6e316bb44c9d297b5770fc021dd146744b672
child 383057 15a5cccb0549ec96b9d752de7e299e5bc621acec
push id21909
push usercpearce@mozilla.com
push dateFri, 01 Jul 2016 03:07:19 +0000
reviewersgerald
bugs1267918
milestone50.0a1
Bug 1267918 - Add GMPCrashHelper to GMPService::GetGMP* functions. r=gerald This enables callers to specify a way to determine the correct window to dispatch the PluginCrashed event to should the GMP actor crash. We need a way to determine the correct window at crash time, as the GMP's window can change at runtime. For example, if the GMP is being used for unencrypted decoding, the <video> element can be moved to a new browser window at runtime. Note: I don't handle disconnecting the GMPCrashHandlers in this patch; we do delete the GMPCrashHandlers in this patch when their associated GMP crashes, and in the next patch we handle disconnecting GMPCrashHandlers in the case where we don't crash. MozReview-Commit-ID: DrwcZAB6Ys0
dom/media/eme/CDMProxy.cpp
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
dom/media/gmp/mozIGeckoMediaPluginService.idl
dom/media/gtest/TestGMPCrossOrigin.cpp
dom/media/gtest/TestGMPRemoveAndDelete.cpp
dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -223,17 +223,17 @@ CDMProxy::gmp_InitGetGMPDecryptor(nsresu
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
   UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
                                                                        Move(aData)));
-  nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
+  nsresult rv = mps->GetGMPDecryptor(nullptr, &tags, GetNodeId(), Move(callback));
   if (NS_FAILED(rv)) {
     RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
   }
 }
 
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -271,30 +271,52 @@ GeckoMediaPluginService::AddPluginCrashe
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::RunPluginCrashCallbacks(uint32_t aPluginId,
                                                  const nsACString& aPluginName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
-  RemoveObsoletePluginCrashCallbacks();
+
+  nsAutoPtr<nsTArray<RefPtr<GMPCrashHelper>>> helpers;
+  {
+    MutexAutoLock lock(mMutex);
+    mPluginCrashHelpers.RemoveAndForget(aPluginId, helpers);
+  }
+  if (!helpers) {
+    LOGD(("%s::%s(%i) No crash helpers, not handling crash.", __CLASS__, __FUNCTION__, aPluginId));
+    return NS_OK;
+  }
 
-  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
-    RefPtr<GMPCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    if (callback->GetPluginId() == aPluginId) {
-      LOGD(("%s::%s(%i) - Running #%u",
-          __CLASS__, __FUNCTION__, aPluginId, i - 1));
-      callback->Run(aPluginName);
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
+  for (const auto& helper : *helpers) {
+    nsCOMPtr<nsPIDOMWindowInner> window = helper->GetPluginCrashedEventTarget();
+    if (NS_WARN_IF(!window)) {
+      continue;
+    }
+    nsCOMPtr<nsIDocument> document(window->GetExtantDoc());
+    if (NS_WARN_IF(!document)) {
+      continue;
     }
-  }
-  mPluginCrashes.AppendElement(PluginCrash(aPluginId, aPluginName));
-  if (mPluginCrashes.Length() > MAX_PLUGIN_CRASHES) {
-    mPluginCrashes.RemoveElementAt(0);
+
+    dom::PluginCrashedEventInit init;
+    init.mPluginID = aPluginId;
+    init.mBubbles = true;
+    init.mCancelable = true;
+    init.mGmpPlugin = true;
+    CopyUTF8toUTF16(aPluginName, init.mPluginName);
+    init.mSubmittedCrashReport = false;
+    RefPtr<dom::PluginCrashedEvent> event =
+      dom::PluginCrashedEvent::Constructor(document,
+                                           NS_LITERAL_STRING("PluginCrashed"),
+                                           init);
+    event->SetTrusted(true);
+    event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+
+    EventDispatcher::DispatchDOMEvent(window, nullptr, event, nullptr, nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
 GeckoMediaPluginService::Init()
 {
@@ -403,32 +425,36 @@ public:
     mCallback->Done(gmpADP);
   }
 
 private:
   UniquePtr<GetGMPAudioDecoderCallback> mCallback;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
+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)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                            *aTags, Move(callback))) {
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 class GetGMPContentParentForVideoDecoderDone : public GetGMPContentParentCallback
 {
@@ -448,32 +474,36 @@ public:
     mCallback->Done(gmpVDP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoDecoderCallback> mCallback;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
+GeckoMediaPluginService::GetGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                            nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPVideoDecoderCallback>&& 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 GetGMPContentParentForVideoDecoderDone(Move(aCallback)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                            *aTags, Move(callback))) {
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 class GetGMPContentParentForVideoEncoderDone : public GetGMPContentParentCallback
 {
@@ -493,32 +523,36 @@ public:
     mCallback->Done(gmpVEP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoEncoderCallback> mCallback;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
+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)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
-                            *aTags, Move(callback))) {
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 class GetGMPContentParentForDecryptorDone : public GetGMPContentParentCallback
 {
@@ -537,17 +571,18 @@ public:
     mCallback->Done(ksp);
   }
 
 private:
   UniquePtr<GetGMPDecryptorCallback> mCallback;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
+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()) {
     NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
                "EME decryption not available without sandboxing support.");
     return NS_ERROR_NOT_AVAILABLE;
@@ -559,27 +594,66 @@ GeckoMediaPluginService::GetGMPDecryptor
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
     new GetGMPContentParentForDecryptorDone(Move(aCallback)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                            *aTags, Move(callback))) {
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::HasPluginForAPI(const nsACString& aAPI,
                                          nsTArray<nsCString>* aTags,
                                          bool* aOutHavePlugin)
 {
   nsCString unused;
   return GetPluginVersionForAPI(aAPI, aTags, aOutHavePlugin, unused);
 }
 
+void
+GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper)
+{
+  if (!aHelper) {
+    return;
+  }
+  MutexAutoLock lock(mMutex);
+  nsTArray<RefPtr<GMPCrashHelper>>* helpers;
+  if (!mPluginCrashHelpers.Get(aPluginId, &helpers)) {
+    helpers = new nsTArray<RefPtr<GMPCrashHelper>>();
+    mPluginCrashHelpers.Put(aPluginId, helpers);
+  } else if (helpers->Contains(aHelper)) {
+    return;
+  }
+  helpers->AppendElement(aHelper);
+}
+
+void GeckoMediaPluginService::DisconnectCrashHelper(GMPCrashHelper* aHelper)
+{
+  if (!aHelper) {
+    return;
+  }
+  MutexAutoLock lock(mMutex);
+  for (auto iter = mPluginCrashHelpers.Iter(); !iter.Done(); iter.Next()) {
+    nsTArray<RefPtr<GMPCrashHelper>>* helpers = iter.Data();
+    if (!helpers->Contains(aHelper)) {
+      continue;
+    }
+    helpers->RemoveElement(aHelper);
+    MOZ_ASSERT(!helpers->Contains(aHelper)); // Ensure there aren't duplicates.
+    if (helpers->IsEmpty()) {
+      iter.Remove();
+    }
+  }
+}
+
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -15,19 +15,32 @@
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsIWeakReference.h"
 #include "mozilla/AbstractThread.h"
+#include "nsClassHashtable.h"
 
 template <class> struct already_AddRefed;
 
+// For every GMP actor requested, the caller can specify a crash helper,
+// which is an object which supplies the nsPIDOMWindowInner to which we'll
+// dispatch the PluginCrashed event if the GMP crashes.
+class GMPCrashHelper
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPCrashHelper)
+  virtual already_AddRefed<nsPIDOMWindowInner> GetPluginCrashedEventTarget() = 0;
+protected:
+  virtual ~GMPCrashHelper() {}
+};
+
 namespace mozilla {
 
 extern LogModule* GetGMPLog();
 
 namespace gmp {
 
 class GetGMPContentParentCallback;
 
@@ -40,29 +53,33 @@ public:
   virtual nsresult Init();
 
   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,
+  NS_IMETHOD GetGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
     override;
-  NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPVideoEncoder(GMPCrashHelper* aHelper,
+                                nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
     override;
-  NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPAudioDecoder(GMPCrashHelper* aHelper,
+                                nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
     override;
-  NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPDecryptor(GMPCrashHelper* aHelper,
+                             nsTArray<nsCString>* aTags,
                              const nsACString& aNodeId,
                              UniquePtr<GetGMPDecryptorCallback>&& aCallback)
     override;
 
   int32_t AsyncShutdownTimeoutMs();
 
   NS_IMETHOD RunPluginCrashCallbacks(uint32_t aPluginId,
                                      const nsACString& aPluginName) override;
@@ -70,33 +87,37 @@ public:
   // Sets the window to which 'PluginCrashed' chromeonly event is dispatched.
   // Note: if the plugin has crashed before the target window has been set,
   // the 'PluginCrashed' event is dispatched as soon as a target window is set.
   void AddPluginCrashedEventTarget(const uint32_t aPluginId,
                                    nsPIDOMWindowInner* aParentWindow);
 
   RefPtr<AbstractThread> GetAbstractGMPThread();
 
+  void ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper);
+  void DisconnectCrashHelper(GMPCrashHelper* aHelper);
+
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
   void RemoveObsoletePluginCrashCallbacks(); // Called from add/run.
 
   virtual void InitializePlugins(AbstractThread* aAbstractGMPThread) = 0;
-  virtual bool GetContentParentFrom(const nsACString& aNodeId,
+  virtual bool GetContentParentFrom(GMPCrashHelper* aHelper,
+                                    const nsACString& aNodeId,
                                     const nsCString& aAPI,
                                     const nsTArray<nsCString>& aTags,
                                     UniquePtr<GetGMPContentParentCallback>&& aCallback) = 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 and
+  Mutex mMutex; // Protects mGMPThread, mAbstractGMPThread, mPluginCrashHelpers,
                 // mGMPThreadShutdown and some members in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
   RefPtr<AbstractThread> mAbstractGMPThread;
   bool mGMPThreadShutdown;
   bool mShuttingDownOnGMPThread;
 
   class GMPCrashCallback
   {
@@ -135,14 +156,16 @@ protected:
              mPluginName == aOther.mPluginName;
     }
   };
 
   static const size_t MAX_PLUGIN_CRASHES = 100;
   nsTArray<PluginCrash> mPluginCrashes;
 
   nsTArray<RefPtr<GMPCrashCallback>> mPluginCrashCallbacks;
+
+  nsClassHashtable<nsUint32HashKey, nsTArray<RefPtr<GMPCrashHelper>>> mPluginCrashHelpers;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPService_h_
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -45,20 +45,21 @@ GeckoMediaPluginServiceChild::GetSinglet
   }
 #endif
   return service.forget().downcast<GeckoMediaPluginServiceChild>();
 }
 
 class GetContentParentFromDone : public GetServiceChildCallback
 {
 public:
-  GetContentParentFromDone(const nsACString& aNodeId, const nsCString& aAPI,
+  GetContentParentFromDone(GMPCrashHelper* aHelper, const nsACString& aNodeId, const nsCString& aAPI,
                            const nsTArray<nsCString>& aTags,
                            UniquePtr<GetGMPContentParentCallback>&& aCallback)
-    : mNodeId(aNodeId),
+    : mHelper(aHelper),
+      mNodeId(aNodeId),
       mAPI(aAPI),
       mTags(aTags),
       mCallback(Move(aCallback))
   {
   }
 
   void Done(GMPServiceChild* aGMPServiceChild) override
   {
@@ -70,16 +71,21 @@ public:
     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) {
@@ -94,32 +100,34 @@ public:
       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(const nsACString& aNodeId,
+GeckoMediaPluginServiceChild::GetContentParentFrom(GMPCrashHelper* aHelper,
+                                                   const nsACString& aNodeId,
                                                    const nsCString& aAPI,
                                                    const nsTArray<nsCString>& aTags,
                                                    UniquePtr<GetGMPContentParentCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   UniquePtr<GetServiceChildCallback> callback(
-    new GetContentParentFromDone(aNodeId, aAPI, aTags, Move(aCallback)));
+    new GetContentParentFromDone(aHelper, aNodeId, aAPI, aTags, Move(aCallback)));
   GetServiceChild(Move(callback));
 
   return true;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::GetPluginVersionForAPI(const nsACString& aAPI,
                                                      nsTArray<nsCString>* aTags,
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -55,17 +55,18 @@ public:
 
   void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
 
 protected:
   void InitializePlugins(AbstractThread*) override
   {
     // Nothing to do here.
   }
-  bool GetContentParentFrom(const nsACString& aNodeId,
+  bool GetContentParentFrom(GMPCrashHelper* aHelper,
+                            const nsACString& aNodeId,
                             const nsCString& aAPI,
                             const nsTArray<nsCString>& aTags,
                             UniquePtr<GetGMPContentParentCallback>&& aCallback)
     override;
 
 private:
   friend class OpenPGMPServiceChild;
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -520,41 +520,44 @@ GeckoMediaPluginServiceParent::EnsureIni
     return GenericPromise::CreateAndResolve(true, __func__);
   }
   // We should have an init promise in flight.
   MOZ_ASSERT(!mInitPromise.IsEmpty());
   return mInitPromise.Ensure(__func__);
 }
 
 bool
-GeckoMediaPluginServiceParent::GetContentParentFrom(const nsACString& aNodeId,
+GeckoMediaPluginServiceParent::GetContentParentFrom(GMPCrashHelper* aHelper,
+                                                    const nsACString& aNodeId,
                                                     const nsCString& aAPI,
                                                     const nsTArray<nsCString>& aTags,
                                                     UniquePtr<GetGMPContentParentCallback>&& aCallback)
 {
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
   if (!thread) {
     return false;
   }
 
   RefPtr<GeckoMediaPluginServiceParent> self(this);
   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]() -> void {
+    [self, tags, api, nodeId, rawCallback, helper]() -> void {
       UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
       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);
         return;
       }
+      self->ConnectCrashHelper(gmp->GetPluginId(), helper);
       gmp->GetGMPContentParent(Move(callback));
     },
     [rawCallback]() -> void {
       UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
       NS_WARNING("GMPService::EnsureInitialized failed.");
       callback->Done(nullptr);
     });
   return true;
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -110,17 +110,18 @@ 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(const nsACString& aNodeId,
+  bool GetContentParentFrom(GMPCrashHelper* aHelper,
+                            const nsACString& aNodeId,
                             const nsCString& aAPI,
                             const nsTArray<nsCString>& aTags,
                             UniquePtr<GetGMPContentParentCallback>&& aCallback)
     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);
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -10,16 +10,17 @@
 #include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 #include "nsStringGlue.h"
 class GMPAudioDecoderProxy;
 class GMPDecryptorProxy;
 class GMPVideoDecoderProxy;
 class GMPVideoEncoderProxy;
 class GMPVideoHost;
+class GMPCrashHelper;
 
 template<class T>
 class GMPGetterCallback
 {
 public:
   GMPGetterCallback() { MOZ_COUNT_CTOR(GMPGetterCallback<T>); }
   virtual ~GMPGetterCallback() { MOZ_COUNT_DTOR(GMPGetterCallback<T>); }
   virtual void Done(T*) = 0;
@@ -46,16 +47,17 @@ public:
 %}
 
 [ptr] native TagArray(nsTArray<nsCString>);
 native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
 native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
 native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
 native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
 native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
+native GMPCrashHelperPtr(GMPCrashHelper*);
 
 [scriptable, uuid(44d362ae-937a-4803-bee6-f2512a0149d1)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
   /**
    * The GMP thread. Callable from any thread.
    */
@@ -89,65 +91,70 @@ interface mozIGeckoMediaPluginService : 
    * 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 getGMPVideoDecoder(in TagArray tags,
+  void getGMPVideoDecoder(in GMPCrashHelperPtr helper,
+                          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]
-  void getGMPVideoEncoder(in TagArray tags,
+  void getGMPVideoEncoder(in GMPCrashHelperPtr helper,
+                          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.
    * 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,
+  void getGMPAudioDecoder(in GMPCrashHelperPtr helper,
+                          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.
    * 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,
+  void getGMPDecryptor(in GMPCrashHelperPtr helper,
+                       in TagArray tags,
+                       in ACString nodeId,
                        in GetGMPDecryptorCallback callback);
 
   /**
    * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
    */
   [noscript]
   void getNodeId(in AString origin,
                  in AString topLevelOrigin,
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -46,17 +46,18 @@ struct GMPTestRunner
   void RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor);
   void RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor);
 
 private:
   ~GMPTestRunner() { }
 };
 
 template<class T, class Base,
-         nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(nsTArray<nsCString>*,
+         nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(GMPCrashHelper*,
+                                                                nsTArray<nsCString>*,
                                                                 const nsACString&,
                                                                 UniquePtr<Base>&&)>
 class RunTestGMPVideoCodec : public Base
 {
 public:
   void Done(T* aGMP, GMPVideoHost* aHost) override
   {
     EXPECT_TRUE(aGMP);
@@ -85,17 +86,17 @@ protected:
   static nsresult Get(const nsACString& aNodeId, UniquePtr<Base>&& aCallback)
   {
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("h264"));
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     RefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
-    return ((*service).*Getter)(&tags, aNodeId, Move(aCallback));
+    return ((*service).*Getter)(nullptr, &tags, aNodeId, Move(aCallback));
   }
 
 protected:
   GMPTestMonitor& mMonitor;
 };
 
 typedef RunTestGMPVideoCodec<GMPVideoDecoderProxy,
                              GetGMPVideoDecoderCallback,
@@ -673,17 +674,17 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     UniquePtr<GetGMPDecryptorCallback> callback(
       new CreateDecryptorDone(this, aContinuation));
     nsresult rv =
-      service->GetGMPDecryptor(&tags, mNodeId, Move(callback));
+      service->GetGMPDecryptor(nullptr, &tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     RefPtr<GeckoMediaPluginService> service =
--- a/dom/media/gtest/TestGMPRemoveAndDelete.cpp
+++ b/dom/media/gtest/TestGMPRemoveAndDelete.cpp
@@ -318,17 +318,17 @@ GMPRemoveTest::gmp_GetVideoDecoder(nsCSt
     GMPTestMonitor* mMonitor;
     GMPVideoDecoderProxy** mDecoder;
     GMPVideoHost** mHost;
   };
 
   UniquePtr<GetGMPVideoDecoderCallback>
     cb(new Callback(&mTestMonitor, aOutDecoder, aOutHost));
 
-  if (NS_FAILED(GetService()->GetGMPVideoDecoder(&tags, aNodeId, Move(cb)))) {
+  if (NS_FAILED(GetService()->GetGMPVideoDecoder(nullptr, &tags, aNodeId, Move(cb)))) {
     mTestMonitor.SetFinished();
   }
 }
 
 void
 GMPRemoveTest::CloseVideoDecoder()
 {
   mGMPThread->Dispatch(
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
@@ -225,17 +225,17 @@ GMPAudioDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPAudioDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetGMPAudioDecoder(nullptr, &tags, GetNodeId(), Move(callback)))) {
     mInitPromise.Reject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   return promise;
 }
 
 nsresult
 GMPAudioDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
@@ -276,17 +276,17 @@ GMPVideoDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetGMPVideoDecoder(nullptr, &tags, GetNodeId(), Move(callback)))) {
     mInitPromise.Reject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   return promise;
 }
 
 nsresult
 GMPVideoDecoder::Input(MediaRawData* aSample)
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
@@ -228,17 +228,18 @@ WebrtcGmpVideoEncoder::InitEncode_g(
     uint32_t aMaxPayloadSize,
     const RefPtr<GmpInitDoneRunnable>& aInitDone)
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
   UniquePtr<GetGMPVideoEncoderCallback> callback(
     new InitDoneCallback(aThis, aInitDone, aCodecParams, aMaxPayloadSize));
   aThis->mInitting = true;
-  nsresult rv = aThis->mMPS->GetGMPVideoEncoder(&tags,
+  nsresult rv = aThis->mMPS->GetGMPVideoEncoder(nullptr,
+                                                &tags,
                                                 NS_LITERAL_CSTRING(""),
                                                 Move(callback));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     LOGD(("GMP Encode: GetGMPVideoEncoder failed"));
     aThis->Close_g();
     aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
                         "GMP Encode: GetGMPVideoEncoder failed");
   }
@@ -359,17 +360,18 @@ WebrtcGmpVideoEncoder::RegetEncoderForRe
                                             aHeight));
 
   // 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"));
   mInitting = true;
-  if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
+  if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(nullptr,
+                                                    &tags,
                                                     NS_LITERAL_CSTRING(""),
                                                     Move(callback))))) {
     aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
                         "GMP Encode: GetGMPVideoEncoder failed");
   }
 }
 
 int32_t
@@ -713,17 +715,18 @@ WebrtcGmpVideoDecoder::InitDecode_g(
     int32_t aNumberOfCores,
     const RefPtr<GmpInitDoneRunnable>& aInitDone)
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
   UniquePtr<GetGMPVideoDecoderCallback> callback(
     new InitDoneCallback(aThis, aInitDone));
   aThis->mInitting = true;
-  nsresult rv = aThis->mMPS->GetGMPVideoDecoder(&tags,
+  nsresult rv = aThis->mMPS->GetGMPVideoDecoder(nullptr,
+                                                &tags,
                                                 NS_LITERAL_CSTRING(""),
                                                 Move(callback));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     LOGD(("GMP Decode: GetGMPVideoDecoder failed"));
     aThis->Close_g();
     aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
                         "GMP Decode: GetGMPVideoDecoder failed.");
   }