Bug 1267918 - Add GMPCrashHelper to GMPService::GetGMP* functions. r=gerald
authorChris Pearce <cpearce@mozilla.com>
Wed, 29 Jun 2016 11:41:56 +1200
changeset 303405 5bdad12ea4de6852849f32dec2c4e934e911067c
parent 303404 d409232655857c1d27209a02be589344f5eee903
child 303406 94feba0c91327fb20850c5e7224d81b0ac72af2b
push id30405
push usercbook@mozilla.com
push dateSat, 02 Jul 2016 09:16:44 +0000
treeherderautoland@73c0a2bba6db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1267918
milestone50.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 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.");
   }