Bug 1231213 - Only spawn Service Workers in non-web-extensions processes that won't imminently shutdown. r=asuth
☠☠ backed out by 3cf55b7f12f2 ☠ ☠
authorPerry Jiang <perry@mozilla.com>
Wed, 14 Aug 2019 16:20:38 +0000
changeset 488077 6a40ab6852cb51e9903751b04d797c5d53549fa5
parent 488076 216591953f9750051e8915543aa246d4ec1600bb
child 488078 906b80778539e1362a444033c435759d01dfe7f0
push id113900
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:53:50 +0000
treeherdermozilla-inbound@0db07ff50ab5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1231213
milestone70.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
Bug 1231213 - Only spawn Service Workers in non-web-extensions processes that won't imminently shutdown. r=asuth Differential Revision: https://phabricator.services.mozilla.com/D26176
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/workers/remoteworkers/RemoteWorkerManager.cpp
dom/workers/remoteworkers/RemoteWorkerManager.h
dom/workers/remoteworkers/RemoteWorkerParent.cpp
dom/workers/remoteworkers/RemoteWorkerParent.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1377,16 +1377,42 @@ NS_IMETHODIMP
 RemoteWindowContext::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
   nsCOMPtr<nsILoadContext> loadContext = mBrowserParent->GetLoadContext();
   *aUsePrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
   return NS_OK;
 }
 
 }  // namespace
 
+void ContentParent::MaybeAsyncSendShutDownMessage() {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!TryToRecycle());
+
+#ifdef DEBUG
+  // Calling this below while the lock is acquired will deadlock.
+  bool shouldKeepProcessAlive = ShouldKeepProcessAlive();
+#endif
+
+  auto lock = mRemoteWorkerActorData.Lock();
+  MOZ_ASSERT_IF(!lock->mCount, !shouldKeepProcessAlive);
+
+  if (lock->mCount) {
+    return;
+  }
+
+  MOZ_ASSERT(!lock->mShutdownStarted);
+  lock->mShutdownStarted = true;
+
+  // In the case of normal shutdown, send a shutdown message to child to
+  // allow it to perform shutdown tasks.
+  MessageLoop::current()->PostTask(NewRunnableMethod<ShutDownMethod>(
+      "dom::ContentParent::ShutDownProcess", this,
+      &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
+}
+
 void ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
   if (mScriptableHelper) {
     static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
     mScriptableHelper = nullptr;
   }
 
   // Shutting down by sending a shutdown message works differently than the
   // other methods. We first call Shutdown() in the child. After the child is
@@ -1721,24 +1747,27 @@ bool ContentParent::TryToRecycle() {
   }
 
   // The PreallocatedProcessManager took over the ownership let's not keep a
   // reference to it, until we don't take it back.
   RemoveFromList();
   return true;
 }
 
-bool ContentParent::ShouldKeepProcessAlive() const {
+bool ContentParent::ShouldKeepProcessAlive() {
   if (IsForJSPlugin()) {
     return true;
   }
 
   // If we have active workers, we need to stay alive.
-  if (mRemoteWorkerActors) {
-    return true;
+  {
+    const auto lock = mRemoteWorkerActorData.Lock();
+    if (lock->mCount) {
+      return true;
+    }
   }
 
   if (!sBrowserContentParents) {
     return false;
   }
 
   // If we have already been marked as dead, don't prevent shutdown.
   if (!IsAlive()) {
@@ -1840,21 +1869,17 @@ void ContentParent::NotifyTabDestroyed(c
         permissionRequestParent);
   }
 
   // There can be more than one PBrowser for a given app process
   // because of popup windows.  When the last one closes, shut
   // us down.
   if (ManagedPBrowserParent().Count() == 1 && !ShouldKeepProcessAlive() &&
       !TryToRecycle()) {
-    // In the case of normal shutdown, send a shutdown message to child to
-    // allow it to perform shutdown tasks.
-    MessageLoop::current()->PostTask(NewRunnableMethod<ShutDownMethod>(
-        "dom::ContentParent::ShutDownProcess", this,
-        &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
+    MaybeAsyncSendShutDownMessage();
   }
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvOpenRecordReplayChannel(
     const uint32_t& aChannelId, FileDescriptor* aConnection) {
   // We should only get this message from the child if it is recording or
   // replaying.
   if (!this->IsRecordingOrReplaying()) {
@@ -2269,17 +2294,17 @@ ContentParent::ContentParent(ContentPare
       mLaunchTS(TimeStamp::Now()),
       mLaunchYieldTS(mLaunchTS),
       mActivateTS(mLaunchTS),
       mOpener(aOpener),
       mRemoteType(aRemoteType),
       mChildID(gContentChildID++),
       mGeolocationWatchID(-1),
       mJSPluginID(aJSPluginID),
-      mRemoteWorkerActors(0),
+      mRemoteWorkerActorData("ContentParent::mRemoteWorkerActorData"),
       mNumDestroyingTabs(0),
       mLifecycleState(LifecycleState::LAUNCHING),
       mIsForBrowser(!mRemoteType.IsEmpty()),
       mRecordReplayState(aRecordReplayState),
       mRecordingFile(aRecordingFile),
       mCalledClose(false),
       mCalledKillHard(false),
       mCreatedPairedMinidumps(false),
@@ -5904,33 +5929,35 @@ mozilla::ipc::IPCResult ContentParent::R
 
   aContext->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
     Unused << aParent->SendRestoreBrowsingContextChildren(aContext, aChildren);
   });
 
   return IPC_OK();
 }
 
-void ContentParent::RegisterRemoteWorkerActor() { ++mRemoteWorkerActors; }
+void ContentParent::RegisterRemoteWorkerActor() {
+  auto lock = mRemoteWorkerActorData.Lock();
+  ++lock->mCount;
+}
 
 void ContentParent::UnregisterRemoveWorkerActor() {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (--mRemoteWorkerActors) {
-    return;
+  {
+    auto lock = mRemoteWorkerActorData.Lock();
+    if (--lock->mCount) {
+      return;
+    }
   }
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   if (!cpm->GetBrowserParentCountByProcessId(ChildID()) &&
       !ShouldKeepProcessAlive() && !TryToRecycle()) {
-    // In the case of normal shutdown, send a shutdown message to child to
-    // allow it to perform shutdown tasks.
-    MessageLoop::current()->PostTask(NewRunnableMethod<ShutDownMethod>(
-        "dom::ContentParent::ShutDownProcess", this,
-        &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
+    MaybeAsyncSendShutDownMessage();
   }
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
     BrowsingContext* aContext, bool aTrustedCaller) {
   if (!aContext || aContext->IsDiscarded()) {
     MOZ_LOG(
         BrowsingContext::GetLog(), LogLevel::Debug,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -13,16 +13,17 @@
 #include "mozilla/dom/RemoteBrowser.h"
 #include "mozilla/gfx/gfxVarReceiver.h"
 #include "mozilla/gfx/GPUProcessListener.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/DataMutex.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReportingProcess.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Variant.h"
@@ -109,16 +110,17 @@ namespace dom {
 class BrowsingContextGroup;
 class Element;
 class BrowserParent;
 class ClonedMessageData;
 class MemoryReport;
 class TabContext;
 class GetFilesHelper;
 class MemoryReportRequestHost;
+class RemoteWorkerManager;
 struct CancelContentJSOptions;
 
 #define NS_CONTENTPARENT_IID                         \
   {                                                  \
     0xeeec9ebf, 0x8ecf, 0x4e38, {                    \
       0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 \
     }                                                \
   }
@@ -140,16 +142,17 @@ class ContentParent final : public PCont
   typedef mozilla::ipc::TestShellParent TestShellParent;
   typedef mozilla::ipc::URIParams URIParams;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
   typedef mozilla::dom::BrowsingContextGroup BrowsingContextGroup;
 
   friend class mozilla::PreallocatedProcessManagerImpl;
   friend class PContentParent;
+  friend class mozilla::dom::RemoteWorkerManager;
 #ifdef FUZZING
   friend class mozilla::ipc::ProtocolFuzzerHelper;
 #endif
 
  public:
   using LaunchError = mozilla::ipc::LaunchError;
   using LaunchPromise =
       mozilla::MozPromise<RefPtr<ContentParent>, LaunchError, false>;
@@ -743,17 +746,17 @@ class ContentParent final : public PCont
    * GetNewOrUsedBrowserProcess.
    */
   void RemoveFromList();
 
   /**
    * Decide whether the process should be kept alive even when it would normally
    * be shut down, for example when all its tabs are closed.
    */
-  bool ShouldKeepProcessAlive() const;
+  bool ShouldKeepProcessAlive();
 
   /**
    * Mark this ContentParent as "troubled". This means that it is still alive,
    * but it won't be returned for new tabs in GetNewOrUsedBrowserProcess.
    */
   void MarkAsTroubled();
 
   /**
@@ -769,16 +772,18 @@ class ContentParent final : public PCont
     // Send a shutdown message and wait for FinishShutdown call back.
     SEND_SHUTDOWN_MESSAGE,
     // Close the channel ourselves and let the subprocess clean up itself.
     CLOSE_CHANNEL,
     // Close the channel with error and let the subprocess clean up itself.
     CLOSE_CHANNEL_WITH_ERROR,
   };
 
+  void MaybeAsyncSendShutDownMessage();
+
   /**
    * Exit the subprocess and vamoose.  After this call IsAlive()
    * will return false and this ContentParent will not be returned
    * by the Get*() funtions.  However, the shutdown sequence itself
    * may be asynchronous.
    *
    * If aMethod is CLOSE_CHANNEL_WITH_ERROR and this is the first call
    * to ShutDownProcess, then we'll close our channel using CloseWithError()
@@ -1231,21 +1236,34 @@ class ContentParent final : public PCont
   int32_t mJSPluginID;
 
   // After we initiate shutdown, we also start a timer to ensure
   // that even content processes that are 100% blocked (say from
   // SIGSTOP), are still killed eventually.  This task enforces that
   // timer.
   nsCOMPtr<nsITimer> mForceKillTimer;
 
-  // Number of active remote workers. This value is increased when a
-  // RemoteWorkerParent actor is created for this ContentProcess and it is
-  // decreased when the actor is destroyed.
+  // `mCount` is increased when a RemoteWorkerParent actor is created for this
+  // ContentProcess and it is decreased when the actor is destroyed.
+  //
+  // `mShutdownStarted` is flipped to `true` when a runnable that calls
+  // `ShutDownProcess` is dispatched; it's needed because the corresponding
+  // Content Process may be shutdown if there's no remote worker actors, and
+  // decrementing `mCount` and the call to `ShutDownProcess` are async. So,
+  // when a worker is going to be spawned and we see that `mCount` is 0,
+  // we can decide whether or not to use that process based on the value of
+  // `mShutdownStarted.`
+  //
   // It's touched on PBackground thread and on main-thread.
-  Atomic<uint32_t> mRemoteWorkerActors;
+  struct RemoteWorkerActorData {
+    uint32_t mCount = 0;
+    bool mShutdownStarted = false;
+  };
+
+  DataMutex<RemoteWorkerActorData> mRemoteWorkerActorData;
 
   // How many tabs we're waiting to finish their destruction
   // sequence.  Precisely, how many BrowserParents have called
   // NotifyTabDestroying() but not called NotifyTabDestroyed().
   int32_t mNumDestroyingTabs;
 
   // The process starts in the LAUNCHING state, and transitions to
   // ALIVE once it can accept IPC messages.  It remains ALIVE only
--- a/dom/workers/remoteworkers/RemoteWorkerManager.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerManager.cpp
@@ -1,66 +1,79 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "RemoteWorkerManager.h"
 
+#include <utility>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/SystemGroup.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/RemoteWorkerParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/PBackgroundParent.h"
+#include "nsCOMPtr.h"
 #include "nsIXULRuntime.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
 #include "RemoteWorkerServiceParent.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
 namespace {
 
 // Raw pointer because this object is kept alive by RemoteWorkerServiceParent
 // actors.
 RemoteWorkerManager* sRemoteWorkerManager;
 
+bool IsServiceWorker(const RemoteWorkerData& aData) {
+  return aData.serviceWorkerData().type() ==
+         OptionalServiceWorkerData::TServiceWorkerData;
+}
+
 }  // namespace
 
 /* static */
 already_AddRefed<RemoteWorkerManager> RemoteWorkerManager::GetOrCreate() {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   if (!sRemoteWorkerManager) {
     sRemoteWorkerManager = new RemoteWorkerManager();
   }
 
   RefPtr<RemoteWorkerManager> rwm = sRemoteWorkerManager;
   return rwm.forget();
 }
 
 RemoteWorkerManager::RemoteWorkerManager() : mParentActor(nullptr) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(!sRemoteWorkerManager);
 }
 
 RemoteWorkerManager::~RemoteWorkerManager() {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(sRemoteWorkerManager == this);
   sRemoteWorkerManager = nullptr;
 }
 
 void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aActor);
 
   if (!BackgroundParent::IsOtherProcessActor(aActor->Manager())) {
     MOZ_ASSERT(!mParentActor);
     mParentActor = aActor;
     MOZ_ASSERT(mPendings.IsEmpty());
     return;
   }
@@ -78,33 +91,33 @@ void RemoteWorkerManager::RegisterActor(
 
     // We don't need to keep this manager alive manually. The Actor is doing it
     // for us.
     Release();
   }
 }
 
 void RemoteWorkerManager::UnregisterActor(RemoteWorkerServiceParent* aActor) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aActor);
 
   if (aActor == mParentActor) {
     mParentActor = nullptr;
   } else {
     MOZ_ASSERT(mChildActors.Contains(aActor));
     mChildActors.RemoveElement(aActor);
   }
 }
 
 void RemoteWorkerManager::Launch(RemoteWorkerController* aController,
                                  const RemoteWorkerData& aData,
                                  base::ProcessId aProcessId) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   RemoteWorkerServiceParent* targetActor = SelectTargetActor(aData, aProcessId);
 
   // If there is not an available actor, let's store the data, and let's spawn a
   // new process.
   if (!targetActor) {
     // If this is the first time we have a pending launching, we must keep alive
     // the manager.
@@ -115,57 +128,128 @@ void RemoteWorkerManager::Launch(RemoteW
     Pending* pending = mPendings.AppendElement();
     pending->mController = aController;
     pending->mData = aData;
 
     LaunchNewContentProcess();
     return;
   }
 
-  LaunchInternal(aController, targetActor, aData);
+  /**
+   * If a target actor for a Service Worker has been selected, the actor has
+   * already been registered with the corresponding ContentParent (see
+   * `SelectTargetActorForServiceWorker()`).
+   */
+  LaunchInternal(aController, targetActor, aData, IsServiceWorker(aData));
 }
 
 void RemoteWorkerManager::LaunchInternal(
     RemoteWorkerController* aController,
-    RemoteWorkerServiceParent* aTargetActor, const RemoteWorkerData& aData) {
+    RemoteWorkerServiceParent* aTargetActor, const RemoteWorkerData& aData,
+    bool aRemoteWorkerAlreadyRegistered) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aController);
   MOZ_ASSERT(aTargetActor);
   MOZ_ASSERT(aTargetActor == mParentActor ||
              mChildActors.Contains(aTargetActor));
 
   RemoteWorkerParent* workerActor = static_cast<RemoteWorkerParent*>(
       aTargetActor->Manager()->SendPRemoteWorkerConstructor(aData));
   if (NS_WARN_IF(!workerActor)) {
     AsyncCreationFailed(aController);
     return;
   }
 
-  workerActor->Initialize();
+  workerActor->Initialize(aRemoteWorkerAlreadyRegistered);
 
   // This makes the link better the 2 actors.
   aController->SetWorkerActor(workerActor);
   workerActor->SetController(aController);
 }
 
 void RemoteWorkerManager::AsyncCreationFailed(
     RemoteWorkerController* aController) {
   RefPtr<RemoteWorkerController> controller = aController;
   nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableFunction("RemoteWorkerManager::AsyncCreationFailed",
                              [controller]() { controller->CreationFailed(); });
 
   NS_DispatchToCurrentThread(r.forget());
 }
 
+/**
+ * Service Workers can spawn even when their registering page/script isn't
+ * active (e.g. push notifications), so we don't attempt to spawn the worker
+ * in its registering script's process. We search linearly and choose the
+ * search's starting position randomly.
+ *
+ * Spawning the workers in a random process makes the process selection criteria
+ * a little tricky, as a candidate process may imminently shutdown due to a
+ * remove worker actor unregistering
+ * (see `ContentParent::UnregisterRemoveWorkerActor`).
+ *
+ * In `ContentParent::MaybeAsyncSendShutDownMessage` we only dispatch a runnable
+ * to call `ContentParent::ShutDownProcess` if there are no registered remote
+ * worker actors, and we ensure that the check for the number of registered
+ * actors and the dispatching of the runnable are atomic. That happens on the
+ * main thread, so here on the background thread,  while
+ * `ContentParent::mRemoteWorkerActorData` is locked, if `mCount` > 0, we can
+ * register a remote worker actor "early" and guarantee that the corresponding
+ * content process will not shutdown.
+ */
+RemoteWorkerServiceParent*
+RemoteWorkerManager::SelectTargetActorForServiceWorker() const {
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mChildActors.IsEmpty());
+
+  nsTArray<RefPtr<ContentParent>> contentParents;
+
+  auto scopeExit = MakeScopeExit([&] {
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+        __func__,
+        [doomed = std::move(contentParents)]() mutable { doomed.Clear(); });
+
+    SystemGroup::Dispatch(TaskCategory::Other, r.forget());
+  });
+
+  uint32_t random = uint32_t(rand() % mChildActors.Length());
+  uint32_t i = random;
+
+  do {
+    auto actor = mChildActors[i];
+    PBackgroundParent* bgParent = actor->Manager();
+    MOZ_ASSERT(bgParent);
+
+    RefPtr<ContentParent> contentParent =
+        BackgroundParent::GetContentParent(bgParent);
+
+    auto scopeExit = MakeScopeExit(
+        [&] { contentParents.AppendElement(std::move(contentParent)); });
+
+    if (contentParent->GetRemoteType().EqualsLiteral(DEFAULT_REMOTE_TYPE)) {
+      auto lock = contentParent->mRemoteWorkerActorData.Lock();
+
+      if (lock->mCount || !lock->mShutdownStarted) {
+        ++lock->mCount;
+        return actor;
+      }
+    }
+
+    i = (i + 1) % mChildActors.Length();
+  } while (i != random);
+
+  return nullptr;
+}
+
 RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActor(
     const RemoteWorkerData& aData, base::ProcessId aProcessId) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   // System principal workers should run on the parent process.
   if (aData.principalInfo().type() == PrincipalInfo::TSystemPrincipalInfo) {
     MOZ_ASSERT(mParentActor);
     return mParentActor;
   }
 
   // If e10s is off, use the parent process.
@@ -176,31 +260,35 @@ RemoteWorkerServiceParent* RemoteWorkerM
 
   // We shouldn't have to worry about content-principal parent-process workers.
   MOZ_ASSERT(aProcessId != base::GetCurrentProcId());
 
   if (mChildActors.IsEmpty()) {
     return nullptr;
   }
 
+  if (IsServiceWorker(aData)) {
+    return SelectTargetActorForServiceWorker();
+  }
+
   for (RemoteWorkerServiceParent* actor : mChildActors) {
     // Let's execute the RemoteWorker on the same process.
     if (actor->OtherPid() == aProcessId) {
       return actor;
     }
   }
 
   // Let's choose an actor, randomly.
   uint32_t id = uint32_t(rand()) % mChildActors.Length();
   return mChildActors[id];
 }
 
 void RemoteWorkerManager::LaunchNewContentProcess() {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   // This runnable will spawn a new process if it doesn't exist yet.
   nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableFunction("LaunchNewContentProcess", []() {
         RefPtr<ContentParent> unused =
             ContentParent::GetNewOrUsedBrowserProcess(
                 nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
       });
--- a/dom/workers/remoteworkers/RemoteWorkerManager.h
+++ b/dom/workers/remoteworkers/RemoteWorkerManager.h
@@ -34,19 +34,22 @@ class RemoteWorkerManager final {
 
  private:
   RemoteWorkerManager();
   ~RemoteWorkerManager();
 
   RemoteWorkerServiceParent* SelectTargetActor(const RemoteWorkerData& aData,
                                                base::ProcessId aProcessId);
 
+  RemoteWorkerServiceParent* SelectTargetActorForServiceWorker() const;
+
   void LaunchInternal(RemoteWorkerController* aController,
                       RemoteWorkerServiceParent* aTargetActor,
-                      const RemoteWorkerData& aData);
+                      const RemoteWorkerData& aData,
+                      bool aRemoteWorkerAlreadyRegistered = false);
 
   void LaunchNewContentProcess();
 
   void AsyncCreationFailed(RemoteWorkerController* aController);
 
   // The list of existing RemoteWorkerServiceParent actors for child processes.
   // Raw pointers because RemoteWorkerServiceParent actors unregister themselves
   // when destroyed.
--- a/dom/workers/remoteworkers/RemoteWorkerParent.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.cpp
@@ -48,22 +48,24 @@ RemoteWorkerParent::RemoteWorkerParent()
   MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 RemoteWorkerParent::~RemoteWorkerParent() {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 }
 
-void RemoteWorkerParent::Initialize() {
+void RemoteWorkerParent::Initialize(bool aAlreadyRegistered) {
   RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager());
 
   // Parent is null if the child actor runs on the parent process.
   if (parent) {
-    parent->RegisterRemoteWorkerActor();
+    if (!aAlreadyRegistered) {
+      parent->RegisterRemoteWorkerActor();
+    }
 
     nsCOMPtr<nsIEventTarget> target =
         SystemGroup::EventTargetFor(TaskCategory::Other);
 
     NS_ProxyRelease("RemoteWorkerParent::Initialize ContentParent", target,
                     parent.forget());
   }
 }
--- a/dom/workers/remoteworkers/RemoteWorkerParent.h
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.h
@@ -17,17 +17,17 @@ class RemoteWorkerController;
 class RemoteWorkerParent final : public PRemoteWorkerParent {
   friend class PRemoteWorkerParent;
 
  public:
   NS_INLINE_DECL_REFCOUNTING(RemoteWorkerParent)
 
   RemoteWorkerParent();
 
-  void Initialize();
+  void Initialize(bool aAlreadyRegistered = false);
 
   void SetController(RemoteWorkerController* aController);
 
   void MaybeSendDelete();
 
  private:
   ~RemoteWorkerParent();