Bug 1315510 - Automatically recreate VideoDecoderManager if the GPU process crashes. r=dvander
☠☠ backed out by 3e74d390dea4 ☠ ☠
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 08 Nov 2016 15:21:35 +1300
changeset 348295 d537051ade6a33a18905cb6e901f07e60dbda168
parent 348294 01800003ec3b043995e4ec1daaaf827e7ee2bbee
child 348296 9ee070ee6dad5d0717f1da88bb94bfa25798c8f8
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs1315510
milestone52.0a1
Bug 1315510 - Automatically recreate VideoDecoderManager if the GPU process crashes. r=dvander
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/media/ipc/RemoteVideoDecoder.cpp
dom/media/ipc/VideoDecoderChild.cpp
dom/media/ipc/VideoDecoderManagerChild.cpp
dom/media/ipc/VideoDecoderManagerChild.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
layout/build/nsLayoutStatics.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/Unused.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
@@ -1176,34 +1177,37 @@ ContentChild::RecvGMPsChanged(nsTArray<G
 {
   GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities));
   return true;
 }
 
 bool
 ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
                                 Endpoint<PImageBridgeChild>&& aImageBridge,
-                                Endpoint<PVRManagerChild>&& aVRBridge)
+                                Endpoint<PVRManagerChild>&& aVRBridge,
+                                Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
 {
   if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) {
     return false;
   }
   if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) {
     return false;
   }
   if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) {
     return false;
   }
+  VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
   return true;
 }
 
 bool
 ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
                                   Endpoint<PImageBridgeChild>&& aImageBridge,
-                                  Endpoint<PVRManagerChild>&& aVRBridge)
+                                  Endpoint<PVRManagerChild>&& aVRBridge,
+                                  Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
 {
   nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
 
   // Zap all the old layer managers we have lying around.
   for (const auto& tabChild : tabs) {
     if (tabChild->LayersId()) {
       tabChild->InvalidateLayers();
     }
@@ -1221,16 +1225,18 @@ ContentChild::RecvReinitRendering(Endpoi
   }
 
   // Establish new PLayerTransactions.
   for (const auto& tabChild : tabs) {
     if (tabChild->LayersId()) {
       tabChild->ReinitRendering();
     }
   }
+
+  VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
   return true;
 }
 
 PBackgroundChild*
 ContentChild::AllocPBackgroundChild(Transport* aTransport,
                                     ProcessId aOtherProcess)
 {
   return BackgroundChild::Alloc(aTransport, aOtherProcess);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -164,23 +164,25 @@ public:
 
   bool
   RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override;
 
   bool
   RecvInitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
-    Endpoint<PVRManagerChild>&& aVRBridge) override;
+    Endpoint<PVRManagerChild>&& aVRBridge,
+    Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override;
 
   bool
   RecvReinitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
-    Endpoint<PVRManagerChild>&& aVRBridge) override;
+    Endpoint<PVRManagerChild>&& aVRBridge,
+    Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override;
 
   PProcessHangMonitorChild*
   AllocPProcessHangMonitorChild(Transport* aTransport,
                                 ProcessId aOtherProcess) override;
 
   virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
 
   PBackgroundChild*
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1022,23 +1022,16 @@ ContentParent::RecvFindPlugins(const uin
                                nsresult* aRv,
                                nsTArray<PluginTag>* aPlugins,
                                uint32_t* aNewPluginEpoch)
 {
   *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
   return true;
 }
 
-bool
-ContentParent::RecvInitVideoDecoderManager(Endpoint<PVideoDecoderManagerChild>* aEndpoint)
-{
-  GPUProcessManager::Get()->CreateContentVideoDecoderManager(OtherPid(), aEndpoint);
-  return true;
-}
-
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent,
                                   bool aFreshProcess)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
 
@@ -2222,28 +2215,31 @@ ContentParent::InitInternal(ProcessPrior
     // on demand.)
     bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop();
     if (useOffMainThreadCompositing) {
       GPUProcessManager* gpm = GPUProcessManager::Get();
 
       Endpoint<PCompositorBridgeChild> compositor;
       Endpoint<PImageBridgeChild> imageBridge;
       Endpoint<PVRManagerChild> vrBridge;
+      Endpoint<PVideoDecoderManagerChild> videoManager;
 
       DebugOnly<bool> opened = gpm->CreateContentBridges(
         OtherPid(),
         &compositor,
         &imageBridge,
-        &vrBridge);
+        &vrBridge,
+        &videoManager);
       MOZ_ASSERT(opened);
 
       Unused << SendInitRendering(
         Move(compositor),
         Move(imageBridge),
-        Move(vrBridge));
+        Move(vrBridge),
+        Move(videoManager));
 
       gpm->AddListener(this);
     }
   }
 
   if (gAppData) {
     // Sending all information to content process.
     Unused << SendAppInit();
@@ -2378,28 +2374,31 @@ ContentParent::RecvGetGfxVars(Infallible
 void
 ContentParent::OnCompositorUnexpectedShutdown()
 {
   GPUProcessManager* gpm = GPUProcessManager::Get();
 
   Endpoint<PCompositorBridgeChild> compositor;
   Endpoint<PImageBridgeChild> imageBridge;
   Endpoint<PVRManagerChild> vrBridge;
+  Endpoint<PVideoDecoderManagerChild> videoManager;
 
   DebugOnly<bool> opened = gpm->CreateContentBridges(
     OtherPid(),
     &compositor,
     &imageBridge,
-    &vrBridge);
+    &vrBridge,
+    &videoManager);
   MOZ_ASSERT(opened);
 
   Unused << SendReinitRendering(
     Move(compositor),
     Move(imageBridge),
-    Move(vrBridge));
+    Move(vrBridge),
+    Move(videoManager));
 }
 
 void
 ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
 {
   if (!mIPCOpen) {
     return;
   }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -257,18 +257,16 @@ public:
   virtual bool RecvGetBlocklistState(const uint32_t& aPluginId,
                                      uint32_t* aIsBlocklisted) override;
 
   virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
                                nsresult* aRv,
                                nsTArray<PluginTag>* aPlugins,
                                uint32_t* aNewPluginEpoch) override;
 
-  virtual bool RecvInitVideoDecoderManager(Endpoint<PVideoDecoderManagerChild>* endpoint) override;
-
   virtual bool RecvUngrabPointer(const uint32_t& aTime) override;
 
   virtual bool RecvRemovePermission(const IPC::Principal& aPrincipal,
                                     const nsCString& aPermissionType,
                                     nsresult* aRv) override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -424,25 +424,27 @@ both:
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
     // Give the content process its endpoints to the compositor.
     async InitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> imageBridge,
-      Endpoint<PVRManagerChild> vr);
+      Endpoint<PVRManagerChild> vr,
+      Endpoint<PVideoDecoderManagerChild> video);
 
     // Re-create the rendering stack using the given endpoints. This is sent
     // after the compositor process has crashed. The new endpoints may be to a
     // newly launched GPU process, or the compositor thread of the UI process.
     async ReinitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> bridge,
-      Endpoint<PVRManagerChild> vr);
+      Endpoint<PVRManagerChild> vr,
+      Endpoint<PVideoDecoderManagerChild> video);
 
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
@@ -737,18 +739,16 @@ parent:
 
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
     async PDeviceStorageRequest(DeviceStorageParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
-    sync InitVideoDecoderManager() returns (Endpoint<PVideoDecoderManagerChild> endpoint);
-
     /**
      * Is this token compatible with the provided version?
      *
      * |version| The offered version to test
      * Returns |True| if the offered version is compatible
      */
     sync NSSU2FTokenIsCompatibleVersion(nsString version)
         returns (bool result);
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -4,16 +4,17 @@
  * 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 "RemoteVideoDecoder.h"
 #include "VideoDecoderChild.h"
 #include "VideoDecoderManagerChild.h"
 #include "mozilla/layers/TextureClient.h"
 #include "base/thread.h"
 #include "MediaInfo.h"
+#include "MediaPrefs.h"
 #include "ImageContainer.h"
 
 namespace mozilla {
 namespace dom {
 
 using base::Thread;
 using namespace ipc;
 using namespace layers;
@@ -142,17 +143,18 @@ PlatformDecoderModule::ConversionRequire
 RemoteDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   return mWrapped->DecoderNeedsConversion(aConfig);
 }
 
 already_AddRefed<MediaDataDecoder>
 RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
 {
-  if (!aParams.mKnowsCompositor ||
+  if (!MediaPrefs::PDMUseGPUDecoder() ||
+      !aParams.mKnowsCompositor ||
       aParams.mKnowsCompositor->GetTextureFactoryIdentifier().mParentProcessType != GeckoProcessType_GPU) {
     return nullptr;
   }
 
   MediaDataDecoderCallback* callback = aParams.mCallback;
   MOZ_ASSERT(callback->OnReaderTaskQueue());
   RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder(callback);
 
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -98,34 +98,45 @@ VideoDecoderChild::RecvInitFailed(const 
   mInitPromise.Reject(aReason, __func__);
   return true;
 }
 
 void
 VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (aWhy == AbnormalShutdown) {
-    if (mInitialized) {
-      mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
-    } else {
-      mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
-    }
+    // Defer reporting an error until we've recreated the manager so that
+    // it'll be safe for MediaFormatReader to recreate decoders
+    RefPtr<VideoDecoderChild> ref = this;
+    GetManager()->RunWhenRecreated(NS_NewRunnableFunction([=]() {
+      if (ref->mInitialized) {
+        ref->mCallback->Error(NS_ERROR_DOM_MEDIA_DECODE_ERR);
+      } else {
+        ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
+      }
+    }));
   }
   mCanSend = false;
 }
 
 void
 VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback,
                             const VideoInfo& aVideoInfo,
                             layers::KnowsCompositor* aKnowsCompositor)
 {
   RefPtr<VideoDecoderManagerChild> manager = VideoDecoderManagerChild::GetSingleton();
-  if (!manager) {
+  // If the manager isn't available, then don't initialize mIPDLSelfRef and leave
+  // us in an error state. We'll then immediately reject the promise when Init()
+  // is called and the caller can try again. Hopefully by then the new manager is
+  // ready, or we've notified the caller of it being no longer available.
+  // If not, then the cycle repeats until we're ready.
+  if (!manager || !manager->CanSend()) {
     return;
   }
+
   mIPDLSelfRef = this;
   mCallback = aCallback;
   mVideoInfo = aVideoInfo;
   mKnowsCompositor = aKnowsCompositor;
   if (manager->SendPVideoDecoderConstructor(this)) {
     mCanSend = true;
   }
 }
@@ -145,96 +156,99 @@ VideoDecoderChild::IPDLActorDestroyed()
 }
 
 // MediaDataDecoder methods
 
 RefPtr<MediaDataDecoder::InitPromise>
 VideoDecoderChild::Init()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier())) {
+
+  if (!mIPDLSelfRef) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
-      NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+      NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
+  }
+  // If we failed to send this, then we'll still resolve the Init promise
+  // as ActorDestroy handles it.
+  if (mCanSend) {
+    SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier());
   }
   return mInitPromise.Ensure(__func__);
 }
 
 void
 VideoDecoderChild::Input(MediaRawData* aSample)
 {
   AssertOnManagerThread();
   if (!mCanSend) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
     return;
   }
 
   // TODO: It would be nice to add an allocator method to
   // MediaDataDecoder so that the demuxer could write directly
   // into shmem rather than requiring a copy here.
   Shmem buffer;
   if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+    mCallback->Error(NS_ERROR_DOM_MEDIA_DECODE_ERR);
     return;
   }
 
   memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
 
   MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset,
                                         aSample->mTime,
                                         aSample->mTimecode,
                                         aSample->mDuration,
                                         aSample->mFrames,
                                         aSample->mKeyframe),
                           buffer);
-  if (!SendInput(sample)) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
-  }
+  SendInput(sample);
 }
 
 void
 VideoDecoderChild::Flush()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendFlush()) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendFlush();
   }
 }
 
 void
 VideoDecoderChild::Drain()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendDrain()) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendDrain();
   }
 }
 
 void
 VideoDecoderChild::Shutdown()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendShutdown()) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendShutdown();
   }
   mInitialized = false;
 }
 
 bool
 VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const
 {
   aFailureReason = mHardwareAcceleratedReason;
   return mIsHardwareAccelerated;
 }
 
 void
 VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime)
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendSetSeekThreshold(aTime.ToMicroseconds())) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendSetSeekThreshold(aTime.ToMicroseconds());
   }
 }
 
 void
 VideoDecoderChild::AssertOnManagerThread()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
 }
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -8,109 +8,94 @@
 #include "mozilla/dom/ContentChild.h"
 #include "MediaPrefs.h"
 #include "nsThreadUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
+#include "base/task.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace ipc;
 using namespace layers;
 using namespace gfx;
 
 // Only modified on the main-thread
 StaticRefPtr<nsIThread> sVideoDecoderChildThread;
 StaticRefPtr<AbstractThread> sVideoDecoderChildAbstractThread;
 
 // Only accessed from sVideoDecoderChildThread
 static StaticRefPtr<VideoDecoderManagerChild> sDecoderManager;
+static nsTArray<RefPtr<Runnable>> sRecreateTasks;
 
 /* static */ void
-VideoDecoderManagerChild::Initialize()
+VideoDecoderManagerChild::InitializeThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  MediaPrefs::GetSingleton();
-
-#ifdef XP_WIN
-  if (!MediaPrefs::PDMUseGPUDecoder()) {
-    return;
-  }
-
-  // Can't run remote video decoding in the parent process.
-  if (!ContentChild::GetSingleton()) {
-    return;
-  }
-
   if (!sVideoDecoderChildThread) {
     RefPtr<nsIThread> childThread;
     nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread));
     NS_ENSURE_SUCCESS_VOID(rv);
     sVideoDecoderChildThread = childThread;
 
     sVideoDecoderChildAbstractThread =
       AbstractThread::CreateXPCOMThreadWrapper(childThread, false);
   }
-#else
-  return;
-#endif
+}
 
+/* static */ void
+VideoDecoderManagerChild::InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
+{
+  InitializeThread();
+  sVideoDecoderChildThread->Dispatch(NewRunnableFunction(&Open, Move(aVideoManager)), NS_DISPATCH_NORMAL);
 }
 
 /* static */ void
 VideoDecoderManagerChild::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sVideoDecoderChildThread) {
     sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([]() {
-      if (sDecoderManager) {
+      if (sDecoderManager && sDecoderManager->CanSend()) {
         sDecoderManager->Close();
         sDecoderManager = nullptr;
       }
     }), NS_DISPATCH_NORMAL);
 
     sVideoDecoderChildAbstractThread = nullptr;
     sVideoDecoderChildThread->Shutdown();
     sVideoDecoderChildThread = nullptr;
   }
 }
 
+void
+VideoDecoderManagerChild::RunWhenRecreated(already_AddRefed<Runnable> aTask)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
+
+  // If we've already been recreated, then run the task immediately.
+  if (sDecoderManager && sDecoderManager != this && sDecoderManager->CanSend()) {
+    RefPtr<Runnable> task = aTask;
+    task->Run();
+  } else {
+    sRecreateTasks.AppendElement(aTask);
+  }
+}
+
+
 /* static */ VideoDecoderManagerChild*
 VideoDecoderManagerChild::GetSingleton()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
-
-  if (!sDecoderManager || !sDecoderManager->mCanSend) {
-    RefPtr<VideoDecoderManagerChild> manager;
-      
-    NS_DispatchToMainThread(NS_NewRunnableFunction([&]() {
-      Endpoint<PVideoDecoderManagerChild> endpoint;
-      if (!ContentChild::GetSingleton()->SendInitVideoDecoderManager(&endpoint)) {
-        return;
-      }
-
-      if (!endpoint.IsValid()) {
-        return;
-      }
-
-      manager = new VideoDecoderManagerChild();
-
-      RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVideoDecoderManagerChild>&&>(
-        manager, &VideoDecoderManagerChild::Open, Move(endpoint));
-      sVideoDecoderChildThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
-    }), NS_DISPATCH_SYNC);
-
-    sDecoderManager = manager;
-  }
   return sDecoderManager;
 }
 
 /* static */ nsIThread*
 VideoDecoderManagerChild::GetManagerThread()
 {
   return sVideoDecoderChildThread;
 }
@@ -133,43 +118,66 @@ VideoDecoderManagerChild::DeallocPVideoD
   VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
   child->IPDLActorDestroyed();
   return true;
 }
 
 void
 VideoDecoderManagerChild::Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint)
 {
-  if (!aEndpoint.Bind(this)) {
-    return;
+  // Make sure we always dispatch everything in sRecreateTasks, even if we
+  // fail since this is as close to being recreated as we will ever be.
+  sDecoderManager = nullptr;
+  if (aEndpoint.IsValid()) {
+    RefPtr<VideoDecoderManagerChild> manager = new VideoDecoderManagerChild();
+    if (aEndpoint.Bind(manager)) {
+      sDecoderManager = manager;
+      manager->InitIPDL();
+    }
   }
-  AddRef();
+  for (Runnable* task : sRecreateTasks) {
+    task->Run();
+  }
+  sRecreateTasks.Clear();
+}
+
+void
+VideoDecoderManagerChild::InitIPDL()
+{
   mCanSend = true;
+  mIPDLSelfRef = this;
 }
 
 void
 VideoDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mCanSend = false;
 }
 
 void
 VideoDecoderManagerChild::DeallocPVideoDecoderManagerChild()
 {
-  Release();
+  mIPDLSelfRef = nullptr;
+}
+
+bool
+VideoDecoderManagerChild::CanSend()
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
+  return mCanSend;
 }
 
 bool
 VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem)
 {
   if (NS_GetCurrentThread() != sVideoDecoderChildThread) {
     RefPtr<VideoDecoderManagerChild> self = this;
     mozilla::ipc::Shmem shmem = aShmem;
     sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([self, shmem]() {
-      if (self->mCanSend) {
+      if (self->CanSend()) {
         mozilla::ipc::Shmem shmemCopy = shmem;
         self->DeallocShmem(shmemCopy);
       }
     }), NS_DISPATCH_NORMAL);
     return true;
   }
   return PVideoDecoderManagerChild::DeallocShmem(aShmem);
 }
@@ -202,17 +210,17 @@ VideoDecoderManagerChild::Readback(const
   // loop while it waits. This function can be called from JS and we
   // don't want that to happen.
   SynchronousTask task("Readback sync");
 
   RefPtr<VideoDecoderManagerChild> ref = this;
   SurfaceDescriptor sd;
   sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() {
     AutoCompleteTask complete(&task);
-    if (ref->mCanSend) {
+    if (ref->CanSend()) {
       ref->SendReadback(aSD, &sd);
     }
   }), NS_DISPATCH_NORMAL);
 
   task.Wait();
 
   if (!IsSurfaceDescriptorValid(sd)) {
     return nullptr;
@@ -234,17 +242,17 @@ VideoDecoderManagerChild::Readback(const
 }
 
 void
 VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD)
 {
   RefPtr<VideoDecoderManagerChild> ref = this;
   SurfaceDescriptorGPUVideo sd = Move(aSD);
   sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([ref, sd]() {
-    if (ref->mCanSend) {
+    if (ref->CanSend()) {
       ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
     }
   }), NS_DISPATCH_NORMAL);
 }
 
 void
 VideoDecoderManagerChild::FatalError(const char* const aName, const char* const aMsg) const
 {
--- a/dom/media/ipc/VideoDecoderManagerChild.h
+++ b/dom/media/ipc/VideoDecoderManagerChild.h
@@ -46,35 +46,50 @@ public:
     return PVideoDecoderManagerChild::AllocUnsafeShmem(aSize, aShmType, aShmem);
   }
 
   // Can be called from any thread, dispatches the request to the IPDL thread internally
   // and will be ignored if the IPDL actor has been destroyed.
   bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
   // Main thread only
-  static void Initialize();
+  static void InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager);
   static void Shutdown();
 
+  // Run aTask (on the manager thread) when we next attempt to create a new manager
+  // (even if creation fails). Intended to be called from ActorDestroy when we get
+  // notified that the old manager is being destroyed.
+  // Can only be called from the manager thread.
+  void RunWhenRecreated(already_AddRefed<Runnable> aTask);
+
+  bool CanSend();
+
 protected:
+  void InitIPDL();
+
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVideoDecoderManagerChild() override;
 
   void FatalError(const char* const aName, const char* const aMsg) const override;
 
   PVideoDecoderChild* AllocPVideoDecoderChild() override;
   bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
 
 private:
+  // Main thread only
+  static void InitializeThread();
+
   VideoDecoderManagerChild()
     : mCanSend(false)
   {}
   ~VideoDecoderManagerChild() {}
 
-  void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint);
+  static void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint);
+
+  RefPtr<VideoDecoderManagerChild> mIPDLSelfRef;
 
   // Should only ever be accessed on the manager thread.
   bool mCanSend;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -24,16 +24,17 @@
 #include "nsContentUtils.h"
 #include "VRManagerChild.h"
 #include "VRManagerParent.h"
 #include "VsyncBridgeChild.h"
 #include "VsyncIOThreadHolder.h"
 #include "VsyncSource.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "MediaPrefs.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
 static StaticAutoPtr<GPUProcessManager> sSingleton;
 
@@ -531,24 +532,28 @@ GPUProcessManager::CreateRemoteSession(n
   return nullptr;
 #endif
 }
 
 bool
 GPUProcessManager::CreateContentBridges(base::ProcessId aOtherProcess,
                                         ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor,
                                         ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
-                                        ipc::Endpoint<PVRManagerChild>* aOutVRBridge)
+                                        ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
+                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager)
 {
   if (!CreateContentCompositorBridge(aOtherProcess, aOutCompositor) ||
       !CreateContentImageBridge(aOtherProcess, aOutImageBridge) ||
       !CreateContentVRManager(aOtherProcess, aOutVRBridge))
   {
     return false;
   }
+  // VideoDeocderManager is only supported in the GPU process, so we allow this to be
+  // fallible.
+  CreateContentVideoDecoderManager(aOtherProcess, aOutVideoManager);
   return true;
 }
 
 bool
 GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                                  ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint)
 {
   EnsureGPUReady();
@@ -654,41 +659,41 @@ GPUProcessManager::CreateContentVRManage
       return false;
     }
   }
 
   *aOutEndpoint = Move(childPipe);
   return true;
 }
 
-bool
+void
 GPUProcessManager::CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
                                                     ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndpoint)
 {
-  if (!mGPUChild) {
-    return false;
+  if (!mGPUChild || !MediaPrefs::PDMUseGPUDecoder()) {
+    return;
   }
 
   ipc::Endpoint<dom::PVideoDecoderManagerParent> parentPipe;
   ipc::Endpoint<dom::PVideoDecoderManagerChild> childPipe;
 
   nsresult rv = dom::PVideoDecoderManager::CreateEndpoints(
     mGPUChild->OtherPid(),
     aOtherProcess,
     &parentPipe,
     &childPipe);
   if (NS_FAILED(rv)) {
     gfxCriticalNote << "Could not create content video decoder: " << hexa(int(rv));
-    return false;
+    return;
   }
 
   mGPUChild->SendNewContentVideoDecoderManager(Move(parentPipe));
 
   *aOutEndpoint = Move(childPipe);
-  return true;
+  return;
 }
 
 already_AddRefed<IAPZCTreeManager>
 GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId)
 {
   return CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
 }
 
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -87,19 +87,18 @@ public:
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   bool CreateContentBridges(
     base::ProcessId aOtherProcess,
     ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor,
     ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
-    ipc::Endpoint<PVRManagerChild>* aOutVRBridge);
-  bool CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
-                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
+    ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
+    ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager);
 
   // This returns a reference to the APZCTreeManager to which
   // pan/zoom-related events can be sent.
   already_AddRefed<IAPZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
 
   // Maps the layer tree and process together so that aOwningPID is allowed
   // to access aLayersId across process.
   void MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId);
@@ -154,16 +153,18 @@ private:
   void OnXPCOMShutdown();
 
   bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
   bool CreateContentVRManager(base::ProcessId aOtherProcess,
                               ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
+  void CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
+                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
 
   // Called from RemoteCompositorSession. We track remote sessions so we can
   // notify their owning widgets that the session must be restarted.
   void RegisterSession(RemoteCompositorSession* aSession);
   void UnregisterSession(RemoteCompositorSession* aSession);
 
 private:
   GPUProcessManager();
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -62,17 +62,16 @@
 #include "DOMStorageObserver.h"
 #include "CacheObserver.h"
 #include "DisplayItemClip.h"
 #include "ActiveLayerTracker.h"
 #include "CounterStyleManager.h"
 #include "FrameLayerBuilder.h"
 #include "AnimationCommon.h"
 #include "LayerAnimationInfo.h"
-#include "mozilla/dom/VideoDecoderManagerChild.h"
 
 #include "AudioChannelService.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/WebCryptoThreadPool.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
@@ -297,18 +296,16 @@ nsLayoutStatics::Initialize()
 
 #ifdef DEBUG
   nsStyleContext::Initialize();
   mozilla::LayerAnimationInfo::Initialize();
 #endif
 
   MediaDecoder::InitStatics();
 
-  VideoDecoderManagerChild::Initialize();
-
   PromiseDebugging::Init();
 
   mozilla::dom::devicestorage::DeviceStorageStatics::Initialize();
 
   mozilla::dom::WebCryptoThreadPool::Initialize();
 
   // NB: We initialize servo in nsAppRunner.cpp, because we need to do it after
   // creating the hidden DOM window to support some current stylo hacks. We