Recreate content compositor endpoints when the GPU process dies. (bug 1300936 part 3, r=mattwoodrow,billm)
authorDavid Anderson <danderson@mozilla.com>
Tue, 20 Sep 2016 01:18:50 -0700
changeset 314465 9a6f5fac405ffd3ab59163bf4c5ec866a1bb142b
parent 314464 5ac08fb77360da01e694dd3fb5fef07a64173298
child 314466 101e8160640228dcc0e59429d27a2f3a3a6a957d
push id81899
push userdanderson@mozilla.com
push dateTue, 20 Sep 2016 08:20:45 +0000
treeherdermozilla-inbound@101e81606402 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, billm
bugs1300936
milestone52.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
Recreate content compositor endpoints when the GPU process dies. (bug 1300936 part 3, r=mattwoodrow,billm)
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
gfx/ipc/GPUProcessListener.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
gfx/ipc/moz.build
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeChild.h
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/vr/ipc/VRManagerChild.cpp
gfx/vr/ipc/VRManagerChild.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1181,16 +1181,33 @@ ContentChild::RecvInitRendering(Endpoint
     return false;
   }
   if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) {
     return false;
   }
   return true;
 }
 
+bool
+ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
+                                  Endpoint<PImageBridgeChild>&& aImageBridge,
+                                  Endpoint<PVRManagerChild>&& aVRBridge)
+{
+  if (!CompositorBridgeChild::ReinitForContent(Move(aCompositor))) {
+    return false;
+  }
+  if (!ImageBridgeChild::ReinitForContent(Move(aImageBridge))) {
+    return false;
+  }
+  if (!gfx::VRManagerChild::ReinitForContent(Move(aVRBridge))) {
+    return false;
+  }
+  return true;
+}
+
 PSharedBufferManagerChild*
 ContentChild::AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                               base::ProcessId aOtherProcess)
 {
   return SharedBufferManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
 PBackgroundChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -162,16 +162,22 @@ public:
                         base::ProcessId otherProcess) override;
 
   bool
   RecvInitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
     Endpoint<PVRManagerChild>&& aVRBridge) override;
 
+  bool
+  RecvReinitRendering(
+    Endpoint<PCompositorBridgeChild>&& aCompositor,
+    Endpoint<PImageBridgeChild>&& aImageBridge,
+    Endpoint<PVRManagerChild>&& aVRBridge) override;
+
   PSharedBufferManagerChild*
   AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                   base::ProcessId aOtherProcess) override;
 
   PProcessHangMonitorChild*
   AllocPProcessHangMonitorChild(Transport* aTransport,
                                 ProcessId aOtherProcess) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1819,16 +1819,21 @@ ContentParent::ActorDestroy(ActorDestroy
                           sObserverTopics[i]);
     }
   }
 
   // remove the global remote preferences observers
   Preferences::RemoveObserver(this, "");
   gfxVars::RemoveReceiver(this);
 
+  if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
+    // Note: the manager could have shutdown already.
+    gpu->RemoveListener(this);
+  }
+
   RecvRemoveGeolocationListener();
 
   mConsoleService = nullptr;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
   if (mGatherer && !mProfile.IsEmpty()) {
     mGatherer->OOPExitProfile(mProfile);
   }
@@ -2234,16 +2239,18 @@ ContentParent::InitInternal(ProcessPrior
         &imageBridge,
         &vrBridge);
       MOZ_ASSERT(opened);
 
       Unused << SendInitRendering(
         Move(compositor),
         Move(imageBridge),
         Move(vrBridge));
+
+      gpm->AddListener(this);
     }
 #ifdef MOZ_WIDGET_GONK
     DebugOnly<bool> opened = PSharedBufferManager::Open(this);
     MOZ_ASSERT(opened);
 #endif
   }
 
   if (gAppData) {
@@ -2373,16 +2380,38 @@ ContentParent::RecvGetGfxVars(Infallible
 
   // Now that content has initialized gfxVars, we can start listening for
   // updates.
   gfxVars::AddReceiver(this);
   return true;
 }
 
 void
+ContentParent::OnCompositorUnexpectedShutdown()
+{
+  GPUProcessManager* gpm = GPUProcessManager::Get();
+
+  Endpoint<PCompositorBridgeChild> compositor;
+  Endpoint<PImageBridgeChild> imageBridge;
+  Endpoint<PVRManagerChild> vrBridge;
+
+  DebugOnly<bool> opened = gpm->CreateContentBridges(
+    OtherPid(),
+    &compositor,
+    &imageBridge,
+    &vrBridge);
+  MOZ_ASSERT(opened);
+
+  Unused << SendReinitRendering(
+    Move(compositor),
+    Move(imageBridge),
+    Move(vrBridge));
+}
+
+void
 ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
 {
   if (!mIPCOpen) {
     return;
   }
   Unused << SendVarUpdate(aVar);
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ContentParent_h
 #define mozilla_dom_ContentParent_h
 
 #include "mozilla/dom/PContentParent.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/gfx/gfxVarReceiver.h"
+#include "mozilla/gfx/GPUProcessListener.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 
@@ -87,16 +88,17 @@ class GetFilesHelper;
 
 class ContentParent final : public PContentParent
                           , public nsIContentParent
                           , public nsIObserver
                           , public nsIDOMGeoPositionCallback
                           , public nsIDOMGeoPositionErrorCallback
                           , public gfx::gfxVarReceiver
                           , public mozilla::LinkedListElement<ContentParent>
+                          , public gfx::GPUProcessListener
 {
   typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
   typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
   typedef mozilla::ipc::TestShellParent TestShellParent;
   typedef mozilla::ipc::URIParams URIParams;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
@@ -569,16 +571,17 @@ public:
 protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
+  void OnCompositorUnexpectedShutdown() override;
 
 private:
   static nsDataHashtable<nsStringHashKey, ContentParent*> *sAppContentParents;
   static nsTArray<ContentParent*>* sNonAppContentParents;
   static nsTArray<ContentParent*>* sPrivateContent;
   static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
 
   static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -464,16 +464,24 @@ both:
 
 child:
     // Give the content process its endpoints to the compositor.
     async InitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> imageBridge,
       Endpoint<PVRManagerChild> vr);
 
+    // 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);
+
     /**
      * 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);
 
     async PMemoryReportRequest(uint32_t generation, bool anonymize,
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/GPUProcessListener.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 _include_mozilla_gfx_ipc_GPUProcessListener_h_
+#define _include_mozilla_gfx_ipc_GPUProcessListener_h_
+
+namespace mozilla {
+namespace gfx {
+
+class GPUProcessListener
+{
+ public:
+  virtual ~GPUProcessListener()
+  {}
+
+  // Called when the compositor has died and the rendering stack must be
+  // recreated.
+  virtual void OnCompositorUnexpectedShutdown()
+  {}
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_GPUProcessListener_h_
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 "GPUProcessManager.h"
 #include "GPUProcessHost.h"
+#include "GPUProcessListener.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/InProcessCompositorSession.h"
@@ -239,27 +240,100 @@ GPUProcessManager::OnProcessLaunchComple
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
 
+  // The shutdown and restart sequence for the GPU process is as follows:
+  //
+  //  (1) The GPU process dies. IPDL will enqueue an ActorDestroy message on
+  //      each channel owning a bridge to the GPU process, on the thread
+  //      owning that channel.
+  //
+  //  (2) The first channel to process its ActorDestroy message will post a
+  //      message to the main thread to call NotifyRemoteActorDestroyed on
+  //      the GPUProcessManager, which calls OnProcessUnexpectedShutdown if
+  //      it has not handled shutdown for this process yet.
+  //
+  //  (3) We then notify each widget that its session with the compositor is
+  //      now invalid. The widget is responsible for destroying its layer
+  //      manager and CompositorBridgeChild. Note that at this stage, not
+  //      all actors may have received ActorDestroy yet. CompositorBridgeChild
+  //      may attempt to send messages, and if this happens, it will probably
+  //      report a MsgDropped error. This is okay.
+  //
+  //  (4) At this point, the UI process has a clean slate: no layers should
+  //      exist for the old compositor. We may make a decision on whether or
+  //      not to re-launch the GPU process. Currently, we do not relaunch it,
+  //      and any new compositors will be created in-process and will default
+  //      to software.
+  //
+  //  (5) Next we notify each ContentParent of the lost connection. It will
+  //      request new endpoints from the GPUProcessManager and forward them
+  //      to its ContentChild. The parent-side of these endpoints may come
+  //      from the compositor thread of the UI process, or the compositor
+  //      thread of the GPU process. However, no actual compositors should
+  //      exist yet.
+  //
+  //  (6) Each ContentChild will receive new endpoints. It will destroy its
+  //      Compositor/ImageBridgeChild singletons and recreate them, as well
+  //      as invalidate all retained layers.
+  //
+  //  (7) In addition, each ContentChild will ask each of its TabChildren
+  //      to re-request association with the compositor for the window
+  //      owning the tab. The sequence of calls looks like:
+  //        (a) [CONTENT] ContentChild::RecvReinitRendering
+  //        (b) [CONTENT] TabChild::ReinitRendering
+  //        (c) [CONTENT] TabChild::SendEnsureLayersConnected
+  //        (d)      [UI] TabParent::RecvEnsureLayersConnected
+  //        (e)      [UI] RenderFrameParent::EnsureLayersConnected
+  //        (f)      [UI] CompositorBridgeChild::SendNotifyChildRecreated
+  //
+  //      Note that at step (e), RenderFrameParent will call GetLayerManager
+  //      on the nsIWidget owning the tab. This step ensures that a compositor
+  //      exists for the window. If we decided to launch a new GPU Process,
+  //      at this point we block until the process has launched and we're
+  //      able to create a new window compositor. Otherwise, if compositing
+  //      is now in-process, this will simply create a new
+  //      CompositorBridgeParent in the UI process. If there are multiple tabs
+  //      in the same window, additional tabs will simply return the already-
+  //      established compositor.
+  //
+  //      Finally, this step serves one other crucial function: tabs must be
+  //      associated with a window compositor or else they can't forward
+  //      layer transactions. So this step both ensures that a compositor
+  //      exists, and that the tab can forward layers.
+  //
+  //  (8) Last, if the window had no remote tabs, step (7) will not have 
+  //      applied, and the window will not have a new compositor just yet.
+  //      The next refresh tick and paint will ensure that one exists, again
+  //      via nsIWidget::GetLayerManager.
+
   // Build a list of sessions to notify, since notification might delete
   // entries from the list.
   nsTArray<RefPtr<RemoteCompositorSession>> sessions;
   for (auto& session : mRemoteSessions) {
     sessions.AppendElement(session);
   }
 
-  // Notify.
+  // Notify each widget that we have lost the GPU process. This will ensure
+  // that each widget destroys its layer manager and CompositorBridgeChild.
   for (const auto& session : sessions) {
     session->NotifySessionLost();
   }
+
+  // Notify content. This will ensure that each content process re-establishes
+  // a connection to the compositor thread (whether it's in-process or in a
+  // newly launched GPU process).
+  for (const auto& listener : mListeners) {
+    listener->OnCompositorUnexpectedShutdown();
+  }
 }
 
 void
 GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
       &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
@@ -608,10 +682,22 @@ GPUProcessManager::RegisterSession(Remot
 }
 
 void
 GPUProcessManager::UnregisterSession(RemoteCompositorSession* aSession)
 {
   mRemoteSessions.RemoveElement(aSession);
 }
 
+void
+GPUProcessManager::AddListener(GPUProcessListener* aListener)
+{
+  mListeners.AppendElement(aListener);
+}
+
+void
+GPUProcessManager::RemoveListener(GPUProcessListener* aListener)
+{
+  mListeners.RemoveElement(aListener);
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -39,16 +39,17 @@ class ContentParent;
 class TabParent;
 } // namespace dom
 namespace ipc {
 class GeckoChildProcessHost;
 } // namespace ipc
 namespace gfx {
 
 class GPUChild;
+class GPUProcessListener;
 class PVRManagerChild;
 class VsyncBridgeChild;
 class VsyncIOThreadHolder;
 
 // The GPUProcessManager is a singleton responsible for creating GPU-bound
 // objects that may live in another process. Currently, it provides access
 // to the compositor via CompositorBridgeParent.
 class GPUProcessManager final : public GPUProcessHost::Listener
@@ -116,16 +117,19 @@ public:
 
   void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
   void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
 
   // Notify the GPUProcessManager that a top-level PGPU protocol has been
   // terminated. This may be called from any thread.
   void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
 
+  void AddListener(GPUProcessListener* aListener);
+  void RemoveListener(GPUProcessListener* aListener);
+
   // Returns access to the PGPU protocol if a GPU process is present.
   GPUChild* GetGPUChild() {
     return mGPUChild;
   }
 
 private:
   // Called from our xpcom-shutdown observer.
   void OnXPCOMShutdown();
@@ -184,16 +188,17 @@ private:
 
 private:
   RefPtr<Observer> mObserver;
   ipc::TaskFactory<GPUProcessManager> mTaskFactory;
   RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
   uint64_t mNextLayerTreeId;
 
   nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions;
+  nsTArray<GPUProcessListener*> mListeners;
 
   // Fields that are associated with the current GPU process.
   GPUProcessHost* mProcess;
   MOZ_INIT_OUTSIDE_CTOR uint64_t mProcessToken;
   GPUChild* mGPUChild;
   RefPtr<VsyncBridgeChild> mVsyncBridge;
 };
 
--- a/gfx/ipc/moz.build
+++ b/gfx/ipc/moz.build
@@ -9,16 +9,17 @@ EXPORTS.mozilla += [
     'GfxMessageUtils.h'
 ]
 
 EXPORTS.mozilla.gfx += [
     'GPUChild.h',
     'GPUParent.h',
     'GPUProcessHost.h',
     'GPUProcessImpl.h',
+    'GPUProcessListener.h',
     'GPUProcessManager.h',
     'SharedDIB.h',
     'VsyncBridgeChild.h',
     'VsyncBridgeParent.h',
     'VsyncIOThreadHolder.h',
 ]
 
 EXPORTS.mozilla.layers += [
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -191,16 +191,32 @@ CompositorBridgeChild::InitForContent(En
 
   child->mCanSend = true;
 
   // We release this ref in DeferredDestroyCompositor.
   sCompositorBridge = child;
   return true;
 }
 
+/* static */ bool
+CompositorBridgeChild::ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (RefPtr<CompositorBridgeChild> old = sCompositorBridge.forget()) {
+    // Note that at this point, ActorDestroy may not have been called yet,
+    // meaning mCanSend is still true. In this case we will try to send a
+    // synchronous WillClose message to the parent, and will certainly get
+    // a false result and a MsgDropped processing error. This is okay.
+    old->Destroy();
+  }
+
+  return InitForContent(Move(aEndpoint));
+}
+
 CompositorBridgeParent*
 CompositorBridgeChild::InitSameProcess(widget::CompositorWidget* aWidget,
                                        const uint64_t& aLayerTreeId,
                                        CSSToLayoutDeviceScale aScale,
                                        bool aUseAPZ,
                                        bool aUseExternalSurface,
                                        const gfx::IntSize& aSurfaceSize)
 {
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -59,16 +59,17 @@ public:
    * in progressive paint calculations.
    */
   bool LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId, FrameMetrics&);
 
   /**
    * Initialize the singleton compositor bridge for a content process.
    */
   static bool InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint);
+  static bool ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint);
 
   static RefPtr<CompositorBridgeChild> CreateRemote(
     const uint64_t& aProcessToken,
     ClientLayerManager* aLayerManager,
     Endpoint<PCompositorBridgeChild>&& aEndpoint);
 
   /**
    * Initialize the CompositorBridgeChild, create CompositorBridgeParent, and
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -447,17 +447,19 @@ ImageBridgeChild::ShutdownStep1(Synchron
   ManagedPTextureChild(textures);
   for (int i = textures.Length() - 1; i >= 0; --i) {
     RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
     if (client) {
       client->Destroy();
     }
   }
 
-  SendWillClose();
+  if (mCanSend) {
+    SendWillClose();
+  }
   MarkShutDown();
 
   // From now on, no message can be sent through the image bridge from the
   // client side except the final Stop message.
 }
 
 // dispatched function
 void
@@ -853,19 +855,21 @@ ImageBridgeChild::SendImageBridgeThreadI
 
 bool
 ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   gfxPlatform::GetPlatform();
 
-  sImageBridgeChildThread = new ImageBridgeThread();
-  if (!sImageBridgeChildThread->Start()) {
-    return false;
+  if (!sImageBridgeChildThread) {
+    sImageBridgeChildThread = new ImageBridgeThread();
+    if (!sImageBridgeChildThread->Start()) {
+      return false;
+    }
   }
 
   RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
 
   RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
     child,
     &ImageBridgeChild::Bind,
     Move(aEndpoint));
@@ -875,16 +879,30 @@ ImageBridgeChild::InitForContent(Endpoin
   {
     StaticMutexAutoLock lock(sImageBridgeSingletonLock);
     sImageBridgeChildSingleton = child;
   }
 
   return true;
 }
 
+bool
+ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Note that at this point, ActorDestroy may not have been called yet,
+  // meaning mCanSend is still true. In this case we will try to send a
+  // synchronous WillClose message to the parent, and will certainly get a
+  // false result and a MsgDropped processing error. This is okay.
+  ShutdownSingleton();
+
+  return InitForContent(Move(aEndpoint));
+}
+
 void
 ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
   if (!aEndpoint.Bind(this)) {
     return;
   }
 
   // This reference is dropped in DeallocPImageBridgeChild.
@@ -903,29 +921,38 @@ ImageBridgeChild::BindSameProcess(RefPtr
 
   // This reference is dropped in DeallocPImageBridgeChild.
   this->AddRef();
 
   mCanSend = true;
   SendImageBridgeThreadId();
 }
 
-void ImageBridgeChild::ShutDown()
+/* static */ void
+ImageBridgeChild::ShutDown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  ShutdownSingleton();
+
+  delete sImageBridgeChildThread;
+  sImageBridgeChildThread = nullptr;
+}
+
+/* static */ void
+ImageBridgeChild::ShutdownSingleton()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
     child->WillShutdown();
 
     StaticMutexAutoLock lock(sImageBridgeSingletonLock);
     sImageBridgeChildSingleton = nullptr;
   }
-
-  delete sImageBridgeChildThread;
-  sImageBridgeChildThread = nullptr;
 }
 
 void
 ImageBridgeChild::WillShutdown()
 {
   {
     SynchronousTask task("ImageBridge ShutdownStep1 lock");
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -123,16 +123,17 @@ public:
    *
    * We may want to use a specifi thread in the future. In this case, use
    * CreateWithThread instead.
    */
   static void InitSameProcess();
 
   static void InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint);
   static bool InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint);
+  static bool ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint);
 
   /**
    * Destroys the image bridge by calling DestroyBridge, and destroys the
    * ImageBridge's thread.
    *
    * If you don't want to destroy the thread, call DestroyBridge directly
    * instead.
    */
@@ -384,16 +385,18 @@ protected:
   void ShutdownStep2(SynchronousTask* aTask);
   void MarkShutDown();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPImageBridgeChild() override;
 
   bool CanSend() const;
 
+  static void ShutdownSingleton();
+
 private:
   class ShutdownObserver final : public nsIObserver
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     explicit ShutdownObserver(ImageBridgeChild* aImageBridge);
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -91,16 +91,26 @@ VRManagerChild::InitForContent(Endpoint<
   if (!aEndpoint.Bind(child)) {
     NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
     return false;
   }
   sVRManagerChildSingleton = child;
   return true;
 }
 
+/* static */ bool
+VRManagerChild::ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  ShutDown();
+
+  return InitForContent(Move(aEndpoint));
+}
+
 /*static*/ void
 VRManagerChild::InitSameProcess()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sVRManagerChildSingleton);
 
   sVRManagerChildSingleton = new VRManagerChild();
   sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -43,16 +43,17 @@ public:
 
   int GetInputFrameID();
   bool GetVRDisplays(nsTArray<RefPtr<VRDisplayClient> >& aDisplays);
   bool RefreshVRDisplaysWithCallback(dom::Navigator* aNavigator);
 
   static void InitSameProcess();
   static void InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint);
   static bool InitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
+  static bool ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
   static void ShutDown();
 
   static bool IsCreated();
 
   virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
                                        layers::LayersBackend aLayersBackend,
                                        TextureFlags aFlags,
                                        uint64_t aSerial) override;