Bug 1433309 - Annotate createMediaKeys promise reject with whether failure occurred due to pending shutdown. r=gerald. a=lizzard
authorChris Pearce <cpearce@mozilla.com>
Fri, 26 Jan 2018 12:20:35 +1300
changeset 454680 08e9d3a3a6b13997cac9fa92231f1b17cb00c527
parent 454679 e87e706def11e307c8d2bcb2ba3e23d7c3eead9f
child 454681 d15af2a5aa189688f925b8d0f0cfff767ca708c6
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald, lizzard
bugs1433309
milestone59.0
Bug 1433309 - Annotate createMediaKeys promise reject with whether failure occurred due to pending shutdown. r=gerald. a=lizzard Around every Firefox update, Netflix see a spike in MediaKeySystemAccess.createMediaKeys() promise rejects. I am wondering whether this is caused by the browser restarting to apply a Firefox update while Netflix's player is loading. So add more detail to the promise reject as to the state of the system, to try to validate that theory. MozReview-Commit-ID: 4IDPsFwKCtq
dom/media/gmp/ChromiumCDMParent.cpp
dom/media/gmp/ChromiumCDMParent.h
dom/media/gmp/ChromiumCDMProxy.cpp
dom/media/gmp/GMPService.cpp
dom/media/gmp/GMPService.h
dom/media/gmp/GMPServiceChild.cpp
dom/media/gmp/GMPServiceParent.cpp
dom/media/gtest/TestCDMStorage.cpp
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -7,16 +7,17 @@
 
 #include "ChromiumCDMCallback.h"
 #include "ChromiumCDMCallbackProxy.h"
 #include "ChromiumCDMProxy.h"
 #include "content_decryption_module.h"
 #include "GMPContentChild.h"
 #include "GMPContentParent.h"
 #include "GMPLog.h"
+#include "GMPService.h"
 #include "GMPUtils.h"
 #include "MediaPrefs.h"
 #include "mozilla/dom/MediaKeyMessageEventBinding.h"
 #include "mozilla/gmp/GMPTypes.h"
 #include "mozilla/Unused.h"
 #include "AnnexB.h"
 #include "H264.h"
 
@@ -39,27 +40,52 @@ ChromiumCDMParent::ChromiumCDMParent(GMP
     aContentParent,
     aPluginId);
 }
 
 bool
 ChromiumCDMParent::Init(ChromiumCDMCallback* aCDMCallback,
                         bool aAllowDistinctiveIdentifier,
                         bool aAllowPersistentState,
-                        nsIEventTarget* aMainThread)
+                        nsIEventTarget* aMainThread,
+                        nsCString& aOutFailureReason)
 {
-  GMP_LOG("ChromiumCDMParent::Init(this=%p)", this);
+  GMP_LOG("ChromiumCDMParent::Init(this=%p) shutdown=%d abormalShutdown=%d "
+          "actorDestroyed=%d",
+          this,
+          mIsShutdown,
+          mAbnormalShutdown,
+          mActorDestroyed);
   if (!aCDMCallback || !aMainThread) {
+    aOutFailureReason = nsPrintfCString("ChromiumCDMParent::Init() failed "
+                                        "nullCallback=%d nullMainThread=%d",
+                                        !aCDMCallback,
+                                        !aMainThread);
     GMP_LOG("ChromiumCDMParent::Init(this=%p) failure since aCDMCallback(%p) or"
             " aMainThread(%p) is nullptr", this, aCDMCallback, aMainThread);
     return false;
   }
   mCDMCallback = aCDMCallback;
   mMainThread = aMainThread;
-  return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState);
+
+  if (SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState)) {
+    return true;
+  }
+
+  RefPtr<gmp::GeckoMediaPluginService> service =
+    gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+  bool xpcomWillShutdown = service && service->XPCOMWillShutdownReceived();
+  aOutFailureReason = nsPrintfCString(
+    "ChromiumCDMParent::Init() failed "
+    "shutdown=%d cdmCrash=%d actorDestroyed=%d browserShutdown=%d",
+    mIsShutdown,
+    mAbnormalShutdown,
+    mActorDestroyed,
+    xpcomWillShutdown);
+  return false;
 }
 
 void
 ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken,
                                  uint32_t aSessionType,
                                  uint32_t aInitDataType,
                                  uint32_t aPromiseId,
                                  const nsTArray<uint8_t>& aInitData)
@@ -860,21 +886,21 @@ ChromiumCDMParent::ActorDestroy(ActorDes
     Shutdown();
   }
   MOZ_ASSERT(mIsShutdown);
   RefPtr<ChromiumCDMParent> kungFuDeathGrip(this);
   if (mContentParent) {
     mContentParent->ChromiumCDMDestroyed(this);
     mContentParent = nullptr;
   }
-  bool abnormalShutdown = (aWhy == AbnormalShutdown);
-  if (abnormalShutdown && callback) {
+  mAbnormalShutdown = (aWhy == AbnormalShutdown);
+  if (mAbnormalShutdown && callback) {
     callback->Terminated();
   }
-  MaybeDisconnect(abnormalShutdown);
+  MaybeDisconnect(mAbnormalShutdown);
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 ChromiumCDMParent::InitializeVideoDecoder(
   const gmp::CDMVideoDecoderConfig& aConfig,
   const VideoInfo& aInfo,
   RefPtr<layers::ImageContainer> aImageContainer)
 {
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -38,17 +38,18 @@ public:
 
   ChromiumCDMParent(GMPContentParent* aContentParent, uint32_t aPluginId);
 
   uint32_t PluginId() const { return mPluginId; }
 
   bool Init(ChromiumCDMCallback* aCDMCallback,
             bool aAllowDistinctiveIdentifier,
             bool aAllowPersistentState,
-            nsIEventTarget* aMainThread);
+            nsIEventTarget* aMainThread,
+            nsCString& aOutFailureReason);
 
   void CreateSession(uint32_t aCreateSessionToken,
                      uint32_t aSessionType,
                      uint32_t aInitDataType,
                      uint32_t aPromiseId,
                      const nsTArray<uint8_t>& aInitData);
 
   void LoadSession(uint32_t aPromiseId,
@@ -177,16 +178,17 @@ protected:
   // frames from the CDM to Gecko.
   uint32_t mVideoShmemsActive = 0;
   // Maximum number of shmems to use to return decoded video frames.
   uint32_t mVideoShmemLimit;
 
   bool mIsShutdown = false;
   bool mVideoDecoderInitialized = false;
   bool mActorDestroyed = false;
+  bool mAbnormalShutdown = false;
 
   // The H.264 decoder in Widevine CDM versions 970 and later output in decode
   // order rather than presentation order, so we reorder in presentation order
   // before presenting. mMaxRefFrames is non-zero if we have an initialized
   // decoder and we are decoding H.264. If so, it stores the maximum length of
   // the reorder queue that we need. Note we may have multiple decoders for the
   // life time of this object, but never more than one active at once.
   uint32_t mMaxRefFrames = 0;
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -96,23 +96,23 @@ ChromiumCDMProxy::Init(PromiseId aPromis
       RefPtr<gmp::GetCDMParentPromise> promise =
         service->GetCDM(nodeId, { keySystem }, helper);
       promise->Then(
         thread,
         __func__,
         [self, aPromiseId](RefPtr<gmp::ChromiumCDMParent> cdm) {
           self->mCallback =
             MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
+          nsCString failureReason;
           if (!cdm->Init(self->mCallback.get(),
                          self->mDistinctiveIdentifierRequired,
                          self->mPersistentStateRequired,
-                         self->mMainThread)) {
-            self->RejectPromise(aPromiseId,
-                                NS_ERROR_FAILURE,
-                                NS_LITERAL_CSTRING("GetCDM failed due to CDM initialization failure."));
+                         self->mMainThread,
+                         failureReason)) {
+            self->RejectPromise(aPromiseId, NS_ERROR_FAILURE, failureReason);
             return;
           }
           {
             MutexAutoLock lock(self->mCDMMutex);
             self->mCDM = cdm;
           }
           self->OnCDMCreated(aPromiseId);
         },
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -145,16 +145,17 @@ GeckoMediaPluginService::GetGeckoMediaPl
 }
 
 NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
 
 GeckoMediaPluginService::GeckoMediaPluginService()
   : mMutex("GeckoMediaPluginService::mMutex")
   , mGMPThreadShutdown(false)
   , mShuttingDownOnGMPThread(false)
+  , mXPCOMWillShutdown(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
   if (appInfo) {
     nsAutoCString version;
     nsAutoCString buildID;
     if (NS_SUCCEEDED(appInfo->GetVersion(version)) &&
@@ -219,16 +220,18 @@ GeckoMediaPluginService::RunPluginCrashC
 nsresult
 GeckoMediaPluginService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
   MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false));
+  MOZ_ALWAYS_SUCCEEDS(
+    obsService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false));
 
   // Kick off scanning for plugins
   nsCOMPtr<nsIThread> thread;
   return GetThread(getter_AddRefs(thread));
 }
 
 RefPtr<GetCDMParentPromise>
 GeckoMediaPluginService::GetCDM(const NodeId& aNodeId,
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -5,16 +5,17 @@
 
 #ifndef GMPService_h_
 #define GMPService_h_
 
 #include "nsString.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "nsIObserver.h"
 #include "nsTArray.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Monitor.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsIDocument.h"
 #include "nsIWeakReference.h"
@@ -102,16 +103,18 @@ public:
   NS_IMETHOD RunPluginCrashCallbacks(uint32_t aPluginId,
                                      const nsACString& aPluginName) override;
 
   RefPtr<AbstractThread> GetAbstractGMPThread();
 
   void ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper);
   void DisconnectCrashHelper(GMPCrashHelper* aHelper);
 
+  bool XPCOMWillShutdownReceived() const { return mXPCOMWillShutdown; }
+
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
   virtual void InitializePlugins(AbstractThread* aAbstractGMPThread) = 0;
 
   virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
     GMPCrashHelper* aHelper,
@@ -130,16 +133,17 @@ protected:
   void ShutdownGMPThread();
 
   Mutex mMutex; // Protects mGMPThread, mAbstractGMPThread, mPluginCrashHelpers,
                 // mGMPThreadShutdown and some members in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
   RefPtr<AbstractThread> mAbstractGMPThread;
   bool mGMPThreadShutdown;
   bool mShuttingDownOnGMPThread;
+  Atomic<bool> mXPCOMWillShutdown;
 
   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
@@ -382,16 +382,18 @@ GeckoMediaPluginServiceChild::Observe(ns
   if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
     if (mServiceChild) {
       mozilla::SyncRunnable::DispatchToThread(mGMPThread,
                                               WrapRunnable(mServiceChild.get(),
                                                            &PGMPServiceChild::Close));
       mServiceChild = nullptr;
     }
     ShutdownGMPThread();
+  } else if (!strcmp(NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, aTopic)) {
+    mXPCOMWillShutdown = true;
   }
 
   return NS_OK;
 }
 
 RefPtr<GeckoMediaPluginServiceChild::GetServiceChildPromise>
 GeckoMediaPluginServiceChild::GetServiceChild()
 {
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -304,16 +304,18 @@ GeckoMediaPluginServiceParent::Observe(n
       // GMP thread has already shutdown.
       MOZ_ASSERT(mPlugins.IsEmpty());
       mWaitingForPluginsSyncShutdown = false;
     }
 
   } else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
     MOZ_ASSERT(mShuttingDown);
     ShutdownGMPThread();
+  } else if (!strcmp(NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, aTopic)) {
+    mXPCOMWillShutdown = true;
   } else if (!strcmp("last-pb-context-exited", aTopic)) {
     // When Private Browsing mode exits, all we need to do is clear
     // mTempNodeIds. This drops all the node ids we've cached in memory
     // for PB origin-pairs. If we try to open an origin-pair for non-PB
     // mode, we'll get the NodeId salt stored on-disk, and if we try to
     // open a PB mode origin-pair, we'll re-generate new salt.
     mTempNodeIds.Clear();
   } else if (!strcmp("browser:purge-session-history", aTopic)) {
--- a/dom/media/gtest/TestCDMStorage.cpp
+++ b/dom/media/gtest/TestCDMStorage.cpp
@@ -460,17 +460,22 @@ class CDMStorageTest
           service->GetCDM(aNodeId, Move(tags), nullptr);
     auto thread = GetAbstractGMPThread();
     promise->Then(thread,
                   __func__,
                   [self, aUpdates](RefPtr<gmp::ChromiumCDMParent> cdm) {
                     self->mCDM = cdm;
                     EXPECT_TRUE(!!self->mCDM);
                     self->mCallback.reset(new CallbackProxy(self));
-                    self->mCDM->Init(self->mCallback.get(), false, true, GetMainThreadEventTarget());
+                    nsCString failureReason;
+                    self->mCDM->Init(self->mCallback.get(),
+                                     false,
+                                     true,
+                                     GetMainThreadEventTarget(),
+                                     failureReason);
 
                     for (auto& update : aUpdates) {
                       self->Update(update);
                     }
                   },
                   [](MediaResult rv) { EXPECT_TRUE(false); });
   }