Bug 1209385 - Crash GMPs that don't respond to GMPVideoDecoder::Reset(). r=jwwang, a=sylvestre
authorChris Pearce <cpearce@mozilla.com>
Wed, 30 Sep 2015 06:56:07 +1300
changeset 296205 f7fb82b98052509b881a6c28de2755fbaba1969f
parent 296204 95baa1b51926b77b839c50f4ef6daeb197c95104
child 296206 afac098c635f6cb5fc984e08a48c160f0cd43d29
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang, sylvestre
bugs1209385
milestone43.0a2
Bug 1209385 - Crash GMPs that don't respond to GMPVideoDecoder::Reset(). r=jwwang, a=sylvestre
dom/media/gmp/GMPChild.cpp
dom/media/gmp/GMPChild.h
dom/media/gmp/GMPContentParent.cpp
dom/media/gmp/GMPContentParent.h
dom/media/gmp/GMPMessageUtils.h
dom/media/gmp/GMPParent.cpp
dom/media/gmp/GMPParent.h
dom/media/gmp/GMPServiceChild.cpp
dom/media/gmp/GMPServiceChild.h
dom/media/gmp/GMPServiceParent.cpp
dom/media/gmp/GMPServiceParent.h
dom/media/gmp/GMPUtils.h
dom/media/gmp/GMPVideoDecoderParent.cpp
dom/media/gmp/PGMP.ipdl
dom/media/gmp/PGMPService.ipdl
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -601,20 +601,31 @@ GMPChild::GetGMPStorage()
     if (!sc) {
       return nullptr;
     }
     mStorage = static_cast<GMPStorageChild*>(sc);
   }
   return mStorage;
 }
 
+static MOZ_NEVER_INLINE void
+CrashForApiTimeout()
+{
+  // Never inline so that crash reports are distinctive.
+  MOZ_CRASH("Bug 1209385; GMP API actor failed to respond.");
+}
+
 bool
-GMPChild::RecvCrashPluginNow()
+GMPChild::RecvCrashPluginNow(const GMPCrashReason& aReason)
 {
-  MOZ_CRASH();
+  if (aReason == kGmpApiTimeout) {
+    CrashForApiTimeout();
+  } else {
+    MOZ_CRASH();
+  }
   return true;
 }
 
 bool
 GMPChild::RecvBeginAsyncShutdown()
 {
   LOGD("%s AsyncShutdown=%d", __FUNCTION__, mAsyncShutdown!=nullptr);
 
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -66,17 +66,17 @@ private:
 
   virtual PGMPStorageChild* AllocPGMPStorageChild() override;
   virtual bool DeallocPGMPStorageChild(PGMPStorageChild* aActor) override;
 
   virtual PGMPContentChild* AllocPGMPContentChild(Transport* aTransport,
                                                   ProcessId aOtherPid) override;
   void GMPContentChildActorDestroy(GMPContentChild* aGMPContentChild);
 
-  virtual bool RecvCrashPluginNow() override;
+  virtual bool RecvCrashPluginNow(const GMPCrashReason& aReason) override;
   virtual bool RecvBeginAsyncShutdown() override;
   virtual bool RecvCloseActive() override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
   GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI);
 
--- a/dom/media/gmp/GMPContentParent.cpp
+++ b/dom/media/gmp/GMPContentParent.cpp
@@ -114,16 +114,28 @@ GMPContentParent::DecryptorDestroyed(GMP
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   MOZ_ALWAYS_TRUE(mDecryptors.RemoveElement(aSession));
   CloseIfUnused();
 }
 
 void
+GMPContentParent::CrashPluginNow(GMPCrashReason aReason)
+{
+  if (mParent) {
+    mParent->Crash(aReason);
+  } else {
+    nsRefPtr<GeckoMediaPluginServiceChild> gmp(
+      GeckoMediaPluginServiceChild::GetSingleton());
+    gmp->CrashPluginNow(mPluginId, aReason);
+  }
+}
+
+void
 GMPContentParent::CloseIfUnused()
 {
   if (mAudioDecoders.IsEmpty() &&
       mDecryptors.IsEmpty() &&
       mVideoDecoders.IsEmpty() &&
       mVideoEncoders.IsEmpty()) {
     nsRefPtr<GMPContentParent> toClose;
     if (mParent) {
--- a/dom/media/gmp/GMPContentParent.h
+++ b/dom/media/gmp/GMPContentParent.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GMPContentParent_h_
 #define GMPContentParent_h_
 
 #include "mozilla/gmp/PGMPContentParent.h"
 #include "GMPSharedMemManager.h"
 #include "nsISupportsImpl.h"
+#include "GMPUtils.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPAudioDecoderParent;
 class GMPDecryptorParent;
 class GMPParent;
 class GMPVideoDecoderParent;
@@ -56,16 +57,18 @@ public:
   {
     mPluginId = aPluginId;
   }
   const uint32_t GetPluginId()
   {
     return mPluginId;
   }
 
+  void CrashPluginNow(GMPCrashReason aReason);
+
 private:
   ~GMPContentParent();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() override;
   virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) override;
 
--- a/dom/media/gmp/GMPMessageUtils.h
+++ b/dom/media/gmp/GMPMessageUtils.h
@@ -6,16 +6,18 @@
 #ifndef GMPMessageUtils_h_
 #define GMPMessageUtils_h_
 
 #include "gmp-video-codec.h"
 #include "gmp-video-frame-encoded.h"
 #include "gmp-audio-codec.h"
 #include "gmp-decryption.h"
 
+#include "GMPUtils.h"
+
 namespace IPC {
 
 template <>
 struct ParamTraits<GMPErr>
 : public ContiguousEnumSerializer<GMPErr,
                                   GMPNoErr,
                                   GMPLastErr>
 {};
@@ -242,11 +244,18 @@ struct ParamTraits<GMPVideoCodec>
     }
     aLog->append(StringPrintf(L"[%s, %u, %u]",
                               codecName,
                               aParam.mWidth,
                               aParam.mHeight));
   }
 };
 
+template <>
+struct ParamTraits<mozilla::GMPCrashReason>
+  : public ContiguousEnumSerializer<mozilla::GMPCrashReason,
+  mozilla::kPrefChange,
+  mozilla::kInvalid>
+{};
+
 } // namespace IPC
 
 #endif // GMPMessageUtils_h_
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -114,20 +114,20 @@ GMPParent::Init(GeckoMediaPluginServiceP
 
   MOZ_ASSERT(parentLeafName.Length() > 4);
   mName = Substring(parentLeafName, 4);
 
   return ReadGMPMetaData();
 }
 
 void
-GMPParent::Crash()
+GMPParent::Crash(GMPCrashReason aReason)
 {
   if (mState != GMPStateNotLoaded) {
-    unused << SendCrashPluginNow();
+    unused << SendCrashPluginNow(aReason);
   }
 }
 
 nsresult
 GMPParent::LoadProcess()
 {
   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -17,16 +17,17 @@
 #include "mozilla/gmp/PGMPParent.h"
 #include "nsCOMPtr.h"
 #include "nscore.h"
 #include "nsISupports.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIFile.h"
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "GMPUtils.h"
 
 class nsIThread;
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 
 namespace mozilla {
 namespace dom {
@@ -74,17 +75,17 @@ class GMPParent final : public PGMPParen
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent)
 
   GMPParent();
 
   nsresult Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
   nsresult CloneFrom(const GMPParent* aOther);
 
-  void Crash();
+  void Crash(GMPCrashReason aReason);
 
   nsresult LoadProcess();
 
   // Called internally to close this if we don't need it
   void CloseIfUnused();
 
   // Notify all active de/encoders that we are closing, either because of
   // normal shutdown or unexpected shutdown/crash.
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -210,16 +210,48 @@ GeckoMediaPluginServiceChild::UpdateTria
     uint32_t mState;
   };
 
   UniquePtr<GetServiceChildCallback> callback(new Callback(aKeySystem, aState));
   GetServiceChild(Move(callback));
   return NS_OK;
 }
 
+void
+GeckoMediaPluginServiceChild::CrashPluginNow(uint32_t aPluginId, GMPCrashReason aReason)
+{
+  if (NS_GetCurrentThread() != mGMPThread) {
+    mGMPThread->Dispatch(NS_NewRunnableMethodWithArgs<uint32_t, GMPCrashReason>(
+      this, &GeckoMediaPluginServiceChild::CrashPluginNow,
+      aPluginId, aReason), NS_DISPATCH_NORMAL);
+    return;
+  }
+
+  class Callback : public GetServiceChildCallback
+  {
+  public:
+    Callback(uint32_t aPluginId, GMPCrashReason aReason)
+      : mPluginId(aPluginId)
+      , mReason(aReason)
+    { }
+
+    virtual void Done(GMPServiceChild* aService) override
+    {
+      aService->SendCrashPluginNow(mPluginId, mReason);
+    }
+
+  private:
+    uint32_t mPluginId;
+    GMPCrashReason mReason;
+  };
+
+  UniquePtr<GetServiceChildCallback> callback(new Callback(aPluginId, aReason));
+  GetServiceChild(Move(callback));
+}
+
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::Observe(nsISupports* aSubject,
                                       const char* aTopic,
                                       const char16_t* aSomeData)
 {
   LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, aTopic));
   if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
     if (mServiceChild) {
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -6,16 +6,17 @@
 #ifndef GMPServiceChild_h_
 #define GMPServiceChild_h_
 
 #include "GMPService.h"
 #include "base/process.h"
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/gmp/PGMPServiceChild.h"
 #include "nsRefPtrHashtable.h"
+#include "GMPUtils.h"
 
 namespace mozilla {
 namespace gmp {
 
 #define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
 
 class GMPContentParent;
 class GMPServiceChild;
@@ -47,16 +48,18 @@ public:
                                     nsACString& aOutVersion) override;
   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
                        bool aInPrivateBrowsingMode,
                        UniquePtr<GetNodeIdCallback>&& aCallback) override;
   NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
                                     uint32_t aState) override;
 
+  void CrashPluginNow(uint32_t aPluginId, GMPCrashReason aReason);
+
   NS_DECL_NSIOBSERVER
 
   void SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild);
 
   void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
 
 protected:
   virtual void InitializePlugins() override
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -506,17 +506,17 @@ GeckoMediaPluginServiceParent::UnloadPlu
 void
 GeckoMediaPluginServiceParent::CrashPlugins()
 {
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MutexAutoLock lock(mMutex);
   for (size_t i = 0; i < mPlugins.Length(); i++) {
-    mPlugins[i]->Crash();
+    mPlugins[i]->Crash(kPrefChange);
   }
 }
 
 void
 GeckoMediaPluginServiceParent::LoadFromEnvironment()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
@@ -1216,16 +1216,29 @@ GeckoMediaPluginServiceParent::UpdateTri
   }));
 
   return NS_OK;
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
+void
+GeckoMediaPluginServiceParent::CrashPluginNow(uint32_t aPluginId, GMPCrashReason aReason)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+  MutexAutoLock lock(mMutex);
+  LOGD(("%s::%s(%u, %u)", __CLASS__, __FUNCTION__, aPluginId, aReason));
+  for (const auto& plugin : mPlugins) {
+    if (plugin->GetPluginId() == aPluginId) {
+      plugin->Crash(aReason);
+    }
+  }
+}
+
 static bool
 ExtractHostName(const nsACString& aOrigin, nsACString& aOutData)
 {
   nsCString str;
   str.Assign(aOrigin);
   int begin = str.Find("://");
   // The scheme is missing!
   if (begin == -1) {
@@ -1551,16 +1564,24 @@ GeckoMediaPluginServiceParent::ClearStor
 
 GMPServiceParent::~GMPServiceParent()
 {
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new DeleteTask<Transport>(GetTransport()));
 }
 
 bool
+GMPServiceParent::RecvCrashPluginNow(const uint32_t& aPluginId,
+                                     const GMPCrashReason& aReason)
+{
+  mService->CrashPluginNow(aPluginId, aReason);
+  return true;
+}
+
+bool
 GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId,
                               const nsCString& aAPI,
                               nsTArray<nsCString>&& aTags,
                               nsTArray<ProcessId>&& aAlreadyBridgedTo,
                               ProcessId* aId,
                               nsCString* aDisplayName,
                               uint32_t* aPluginId)
 {
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -8,16 +8,17 @@
 
 #include "GMPService.h"
 #include "mozilla/gmp/PGMPServiceParent.h"
 #include "mozIGeckoMediaPluginChromeService.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/Atomics.h"
 #include "nsThreadUtils.h"
+#include "GMPUtils.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
@@ -52,16 +53,18 @@ public:
   void AsyncShutdownNeeded(GMPParent* aParent);
   void AsyncShutdownComplete(GMPParent* aParent);
 
   int32_t AsyncShutdownTimeoutMs();
 #ifdef MOZ_CRASHREPORTER
   void SetAsyncShutdownPluginState(GMPParent* aGMPParent, char aId, const nsCString& aState);
 #endif // MOZ_CRASHREPORTER
 
+  void CrashPluginNow(uint32_t aPluginId, GMPCrashReason aReason);
+
 private:
   friend class GMPServiceParent;
 
   virtual ~GeckoMediaPluginServiceParent();
 
   void ClearStorage();
 
   GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
@@ -218,16 +221,18 @@ public:
                                 const bool& aInPrivateBrowsing,
                                 nsCString* aID) override;
   static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
                                             nsTArray<nsCString>&& aTags,
                                             bool* aHasPlugin,
                                             nsCString* aVersion);
   virtual bool RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
                                              const uint32_t& aState) override;
+  virtual bool RecvCrashPluginNow(const uint32_t& aPluginId,
+                                  const GMPCrashReason& aReason) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   static PGMPServiceParent* Create(Transport* aTransport, ProcessId aOtherPid);
 
 private:
   nsRefPtr<GeckoMediaPluginServiceParent> mService;
 };
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -32,11 +32,17 @@ bool EMEVoucherFileExists();
 void
 SplitAt(const char* aDelims,
         const nsACString& aInput,
         nsTArray<nsCString>& aOutTokens);
 
 nsCString
 ToBase64(const nsTArray<uint8_t>& aBytes);
 
+enum GMPCrashReason {
+  kPrefChange, // media.gmp.plugin.crash has been toggled.
+  kGmpApiTimeout, // Some API did not respond.
+  kInvalid,
+};
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -183,16 +183,17 @@ GMPVideoDecoderParent::Reset()
   mIsAwaitingResetComplete = true;
 
   nsRefPtr<GMPVideoDecoderParent> self(this);
   nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([self]() -> void
   {
     LOGD(("GMPVideoDecoderParent[%p]::ResetCompleteTimeout() timed out waiting for ResetComplete", self.get()));
     self->mResetCompleteTimeout = nullptr;
     LogToBrowserConsole(NS_LITERAL_STRING("GMPVideoDecoderParent timed out waiting for ResetComplete()"));
+    self->mPlugin->CrashPluginNow(kGmpApiTimeout);
   });
   CancelResetCompleteTimeout();
   mResetCompleteTimeout = SimpleTimer::Create(task, 5000, mPlugin->GMPThread());
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
--- a/dom/media/gmp/PGMP.ipdl
+++ b/dom/media/gmp/PGMP.ipdl
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PCrashReporter;
 include protocol PGMPContent;
 include protocol PGMPTimer;
 include protocol PGMPStorage;
 
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
+using mozilla::GMPCrashReason from "GMPUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 intr protocol PGMP
 {
   parent opens PGMPContent;
 
@@ -28,16 +29,16 @@ parent:
 
   async PGMPContentChildDestroyed();
 
   async AsyncShutdownComplete();
   async AsyncShutdownRequired();
 
 child:
   async BeginAsyncShutdown();
-  async CrashPluginNow();
+  async CrashPluginNow(GMPCrashReason aReason);
   intr StartPlugin();
   async SetNodeId(nsCString nodeId);
   async CloseActive();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -1,16 +1,17 @@
 /* -*- 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/. */
 
 include protocol PGMP;
 
 using base::ProcessId from "base/process.h";
+using mozilla::GMPCrashReason from "GMPUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 sync protocol PGMPService
 {
   parent spawns PGMP as child;
 
@@ -18,12 +19,14 @@ parent:
   sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags,
                ProcessId[] alreadyBridgedTo)
     returns (ProcessId id, nsCString displayName, uint32_t pluginId);
   sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
                     bool inPrivateBrowsing)
     returns (nsCString id);
 
   async UpdateGMPTrialCreateState(nsString keySystem, uint32_t status);
+
+  async CrashPluginNow(uint32_t pluginId, GMPCrashReason aReason);
 };
 
 } // namespace gmp
 } // namespace mozilla