Bug 1267918 - Add GMPCrashHelperHolder to automatically disconnect crash helpers on actor destory. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Wed, 29 Jun 2016 11:42:00 +1200
changeset 383058 48b83836660860a484b4844028366657cd1e9d1f
parent 383057 15a5cccb0549ec96b9d752de7e299e5bc621acec
child 383059 364891f5fc7d399e47d51e5b2ce1ed576581340e
push id21911
push usercpearce@mozilla.com
push dateFri, 01 Jul 2016 04:05:57 +0000
reviewersgerald
bugs1267918
milestone50.0a1
Bug 1267918 - Add GMPCrashHelperHolder to automatically disconnect crash helpers on actor destory. r=gerald Disconnecting the GMPCrashHelpers at the right time is hard, because in the crashing case we're all shutdown before the GMPCrashHelpers can be invoked to help handle the crash report. So add a class to help the helpers; GMPCrashHelperHolder. This composes into the GMP content protocol actors, to help them disconnect the crash helpers at the right time. See the comment in GMPCrashHelperHolder for the details. MozReview-Commit-ID: E5rE6e5Jues
dom/media/gmp/GMPAudioDecoderParent.cpp
dom/media/gmp/GMPAudioDecoderParent.h
dom/media/gmp/GMPCrashHelperHolder.h
dom/media/gmp/GMPDecryptorParent.cpp
dom/media/gmp/GMPDecryptorParent.h
dom/media/gmp/GMPService.cpp
dom/media/gmp/GMPVideoDecoderParent.cpp
dom/media/gmp/GMPVideoDecoderParent.h
dom/media/gmp/GMPVideoEncoderParent.cpp
dom/media/gmp/GMPVideoEncoderParent.h
dom/media/gmp/moz.build
--- a/dom/media/gmp/GMPAudioDecoderParent.cpp
+++ b/dom/media/gmp/GMPAudioDecoderParent.cpp
@@ -221,16 +221,17 @@ GMPAudioDecoderParent::ActorDestroy(Acto
     mCallback->Terminated();
     mCallback = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->AudioDecoderDestroyed(this);
     mPlugin = nullptr;
   }
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 bool
 GMPAudioDecoderParent::RecvDecoded(const GMPAudioDecodedSampleData& aDecoded)
 {
   LOGV(("GMPAudioDecoderParent[%p]::RecvDecoded() timestamp=%lld",
         this, aDecoded.mTimeStamp()));
 
--- a/dom/media/gmp/GMPAudioDecoderParent.h
+++ b/dom/media/gmp/GMPAudioDecoderParent.h
@@ -7,24 +7,26 @@
 #define GMPAudioDecoderParent_h_
 
 #include "mozilla/RefPtr.h"
 #include "gmp-audio-decode.h"
 #include "gmp-audio-codec.h"
 #include "mozilla/gmp/PGMPAudioDecoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPAudioDecoderProxy.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPAudioDecoderParent final : public GMPAudioDecoderProxy
                                   , public PGMPAudioDecoderParent
+                                  , public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPAudioDecoderParent)
 
   explicit GMPAudioDecoderParent(GMPContentParent *aPlugin);
 
   nsresult Shutdown();
 
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPCrashHelperHolder.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef GMPCrashHelperHolder_h_
+#define GMPCrashHelperHolder_h_
+
+#include "GMPService.h"
+#include "mozilla/RefPtr.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+class GMPCrashHelper;
+
+namespace mozilla {
+
+// Disconnecting the GMPCrashHelpers at the right time is hard. We need to
+// ensure that in the crashing case that we stay connected until the
+// "gmp-plugin-crashed" message is processed in the content process.
+//
+// We have two channels connecting to the GMP; PGMP which connects from
+// chrome to GMP process, and PGMPContent, which bridges between the content
+// and GMP process. If the GMP crashes both PGMP and PGMPContent receive
+// ActorDestroy messages and begin to shutdown at the same time.
+//
+// However the crash report mini dump must be generated in the chrome
+// process' ActorDestroy, before the "gmp-plugin-crashed" message can be sent
+// to the content process. We fire the "PluginCrashed" event when we handle
+// the "gmp-plugin-crashed" message in the content process, and we need the
+// crash helpers to do that.
+//
+// The PGMPContent's managed actors' ActorDestroy messages are the only shutdown
+// notification we get in the content process, but we can't disconnect the
+// crash handlers there in the crashing case, as ActorDestroy happens before
+// we've received the "gmp-plugin-crashed" message and had a chance for the
+// crash helpers to generate the window to dispatch PluginCrashed to initiate
+// the crash report submission notification box.
+//
+// So we need to ensure that in the content process, the GMPCrashHelpers stay
+// connected to the GMPService until after ActorDestroy messages are received
+// if there's an abnormal shutdown. In the case where the GMP doesn't crash,
+// we do actually want to disconnect GMPCrashHandlers in ActorDestroy, since
+// we don't have any other signal that a GMP actor is shutting down. If we don't
+// disconnect the crash helper there in the normal shutdown case, the helper
+// will stick around forever and leak.
+//
+// In the crashing case, the GMPCrashHelpers are deallocated when the crash
+// report is processed in GeckoMediaPluginService::RunPluginCrashCallbacks().
+//
+// It's a bit yuck that we have to have two paths for disconnecting the crash
+// helpers, but there aren't really any better options.
+class GMPCrashHelperHolder
+{
+public:
+  
+  void SetCrashHelper(GMPCrashHelper* aHelper)
+  {
+    mCrashHelper = aHelper;
+  }
+  
+  GMPCrashHelper* GetCrashHelper()
+  {
+    return mCrashHelper;
+  }
+
+  void MaybeDisconnect(bool aAbnormalShutdown)
+  {
+    if (!aAbnormalShutdown) {
+      RefPtr<gmp::GeckoMediaPluginService> service(gmp::GeckoMediaPluginService::GetGeckoMediaPluginService());
+      service->DisconnectCrashHelper(GetCrashHelper());
+    }
+  }
+
+private:
+  RefPtr<GMPCrashHelper> mCrashHelper;
+};
+
+}
+
+#endif // GMPCrashHelperHolder_h_
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -421,16 +421,17 @@ GMPDecryptorParent::ActorDestroy(ActorDe
     // May call Close() (and Shutdown()) immediately or with a delay
     mCallback->Terminated();
     mCallback = nullptr;
   }
   if (mPlugin) {
     mPlugin->DecryptorDestroyed(this);
     mPlugin = nullptr;
   }
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 bool
 GMPDecryptorParent::Recv__delete__()
 {
   LOGD(("GMPDecryptorParent[%p]::Recv__delete__()", this));
 
   if (mPlugin) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -5,27 +5,29 @@
 
 #ifndef GMPDecryptorParent_h_
 #define GMPDecryptorParent_h_
 
 #include "mozilla/gmp/PGMPDecryptorParent.h"
 #include "mozilla/RefPtr.h"
 #include "gmp-decryption.h"
 #include "GMPDecryptorProxy.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 
 class CryptoSample;
 
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPDecryptorParent final : public GMPDecryptorProxy
                                , public PGMPDecryptorParent
+                               , public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
 
   explicit GMPDecryptorParent(GMPContentParent *aPlugin);
 
   // GMPDecryptorProxy
 
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -408,32 +408,35 @@ GeckoMediaPluginService::GetAbstractGMPT
 {
   MutexAutoLock lock(mMutex);
   return mAbstractGMPThread;
 }
 
 class GetGMPContentParentForAudioDecoderDone : public GetGMPContentParentCallback
 {
 public:
-  explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
+  explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback,
+                                                  GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPAudioDecoderParent* gmpADP = nullptr;
-    if (aGMPParent) {
-      aGMPParent->GetGMPAudioDecoder(&gmpADP);
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPAudioDecoder(&gmpADP))) {
+      gmpADP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpADP);
   }
 
 private:
   UniquePtr<GetGMPAudioDecoderCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPAudioDecoder(GMPCrashHelper* aHelper,
                                             nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
 {
@@ -441,48 +444,52 @@ GeckoMediaPluginService::GetGMPAudioDeco
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForAudioDecoderDone(Move(aCallback)));
+    new GetGMPContentParentForAudioDecoderDone(Move(aCallback), aHelper));
   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
 {
 public:
-  explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+  explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
+                                                  GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPVideoDecoderParent* gmpVDP = nullptr;
     GMPVideoHostImpl* videoHost = nullptr;
     if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP))) {
       videoHost = &gmpVDP->Host();
+      gmpVDP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpVDP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoDecoderCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoDecoder(GMPCrashHelper* aHelper,
                                             nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
 {
@@ -490,48 +497,52 @@ GeckoMediaPluginService::GetGMPVideoDeco
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoDecoderDone(Move(aCallback)));
+    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper));
   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
 {
 public:
-  explicit GetGMPContentParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
+  explicit GetGMPContentParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback,
+                                                  GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPVideoEncoderParent* gmpVEP = nullptr;
     GMPVideoHostImpl* videoHost = nullptr;
     if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoEncoder(&gmpVEP))) {
       videoHost = &gmpVEP->Host();
+      gmpVEP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpVEP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoEncoderCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoEncoder(GMPCrashHelper* aHelper,
                                             nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
 {
@@ -539,47 +550,50 @@ GeckoMediaPluginService::GetGMPVideoEnco
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoEncoderDone(Move(aCallback)));
+    new GetGMPContentParentForVideoEncoderDone(Move(aCallback), aHelper));
   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
 {
 public:
-  explicit GetGMPContentParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback)
+  explicit GetGMPContentParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback,
+                                               GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPDecryptorParent* ksp = nullptr;
-    if (aGMPParent) {
-      aGMPParent->GetGMPDecryptor(&ksp);
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPDecryptor(&ksp))) {
+      ksp->SetCrashHelper(mHelper);
     }
     mCallback->Done(ksp);
   }
 
 private:
   UniquePtr<GetGMPDecryptorCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPDecryptor(GMPCrashHelper* aHelper,
                                          nsTArray<nsCString>* aTags,
                                          const nsACString& aNodeId,
                                          UniquePtr<GetGMPDecryptorCallback>&& aCallback)
 {
@@ -595,17 +609,17 @@ GeckoMediaPluginService::GetGMPDecryptor
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForDecryptorDone(Move(aCallback)));
+    new GetGMPContentParentForDecryptorDone(Move(aCallback), aHelper));
   if (!GetContentParentFrom(aHelper,
                             aNodeId,
                             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
                             *aTags,
                             Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -295,16 +295,17 @@ GMPVideoDecoderParent::ActorDestroy(Acto
     mCallback = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoDecoderDestroyed(this);
     mPlugin = nullptr;
   }
   mVideoHost.ActorDestroyed();
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 bool
 GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
 {
   --mFrameCount;
   LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld frameCount=%d",
     this, aDecodedFrame.mTimestamp(), mFrameCount));
--- a/dom/media/gmp/GMPVideoDecoderParent.h
+++ b/dom/media/gmp/GMPVideoDecoderParent.h
@@ -10,25 +10,27 @@
 #include "gmp-video-decode.h"
 #include "mozilla/gmp/PGMPVideoDecoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoDecoderProxy.h"
 #include "VideoUtils.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPVideoDecoderParent final : public PGMPVideoDecoderParent
                                   , public GMPVideoDecoderProxy
                                   , public GMPSharedMemManager
+                                  , public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent)
 
   explicit GMPVideoDecoderParent(GMPContentParent *aPlugin);
 
   GMPVideoHostImpl& Host();
   nsresult Shutdown();
--- a/dom/media/gmp/GMPVideoEncoderParent.cpp
+++ b/dom/media/gmp/GMPVideoEncoderParent.cpp
@@ -264,16 +264,17 @@ GMPVideoEncoderParent::ActorDestroy(Acto
     mEncodedThread = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoEncoderDestroyed(this);
     mPlugin = nullptr;
   }
   mVideoHost.ActorDestroyed(); // same as DoneWithAPI
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 static void
 EncodedCallback(GMPVideoEncoderCallbackProxy* aCallback,
                 GMPVideoEncodedFrame* aEncodedFrame,
                 nsTArray<uint8_t>* aCodecSpecificInfo,
                 nsCOMPtr<nsIThread> aThread)
 {
--- a/dom/media/gmp/GMPVideoEncoderParent.h
+++ b/dom/media/gmp/GMPVideoEncoderParent.h
@@ -9,25 +9,27 @@
 #include "mozilla/RefPtr.h"
 #include "gmp-video-encode.h"
 #include "mozilla/gmp/PGMPVideoEncoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoEncoderProxy.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPVideoEncoderParent : public GMPVideoEncoderProxy,
                               public PGMPVideoEncoderParent,
-                              public GMPSharedMemManager
+                              public GMPSharedMemManager,
+                              public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent)
 
   explicit GMPVideoEncoderParent(GMPContentParent *aPlugin);
 
   GMPVideoHostImpl& Host();
   void Shutdown();
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -33,16 +33,17 @@ EXPORTS += [
     'GMPAudioDecoderChild.h',
     'GMPAudioDecoderParent.h',
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
     'GMPChild.h',
     'GMPContentChild.h',
     'GMPContentParent.h',
+    'GMPCrashHelperHolder.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
     'GMPLoader.h',
     'GMPMessageUtils.h',
     'GMPParent.h',
     'GMPPlatform.h',