Bug 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap
authorCatalin Badea <catalin.badea392@gmail.com>
Wed, 30 Sep 2015 19:11:03 -0400
changeset 300584 1ad5a53a875cdad4943d6d0352ec9d0bd7cce8ac
parent 300583 f065bda0feb4ddb94cf04be3f8fee802eb428acb
child 300585 9247b2db57210d843659dd3c40baee0ec9b3e849
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnsm, mrbkap
bugs1188545
milestone44.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 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap
dom/interfaces/base/nsIServiceWorkerManager.idl
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/ServiceWorker.cpp
dom/workers/ServiceWorker.h
dom/workers/ServiceWorkerContainer.cpp
dom/workers/ServiceWorkerEvents.cpp
dom/workers/ServiceWorkerEvents.h
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerPrivate.cpp
dom/workers/ServiceWorkerPrivate.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/moz.build
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -29,17 +29,17 @@ interface nsIServiceWorkerInfo : nsISupp
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString currentWorkerURL;
 
   readonly attribute DOMString activeCacheName;
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(471b2d5d-64c3-4dea-bde1-219853dcaac8)]
+[scriptable, builtinclass, uuid(8fb9db4f-1d04-402b-9c37-542da06e03b9)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
@@ -88,22 +88,20 @@ interface nsIServiceWorkerManager : nsIS
    * a valid entry that window is allowed to load, otherwise this will return nullptr.
    * These are only meant to be called from ServiceWorkerRegistration instances.
    */
   [noscript] nsISupports GetInstalling(in nsIDOMWindow aWindow, in DOMString aScope);
   [noscript] nsISupports GetWaiting(in nsIDOMWindow aWindow, in DOMString aScope);
   [noscript] nsISupports GetActive(in nsIDOMWindow aWindow, in DOMString aScope);
 
   /*
-   * Returns a ServiceWorker.
-   *  - aLoadFailedRunnable is an optional callback that will fire on main thread if
-   *    a ServiceWorker object is returned, but later fails to load for some reason.
+   * Returns a ServiceWorker object representing the active worker controlling this
+   * window.
    */
-  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow,
-                                               in nsIRunnable aLoadFailedRunnable);
+  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
 
   /*
    * Clears ServiceWorker registrations from memory and disk for the specified
    * host.
    * - All ServiceWorker instances change their state to redundant.
    * - Existing ServiceWorker instances handling fetches will keep running.
    * - All documents will immediately stop being controlled.
    * - Unregister jobs will be queued for all registrations.
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -273,55 +273,37 @@ GetWorkerPref(const nsACString& aPref,
     else {
       result = aDefault;
     }
   }
 
   return result;
 }
 
-// This function creates a key for a SharedWorker composed by "shared|name|scriptSpec"
-// and a key for a ServiceWorker composed by "service|scope|cache|scriptSpec".
+// This function creates a key for a SharedWorker composed by "name|scriptSpec".
 // If the name contains a '|', this will be replaced by '||'.
 void
 GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName,
-                        const nsACString& aCacheName, WorkerType aWorkerType,
                         bool aPrivateBrowsing, nsCString& aKey)
 {
   aKey.Truncate();
-  NS_NAMED_LITERAL_CSTRING(sharedPrefix, "shared|");
-  NS_NAMED_LITERAL_CSTRING(servicePrefix, "service|");
-  MOZ_ASSERT(servicePrefix.Length() > sharedPrefix.Length());
-  MOZ_ASSERT(aWorkerType == WorkerTypeShared ||
-             aWorkerType == WorkerTypeService);
-  MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared, aCacheName.IsEmpty());
-  MOZ_ASSERT_IF(aWorkerType == WorkerTypeService, !aCacheName.IsEmpty());
-  MOZ_ASSERT_IF(aWorkerType == WorkerTypeService, !aPrivateBrowsing);
-  aKey.SetCapacity(servicePrefix.Length() + aScriptSpec.Length() +
-                   aName.Length() + aCacheName.Length() + 3);
-
-  aKey.Append(aWorkerType == WorkerTypeService ? servicePrefix : sharedPrefix);
+  aKey.SetCapacity(aScriptSpec.Length() + aName.Length() + 3);
   aKey.Append(aPrivateBrowsing ? "1|" : "0|");
 
   nsACString::const_iterator start, end;
   aName.BeginReading(start);
   aName.EndReading(end);
   for (; start != end; ++start) {
     if (*start == '|') {
       aKey.AppendASCII("||");
     } else {
       aKey.Append(*start);
     }
   }
 
-  if (aWorkerType == WorkerTypeService) {
-    aKey.Append('|');
-    aKey.Append(aCacheName);
-  }
-
   aKey.Append('|');
   aKey.Append(aScriptSpec);
 }
 
 void
 LoadRuntimeOptions(const char* aPrefName, void* /* aClosure */)
 {
   AssertIsOnMainThread();
@@ -1448,26 +1430,25 @@ RuntimeService::RegisterWorker(JSContext
     AssertIsOnMainThread();
 
     if (mShuttingDown) {
       JS_ReportError(aCx, "Cannot create worker during shutdown!");
       return false;
     }
   }
 
-  nsCString sharedWorkerScriptSpec;
-
   const bool isServiceWorker = aWorkerPrivate->IsServiceWorker();
+  const bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
   if (isServiceWorker) {
     AssertIsOnMainThread();
     Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_ATTEMPTS, 1);
   }
 
-  const bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
-  if (isSharedWorker || isServiceWorker) {
+  nsCString sharedWorkerScriptSpec;
+  if (isSharedWorker) {
     AssertIsOnMainThread();
 
     nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
     NS_ASSERTION(scriptURI, "Null script URI!");
 
     nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
     if (NS_FAILED(rv)) {
       NS_WARNING("GetSpec failed?!");
@@ -1527,26 +1508,20 @@ RuntimeService::RegisterWorker(JSContext
     }
     else if (isServiceWorker) {
       domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
     }
     else {
       domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
     }
 
-    if (isSharedWorker || isServiceWorker) {
+    if (isSharedWorker) {
       const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
-      const nsCString& cacheName =
-        aWorkerPrivate->IsServiceWorker() ?
-          NS_ConvertUTF16toUTF8(aWorkerPrivate->ServiceWorkerCacheName()) :
-          EmptyCString();
-
       nsAutoCString key;
       GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName,
-                              cacheName, aWorkerPrivate->Type(),
                               aWorkerPrivate->IsInPrivateBrowsing(), key);
       MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key));
 
       SharedWorkerInfo* sharedWorkerInfo =
         new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
                              sharedWorkerName);
       domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo);
     }
@@ -1575,26 +1550,30 @@ RuntimeService::RegisterWorker(JSContext
       // The navigator overridden properties should have already been read.
 
       Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
       mNavigatorPropertiesLoaded = true;
     }
 
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
-    nsTArray<WorkerPrivate*>* windowArray;
-    if (!mWindowMap.Get(window, &windowArray)) {
-      windowArray = new nsTArray<WorkerPrivate*>(1);
-      mWindowMap.Put(window, windowArray);
-    }
-
-    if (!windowArray->Contains(aWorkerPrivate)) {
-      windowArray->AppendElement(aWorkerPrivate);
-    } else {
-      MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
+    if (!isServiceWorker) {
+      // Service workers are excluded since their lifetime is separate from
+      // that of dom windows.
+      nsTArray<WorkerPrivate*>* windowArray;
+      if (!mWindowMap.Get(window, &windowArray)) {
+        windowArray = new nsTArray<WorkerPrivate*>(1);
+        mWindowMap.Put(window, windowArray);
+      }
+
+      if (!windowArray->Contains(aWorkerPrivate)) {
+        windowArray->AppendElement(aWorkerPrivate);
+      } else {
+        MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
+      }
     }
   }
 
   if (!queued && !ScheduleWorker(aCx, aWorkerPrivate)) {
     return false;
   }
 
   if (isServiceWorker) {
@@ -1642,31 +1621,25 @@ RuntimeService::UnregisterWorker(JSConte
     }
     else {
       MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
                  "Don't know about this worker!");
       domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
     }
 
 
-    if (aWorkerPrivate->IsSharedWorker() ||
-        aWorkerPrivate->IsServiceWorker()) {
+    if (aWorkerPrivate->IsSharedWorker()) {
       MatchSharedWorkerInfo match(aWorkerPrivate);
       domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
                                                    &match);
 
       if (match.mSharedWorkerInfo) {
         nsAutoCString key;
-        const nsCString& cacheName =
-          aWorkerPrivate->IsServiceWorker() ?
-            NS_ConvertUTF16toUTF8(aWorkerPrivate->ServiceWorkerCacheName()) :
-            EmptyCString();
         GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
                                 match.mSharedWorkerInfo->mName,
-                                cacheName, aWorkerPrivate->Type(),
                                 aWorkerPrivate->IsInPrivateBrowsing(), key);
         domainInfo->mSharedWorkerInfos.Remove(key);
       }
     }
 
     // See if there's a queued worker we can schedule.
     if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
         !domainInfo->mQueuedWorkers.IsEmpty()) {
@@ -1700,20 +1673,20 @@ RuntimeService::UnregisterWorker(JSConte
       aWorkerPrivate->IsServiceWorker()) {
     AssertIsOnMainThread();
     aWorkerPrivate->CloseAllSharedWorkers();
   }
 
   if (parent) {
     parent->RemoveChildWorker(aCx, aWorkerPrivate);
   }
-  else if (aWorkerPrivate->IsSharedWorker() || aWorkerPrivate->IsServiceWorker()) {
+  else if (aWorkerPrivate->IsSharedWorker()) {
     mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
   }
-  else {
+  else if (aWorkerPrivate->IsDedicatedWorker()) {
     // May be null.
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
     nsTArray<WorkerPrivate*>* windowArray;
     MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray));
 
     MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate));
 
@@ -2306,17 +2279,17 @@ RuntimeService::RemoveSharedWorkerFromWi
                                   void* aUserArg)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aData.get());
   MOZ_ASSERT(aUserArg);
 
   auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
 
-  MOZ_ASSERT(workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker());
+  MOZ_ASSERT(workerPrivate->IsSharedWorker());
 
   if (aData->RemoveElement(workerPrivate)) {
     MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
 
     if (aData->IsEmpty()) {
       return PL_DHASH_REMOVE;
     }
   }
@@ -2369,17 +2342,17 @@ RuntimeService::CancelWorkersForWindow(n
     if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
       return;
     }
     JSContext* cx = jsapi.cx();
 
     for (uint32_t index = 0; index < workers.Length(); index++) {
       WorkerPrivate*& worker = workers[index];
 
-      if (worker->IsSharedWorker() || worker->IsServiceWorker()) {
+      if (worker->IsSharedWorker()) {
         worker->CloseSharedWorkersForWindow(aWindow);
       } else if (!worker->Cancel(cx)) {
         JS_ReportPendingException(cx);
       }
     }
   }
 }
 
@@ -2427,69 +2400,64 @@ RuntimeService::ThawWorkersForWindow(nsP
       if (!workers[index]->Thaw(cx, aWindow)) {
         JS_ReportPendingException(cx);
       }
     }
   }
 }
 
 nsresult
-RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal,
-                                           const nsAString& aScriptURL,
-                                           const nsACString& aName,
-                                           WorkerType aType,
-                                           SharedWorker** aSharedWorker)
+RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal,
+                                   const nsAString& aScriptURL,
+                                   const nsACString& aName,
+                                   SharedWorker** aSharedWorker)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aType == WorkerTypeShared || aType == WorkerTypeService);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(window);
 
   JSContext* cx = aGlobal.Context();
 
   WorkerLoadInfo loadInfo;
   nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
                                            false,
                                            WorkerPrivate::OverrideLoadGroup,
-                                           aType, &loadInfo);
+                                           WorkerTypeShared, &loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName, aType,
+  return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName,
                                         aSharedWorker);
 }
 
 nsresult
 RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
                                                WorkerLoadInfo* aLoadInfo,
                                                const nsAString& aScriptURL,
                                                const nsACString& aName,
-                                               WorkerType aType,
                                                SharedWorker** aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aLoadInfo);
   MOZ_ASSERT(aLoadInfo->mResolvedScriptURI);
-  MOZ_ASSERT_IF(aType == WorkerTypeService, aLoadInfo->mServiceWorkerID > 0);
 
   nsRefPtr<WorkerPrivate> workerPrivate;
   {
     MutexAutoLock lock(mMutex);
 
     WorkerDomainInfo* domainInfo;
     SharedWorkerInfo* sharedWorkerInfo;
 
     nsCString scriptSpec;
     nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString key;
     GenerateSharedWorkerKey(scriptSpec, aName,
-                            NS_ConvertUTF16toUTF8(aLoadInfo->mServiceWorkerCacheName),
-                            aType, aLoadInfo->mPrivateBrowsing, key);
+                            aLoadInfo->mPrivateBrowsing, key);
 
     if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) &&
         domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) {
       workerPrivate = sharedWorkerInfo->mWorkerPrivate;
     }
   }
 
   // Keep a reference to the window before spawning the worker. If the worker is
@@ -2498,17 +2466,17 @@ RuntimeService::CreateSharedWorkerFromLo
   // will reset the loadInfo's window.
   nsCOMPtr<nsPIDOMWindow> window = aLoadInfo->mWindow;
 
   bool created = false;
   ErrorResult rv;
   if (!workerPrivate) {
     workerPrivate =
       WorkerPrivate::Constructor(aCx, aScriptURL, false,
-                                 aType, aName, aLoadInfo, rv);
+                                 WorkerTypeShared, aName, aLoadInfo, rv);
     NS_ENSURE_TRUE(workerPrivate, rv.StealNSResult());
 
     created = true;
   } else {
     // If we're attaching to an existing SharedWorker private, then we
     // must update the overriden load group to account for our document's
     // load group.
     workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
@@ -2549,36 +2517,30 @@ RuntimeService::CreateSharedWorkerFromLo
   return NS_OK;
 }
 
 void
 RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWorkerPrivate);
-  MOZ_ASSERT(aWorkerPrivate->IsSharedWorker() ||
-             aWorkerPrivate->IsServiceWorker());
+  MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
 
   MutexAutoLock lock(mMutex);
 
   WorkerDomainInfo* domainInfo;
   if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) {
     MatchSharedWorkerInfo match(aWorkerPrivate);
     domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
                                                  &match);
 
     if (match.mSharedWorkerInfo) {
       nsAutoCString key;
-      const nsCString& cacheName =
-        aWorkerPrivate->IsServiceWorker() ?
-          NS_ConvertUTF16toUTF8(aWorkerPrivate->ServiceWorkerCacheName()) :
-          EmptyCString();
       GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
                               match.mSharedWorkerInfo->mName,
-                              cacheName, aWorkerPrivate->Type(),
                               aWorkerPrivate->IsInPrivateBrowsing(), key);
       domainInfo->mSharedWorkerInfos.Remove(key);
     }
   }
 }
 
 void
 RuntimeService::NoteIdleThread(WorkerThread* aThread)
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -147,32 +147,17 @@ public:
 
   void
   ThawWorkersForWindow(nsPIDOMWindow* aWindow);
 
   nsresult
   CreateSharedWorker(const GlobalObject& aGlobal,
                      const nsAString& aScriptURL,
                      const nsACString& aName,
-                     SharedWorker** aSharedWorker)
-  {
-    return CreateSharedWorkerInternal(aGlobal, aScriptURL, aName,
-                                      WorkerTypeShared, aSharedWorker);
-  }
-
-  nsresult
-  CreateSharedWorkerForServiceWorkerFromLoadInfo(JSContext* aCx,
-                                                 WorkerLoadInfo* aLoadInfo,
-                                                 const nsAString& aScriptURL,
-                                                 const nsACString& aScope,
-                                                 SharedWorker** aSharedWorker)
-  {
-    return CreateSharedWorkerFromLoadInfo(aCx, aLoadInfo, aScriptURL, aScope,
-                                          WorkerTypeService, aSharedWorker);
-  }
+                     SharedWorker** aSharedWorker);
 
   void
   ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
 
   const NavigatorProperties&
   GetNavigatorProperties() const
   {
     return mNavigatorProperties;
@@ -304,26 +289,18 @@ private:
 
   static void
   WorkerPrefChanged(const char* aPrefName, void* aClosure);
 
   static void
   JSVersionChanged(const char* aPrefName, void* aClosure);
 
   nsresult
-  CreateSharedWorkerInternal(const GlobalObject& aGlobal,
-                             const nsAString& aScriptURL,
-                             const nsACString& aName,
-                             WorkerType aType,
-                             SharedWorker** aSharedWorker);
-
-  nsresult
   CreateSharedWorkerFromLoadInfo(JSContext* aCx,
                                  WorkerLoadInfo* aLoadInfo,
                                  const nsAString& aScriptURL,
                                  const nsACString& aName,
-                                 WorkerType aType,
                                  SharedWorker** aSharedWorker);
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_runtimeservice_h__ */
--- a/dom/workers/ServiceWorker.cpp
+++ b/dom/workers/ServiceWorker.cpp
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorker.h"
 
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
 #include "ServiceWorkerClient.h"
 #include "ServiceWorkerManager.h"
-#include "SharedWorker.h"
+#include "ServiceWorkerPrivate.h"
 #include "WorkerPrivate.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 
 #ifdef XP_WIN
 #undef PostMessage
@@ -37,25 +37,22 @@ ServiceWorkerVisible(JSContext* aCx, JSO
 
   ServiceWorkerGlobalScope* scope = nullptr;
   nsresult rv = UnwrapObject<prototypes::id::ServiceWorkerGlobalScope_workers,
                              mozilla::dom::ServiceWorkerGlobalScopeBinding_workers::NativeType>(aObj, scope);
   return NS_SUCCEEDED(rv);
 }
 
 ServiceWorker::ServiceWorker(nsPIDOMWindow* aWindow,
-                             ServiceWorkerInfo* aInfo,
-                             SharedWorker* aSharedWorker)
+                             ServiceWorkerInfo* aInfo)
   : DOMEventTargetHelper(aWindow),
-    mInfo(aInfo),
-    mSharedWorker(aSharedWorker)
+    mInfo(aInfo)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aInfo);
-  MOZ_ASSERT(mSharedWorker);
 
   // This will update our state too.
   mInfo->AppendWorker(this);
 }
 
 ServiceWorker::~ServiceWorker()
 {
   AssertIsOnMainThread();
@@ -63,19 +60,16 @@ ServiceWorker::~ServiceWorker()
 }
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorker)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorker, DOMEventTargetHelper,
-                                   mSharedWorker)
-
 JSObject*
 ServiceWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   AssertIsOnMainThread();
 
   return ServiceWorkerBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -97,33 +91,19 @@ ServiceWorker::PostMessage(JSContext* aC
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
   if (!window || !window->GetExtantDoc()) {
     NS_WARNING("Trying to call post message from an invalid dom object.");
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  WorkerPrivate* workerPrivate = GetWorkerPrivate();
-  MOZ_ASSERT(workerPrivate);
-
-  nsAutoPtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
-
-  workerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
-                                            clientInfo, aRv);
-}
-
-WorkerPrivate*
-ServiceWorker::GetWorkerPrivate() const
-{
-  // At some point in the future, this may be optimized to terminate a worker
-  // that hasn't been used in a certain amount of time or when there is memory
-  // pressure or similar.
-  MOZ_ASSERT(mSharedWorker);
-  return mSharedWorker->GetWorkerPrivate();
+  UniquePtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
+  ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate();
+  aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
 }
 
 void
 ServiceWorker::QueueStateChangeEvent(ServiceWorkerState aState)
 {
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethodWithArg<ServiceWorkerState>(this,
                                                     &ServiceWorker::DispatchStateChange,
--- a/dom/workers/ServiceWorker.h
+++ b/dom/workers/ServiceWorker.h
@@ -25,17 +25,16 @@ class SharedWorker;
 bool
 ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
 
 class ServiceWorker final : public DOMEventTargetHelper
 {
   friend class ServiceWorkerManager;
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorker, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(statechange)
   IMPL_EVENT_HANDLER(error)
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   ServiceWorkerState
@@ -48,18 +47,16 @@ public:
   SetState(ServiceWorkerState aState)
   {
     mState = aState;
   }
 
   void
   GetScriptURL(nsString& aURL) const;
 
-  const ServiceWorkerInfo* Info() const { return mInfo; }
-
   void
   DispatchStateChange(ServiceWorkerState aState)
   {
     SetState(aState);
     DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
   }
 
   void
@@ -69,34 +66,24 @@ public:
 #undef PostMessage
 #endif
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
               ErrorResult& aRv);
 
-  WorkerPrivate*
-  GetWorkerPrivate() const;
-
 private:
   // This class can only be created from the ServiceWorkerManager.
-  ServiceWorker(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo,
-                SharedWorker* aSharedWorker);
+  ServiceWorker(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo);
 
   // This class is reference-counted and will be destroyed from Release().
   ~ServiceWorker();
 
   ServiceWorkerState mState;
   const nsRefPtr<ServiceWorkerInfo> mInfo;
-
-  // To allow ServiceWorkers to potentially drop the backing DOMEventTargetHelper and
-  // re-instantiate it later, they simply own a SharedWorker member that
-  // can be released and recreated as required rather than re-implement some of
-  // the SharedWorker logic.
-  nsRefPtr<SharedWorker> mSharedWorker;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworker_h__
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -235,17 +235,16 @@ ServiceWorkerContainer::GetController()
       return nullptr;
     }
 
     // TODO: What should we do here if the ServiceWorker script fails to load?
     //       In theory the DOM ServiceWorker object can exist without the worker
     //       thread running, but it seems our design does not expect that.
     nsCOMPtr<nsISupports> serviceWorker;
     rv = swm->GetDocumentController(GetOwner(),
-                                    nullptr, // aLoadFailedRunnable
                                     getter_AddRefs(serviceWorker));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
 
     mControllerWorker =
       static_cast<workers::ServiceWorker*>(serviceWorker.get());
   }
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -1,17 +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=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 "ServiceWorkerEvents.h"
 #include "ServiceWorkerClient.h"
-#include "ServiceWorkerManager.h"
 
 #include "nsIHttpChannelInternal.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIOutputStream.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
@@ -68,22 +67,22 @@ FetchEvent::FetchEvent(EventTarget* aOwn
 }
 
 FetchEvent::~FetchEvent()
 {
 }
 
 void
 FetchEvent::PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                     nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo)
+                     const nsACString& aScriptSpec,
+                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
 {
   mChannel = aChannel;
-  mServiceWorker = aServiceWorker;
-  mClientInfo = aClientInfo;
+  mScriptSpec.Assign(aScriptSpec);
+  mClientInfo = Move(aClientInfo);
 }
 
 /*static*/ already_AddRefed<FetchEvent>
 FetchEvent::Constructor(const GlobalObject& aGlobal,
                         const nsAString& aType,
                         const FetchEventInit& aOptions,
                         ErrorResult& aRv)
 {
@@ -102,29 +101,29 @@ FetchEvent::Constructor(const GlobalObje
   return e.forget();
 }
 
 namespace {
 
 class FinishResponse final : public nsRunnable
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
   nsRefPtr<InternalResponse> mInternalResponse;
   ChannelInfo mWorkerChannelInfo;
+  const nsCString mScriptSpec;
 
 public:
   FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                 nsMainThreadPtrHandle<ServiceWorker> aServiceWorker,
                  InternalResponse* aInternalResponse,
-                 const ChannelInfo& aWorkerChannelInfo)
+                 const ChannelInfo& aWorkerChannelInfo,
+                 const nsACString& aScriptSpec)
     : mChannel(aChannel)
-    , mServiceWorker(aServiceWorker)
     , mInternalResponse(aInternalResponse)
     , mWorkerChannelInfo(aWorkerChannelInfo)
+    , mScriptSpec(aScriptSpec)
   {
   }
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
 
@@ -165,17 +164,17 @@ public:
     AssertIsOnMainThread();
 
     nsresult rv;
     nsCOMPtr<nsIURI> uri;
     nsAutoCString url;
     mInternalResponse->GetUnfilteredUrl(url);
     if (url.IsEmpty()) {
       // Synthetic response. The buck stops at the worker script.
-      url = mServiceWorker->Info()->ScriptSpec();
+      url = mScriptSpec;
     }
     rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr);
     NS_ENSURE_SUCCESS(rv, false);
 
     nsCOMPtr<nsIChannel> underlyingChannel;
     rv = mChannel->GetChannel(getter_AddRefs(underlyingChannel));
     NS_ENSURE_SUCCESS(rv, false);
     NS_ENSURE_TRUE(underlyingChannel, false);
@@ -187,72 +186,72 @@ public:
     NS_ENSURE_SUCCESS(rv, false);
     return decision == nsIContentPolicy::ACCEPT;
   }
 };
 
 class RespondWithHandler final : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
   const RequestMode mRequestMode;
   const DebugOnly<bool> mIsClientRequest;
   const bool mIsNavigationRequest;
+  const nsCString mScriptSpec;
 public:
   NS_DECL_ISUPPORTS
 
   RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
                      RequestMode aRequestMode, bool aIsClientRequest,
-                     bool aIsNavigationRequest)
+                     bool aIsNavigationRequest,
+                     const nsACString& aScriptSpec)
     : mInterceptedChannel(aChannel)
-    , mServiceWorker(aServiceWorker)
     , mRequestMode(aRequestMode)
     , mIsClientRequest(aIsClientRequest)
     , mIsNavigationRequest(aIsNavigationRequest)
+    , mScriptSpec(aScriptSpec)
   {
   }
 
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   void CancelRequest(nsresult aStatus);
 private:
   ~RespondWithHandler() {}
 };
 
 struct RespondWithClosure
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
   nsRefPtr<InternalResponse> mInternalResponse;
   ChannelInfo mWorkerChannelInfo;
+  const nsCString mScriptSpec;
 
   RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
                      InternalResponse* aInternalResponse,
-                     const ChannelInfo& aWorkerChannelInfo)
+                     const ChannelInfo& aWorkerChannelInfo,
+                     const nsCString& aScriptSpec)
     : mInterceptedChannel(aChannel)
-    , mServiceWorker(aServiceWorker)
     , mInternalResponse(aInternalResponse)
     , mWorkerChannelInfo(aWorkerChannelInfo)
+    , mScriptSpec(aScriptSpec)
   {
   }
 };
 
 void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
 {
   nsAutoPtr<RespondWithClosure> data(static_cast<RespondWithClosure*>(aClosure));
   nsCOMPtr<nsIRunnable> event;
   if (NS_SUCCEEDED(aStatus)) {
     event = new FinishResponse(data->mInterceptedChannel,
-                               data->mServiceWorker,
                                data->mInternalResponse,
-                               data->mWorkerChannelInfo);
+                               data->mWorkerChannelInfo,
+                               data->mScriptSpec);
   } else {
     event = new CancelChannelRunnable(data->mInterceptedChannel,
                                       NS_ERROR_INTERCEPTION_FAILED);
   }
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(event)));
 }
 
 class MOZ_STACK_CLASS AutoCancel
@@ -345,18 +344,19 @@ RespondWithHandler::ResolvedCallback(JSC
     return;
   }
 
   nsRefPtr<InternalResponse> ir = response->GetInternalResponse();
   if (NS_WARN_IF(!ir)) {
     return;
   }
 
-  nsAutoPtr<RespondWithClosure> closure(
-      new RespondWithClosure(mInterceptedChannel, mServiceWorker, ir, worker->GetChannelInfo()));
+  nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, ir,
+                                                               worker->GetChannelInfo(),
+                                                               mScriptSpec));
   nsCOMPtr<nsIInputStream> body;
   ir->GetUnfilteredBody(getter_AddRefs(body));
   // Errors and redirects may not have a body.
   if (body) {
     response->SetBodyUsed();
 
     nsCOMPtr<nsIOutputStream> responseBody;
     rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
@@ -406,18 +406,18 @@ FetchEvent::RespondWith(Promise& aArg, E
   if (mWaitToRespond) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   nsRefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
   mWaitToRespond = true;
   nsRefPtr<RespondWithHandler> handler =
-    new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode(),
-                           ir->IsClientRequest(), ir->IsNavigationRequest());
+    new RespondWithHandler(mChannel, mRequest->Mode(), ir->IsClientRequest(),
+                           ir->IsNavigationRequest(), mScriptSpec);
   aArg.AppendNativeHandler(handler);
 }
 
 already_AddRefed<ServiceWorkerClient>
 FetchEvent::GetClient()
 {
   if (!mClient) {
     if (!mClientInfo) {
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -46,20 +46,20 @@ public:
                         nsresult aStatus);
 
   NS_IMETHOD Run() override;
 };
 
 class FetchEvent final : public Event
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
   nsRefPtr<ServiceWorkerClient> mClient;
   nsRefPtr<Request> mRequest;
-  nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
+  nsCString mScriptSpec;
+  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
   bool mIsReload;
   bool mWaitToRespond;
 protected:
   explicit FetchEvent(EventTarget* aOwner);
   ~FetchEvent();
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -67,18 +67,18 @@ public:
   NS_FORWARD_TO_EVENT
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return FetchEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
   void PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo);
+                const nsACString& aScriptSpec,
+                UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
 
   static already_AddRefed<FetchEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const FetchEventInit& aOptions,
               ErrorResult& aRv);
 
   bool
@@ -198,17 +198,16 @@ private:
 
   NS_METHOD EnsureDecodedText();
   uint8_t* GetContentsCopy();
 };
 
 class PushEvent final : public ExtendableEvent
 {
   nsRefPtr<PushMessageData> mData;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
 
 protected:
   explicit PushEvent(mozilla::dom::EventTarget* aOwner);
   ~PushEvent() {}
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PushEvent, ExtendableEvent)
@@ -230,22 +229,18 @@ public:
               const nsAString& aType,
               const PushEventInit& aOptions,
               ErrorResult& aRv)
   {
     nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
     return Constructor(owner, aType, aOptions, aRv);
   }
 
-  void PostInit(nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
-  {
-    mServiceWorker = aServiceWorker;
-  }
-
-  PushMessageData* GetData() const
+  PushMessageData*
+  GetData() const
   {
     return mData;
   }
 };
 #endif /* ! MOZ_SIMPLEPUSH */
 
 END_WORKERS_NAMESPACE
 #endif /* mozilla_dom_workers_serviceworkerevents_h__ */
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -57,16 +57,17 @@
 #include "nsQueryObject.h"
 #include "nsTArray.h"
 
 #include "RuntimeService.h"
 #include "ServiceWorker.h"
 #include "ServiceWorkerClient.h"
 #include "ServiceWorkerContainer.h"
 #include "ServiceWorkerManagerChild.h"
+#include "ServiceWorkerPrivate.h"
 #include "ServiceWorkerRegistrar.h"
 #include "ServiceWorkerRegistration.h"
 #include "ServiceWorkerScriptCache.h"
 #include "ServiceWorkerEvents.h"
 #include "SharedWorker.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -105,26 +106,16 @@ static_assert(nsIHttpChannelInternal::RE
               "RequestRedirect enumeration value should make Necko Redirect mode value.");
 static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual),
               "RequestRedirect enumeration value should make Necko Redirect mode value.");
 static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_),
               "RequestRedirect enumeration value should make Necko Redirect mode value.");
 
 static StaticRefPtr<ServiceWorkerManager> gInstance;
 
-// Tracks the "dom.disable_open_click_delay" preference.  Modified on main
-// thread, read on worker threads. This is set once in the ServiceWorkerManager
-// constructor before any service workers are spawned.
-// It is updated every time a "notificationclick" event is dispatched. While
-// this is done without synchronization, at the worst, the thread will just get
-// an older value within which a popup is allowed to be displayed, which will
-// still be a valid value since it was set in the constructor. I (:nsm) don't
-// think this needs to be synchronized.
-Atomic<uint32_t> gDOMDisableOpenClickDelay(0);
-
 struct ServiceWorkerManager::RegistrationDataPerPrincipal
 {
   // Ordered list of scopes for glob matching.
   // Each entry is an absolute URL representing the scope.
   // Each value of the hash table is an array of an absolute URLs representing
   // the scopes.
   //
   // An array is used for now since the number of controlled scopes per
@@ -405,18 +396,16 @@ NS_INTERFACE_MAP_BEGIN(ServiceWorkerMana
 NS_INTERFACE_MAP_END
 
 ServiceWorkerManager::ServiceWorkerManager()
   : mActor(nullptr)
   , mShuttingDown(false)
 {
   // Register this component to PBackground.
   MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this));
-
-  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mRegistrationInfos.Clear();
   MOZ_ASSERT(!mActor);
 }
@@ -455,112 +444,76 @@ class ContinueLifecycleTask : public nsI
 {
   NS_DECL_ISUPPORTS
 
 protected:
   virtual ~ContinueLifecycleTask()
   { }
 
 public:
-  virtual void ContinueAfterWorkerEvent(bool aSuccess,
-                                        bool aActivateImmediately) = 0;
+  virtual void ContinueAfterWorkerEvent(bool aSuccess) = 0;
 };
 
 NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
 
 class ServiceWorkerRegisterJob;
 
 class ContinueInstallTask final : public ContinueLifecycleTask
 {
   nsRefPtr<ServiceWorkerRegisterJob> mJob;
 
 public:
   explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
     : mJob(aJob)
   { }
 
-  void ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately) override;
+  void ContinueAfterWorkerEvent(bool aSuccess) override;
 };
 
 class ContinueActivateTask final : public ContinueLifecycleTask
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
 
 public:
   explicit ContinueActivateTask(ServiceWorkerRegistrationInfo* aReg)
     : mRegistration(aReg)
   { }
 
   void
-  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) override;
+  ContinueAfterWorkerEvent(bool aSuccess) override;
 };
 
-class ContinueLifecycleRunnable final : public nsRunnable
+class ContinueLifecycleRunnable final : public LifeCycleEventCallback
 {
   nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mSuccess;
-  bool mActivateImmediately;
 
 public:
-  ContinueLifecycleRunnable(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
-                            bool aSuccess,
-                            bool aActivateImmediately)
+  explicit ContinueLifecycleRunnable(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask)
     : mTask(aTask)
-    , mSuccess(aSuccess)
-    , mActivateImmediately(aActivateImmediately)
+    , mSuccess(false)
   {
-    MOZ_ASSERT(!NS_IsMainThread());
+    AssertIsOnMainThread();
+  }
+
+  void
+  SetResult(bool aResult) override
+  {
+    mSuccess = aResult;
   }
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
-    mTask->ContinueAfterWorkerEvent(mSuccess, mActivateImmediately);
+    mTask->ContinueAfterWorkerEvent(mSuccess);
     return NS_OK;
   }
 };
 
-/*
- * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
- * since it fires the event. This is ok since there can't be nested
- * ServiceWorkers, so the parent thread -> worker thread requirement for
- * runnables is satisfied.
- */
-class LifecycleEventWorkerRunnable final : public WorkerRunnable
-{
-  nsString mEventName;
-  const nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  const nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
-
-public:
-  LifecycleEventWorkerRunnable(nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                               const nsString& aEventName,
-                               const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask)
-      : WorkerRunnable(aServiceWorker->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
-      , mEventName(aEventName)
-      , mServiceWorker(aServiceWorker)
-      , mTask(aTask)
-  {
-    AssertIsOnMainThread();
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    return DispatchLifecycleEvent(aCx, aWorkerPrivate);
-  }
-
-private:
-  bool
-  DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
-
-};
-
 class ServiceWorkerResolveWindowPromiseOnUpdateCallback final : public ServiceWorkerUpdateFinishCallback
 {
   nsRefPtr<nsPIDOMWindow> mWindow;
   // The promise "returned" by the call to Update up to
   // navigator.serviceWorker.register().
   nsRefPtr<Promise> mPromise;
 
   ~ServiceWorkerResolveWindowPromiseOnUpdateCallback()
@@ -627,52 +580,22 @@ public:
 
 class ContinueUpdateRunnable final : public nsRunnable
 {
   nsMainThreadPtrHandle<nsISupports> mJob;
 public:
   explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<nsISupports> aJob)
     : mJob(aJob)
   {
-    MOZ_ASSERT(!NS_IsMainThread());
+    AssertIsOnMainThread();
   }
 
   NS_IMETHOD Run();
 };
 
-class CheckWorkerEvaluationAndContinueUpdateWorkerRunnable final : public WorkerRunnable
-{
-  const nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  const nsMainThreadPtrHandle<nsISupports> mJob;
-public:
-  CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                                                       const nsMainThreadPtrHandle<nsISupports> aJob)
-    : WorkerRunnable(aServiceWorker->GetWorkerPrivate(), WorkerThreadUnchangedBusyCount)
-    , mServiceWorker(aServiceWorker)
-    , mJob(aJob)
-  {
-    AssertIsOnMainThread();
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    aWorkerPrivate->AssertIsOnWorkerThread();
-    if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
-      nsRefPtr<ContinueUpdateRunnable> r = new ContinueUpdateRunnable(mJob);
-      nsresult rv = NS_DispatchToMainThread(r);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to dispatch ContinueUpdateRunnable to main thread.");
-      }
-    }
-
-    return true;
-  }
-};
-
 namespace {
 
 /**
  * The spec mandates slightly different behaviors for computing the scope
  * prefix string in case a Service-Worker-Allowed header is specified versus
  * when it's not available.
  *
  * With the header:
@@ -1075,38 +998,27 @@ public:
     nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs
       <StorensRefPtrPassByPtr<ServiceWorkerManager>, nsCString>
       (this, &ServiceWorkerRegisterJob::FailScopeUpdate, swm, scopeKey);
 
     MOZ_ASSERT(!mUpdateAndInstallInfo);
     mUpdateAndInstallInfo =
       new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec,
                             aNewCacheName);
-    nsRefPtr<ServiceWorker> serviceWorker;
-    rv = swm->CreateServiceWorker(mRegistration->mPrincipal,
-                                  mUpdateAndInstallInfo,
-                                  failRunnable,
-                                  getter_AddRefs(serviceWorker));
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return FailScopeUpdate(swm, scopeKey);
-    }
 
     nsRefPtr<ServiceWorkerJob> upcasted = this;
     nsMainThreadPtrHandle<nsISupports> handle(
         new nsMainThreadPtrHolder<nsISupports>(upcasted));
-
-    nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-      new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
-    nsRefPtr<CheckWorkerEvaluationAndContinueUpdateWorkerRunnable> r =
-      new CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(serviceWorkerHandle, handle);
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    bool ok = r->Dispatch(jsapi.cx());
-    if (NS_WARN_IF(!ok)) {
+    nsRefPtr<nsRunnable> callback = new ContinueUpdateRunnable(handle);
+
+    ServiceWorkerPrivate* workerPrivate =
+      mUpdateAndInstallInfo->WorkerPrivate();
+    rv = workerPrivate->ContinueOnSuccessfulScriptEvaluation(callback);
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
       return FailScopeUpdate(swm, scopeKey);
     }
   }
 
   void
   FailScopeUpdate(ServiceWorkerManager* aSwm, const nsACString& aScopeKey)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -1166,18 +1078,18 @@ public:
 
     nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
     if (mCanceled) {
       return Fail(NS_ERROR_DOM_ABORT_ERR);
     }
 
     // Begin [[Install]] atomic step 4.
     if (mRegistration->mInstallingWorker) {
-      // FIXME(nsm): Terminate and stuff
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
+      mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
     }
 
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER);
 
     mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget();
     mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
@@ -1188,46 +1100,35 @@ public:
     nsCOMPtr<nsIRunnable> upr =
       NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
         swm,
         &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
         mRegistration);
 
     NS_DispatchToMainThread(upr);
 
-    // Call ContinueAfterInstallEvent(false, false) on main thread if the SW
+    // Call ContinueAfterInstallEvent(false) on main thread if the SW
     // script fails to load.
-    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool, bool>
-      (this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false, false);
-
-    nsRefPtr<ServiceWorker> serviceWorker;
-    rv = swm->CreateServiceWorker(mRegistration->mPrincipal,
-                                  mRegistration->mInstallingWorker,
-                                  failRunnable,
-                                  getter_AddRefs(serviceWorker));
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      ContinueAfterInstallEvent(false /* aSuccess */, false /* aActivateImmediately */);
-      return;
-    }
-
-    nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
-        new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
-
-    nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-      new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
-    nsRefPtr<LifecycleEventWorkerRunnable> r =
-      new LifecycleEventWorkerRunnable(serviceWorkerHandle, NS_LITERAL_STRING("install"), handle);
-
-    AutoJSAPI jsapi;
-    jsapi.Init();
+    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool>
+      (this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false);
+
+    nsMainThreadPtrHandle<ContinueLifecycleTask> installTask(
+      new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
+    nsRefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask);
 
     // This triggers Step 4.7 "Queue a task to run the following substeps..."
     // which sends the install event to the worker.
-    r->Dispatch(jsapi.cx());
+    ServiceWorkerPrivate* workerPrivate =
+      mRegistration->mInstallingWorker->WorkerPrivate();
+    rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
+                                           callback, failRunnable);
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ContinueAfterInstallEvent(false /* aSuccess */);
+    }
   }
 
 private:
   void
   Update()
   {
     // Since Update() is called synchronously from Start(), we can assert this.
     MOZ_ASSERT(!mCanceled);
@@ -1244,18 +1145,18 @@ private:
   {
     AssertIsOnMainThread();
     nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
     if (mCanceled) {
       return Fail(NS_ERROR_DOM_ABORT_ERR);
     }
 
     if (mRegistration->mInstallingWorker) {
-      // FIXME(nsm): "Terminate installing worker".
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
+      // This will terminate the installing worker thread.
       mRegistration->mInstallingWorker = nullptr;
     }
 
     nsRefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest();
     nsAutoString cacheName;
 
     // 9.2.20 If newestWorker is not null, and newestWorker's script url is
     // equal to registration's registering script url and response is a
@@ -1316,17 +1217,17 @@ private:
     // FailCommon relies on it.
     // FailCommon does check for cancellation, but let's be safe here.
     nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
     callback->UpdateFailed(aRv);
     FailCommon(aRv);
   }
 
   void
-  ContinueAfterInstallEvent(bool aInstallEventSuccess, bool aActivateImmediately)
+  ContinueAfterInstallEvent(bool aInstallEventSuccess)
   {
     if (mCanceled) {
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     if (!mRegistration->mInstallingWorker) {
       NS_WARNING("mInstallingWorker was null.");
       return Done(NS_ERROR_DOM_ABORT_ERR);
@@ -1341,17 +1242,17 @@ private:
       swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                      WhichServiceWorker::INSTALLING_WORKER);
       swm->MaybeRemoveRegistration(mRegistration);
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     // "If registration's waiting worker is not null"
     if (mRegistration->mWaitingWorker) {
-      // FIXME(nsm): Terminate
+      mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker();
       mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
 
       nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
                                                          mRegistration->mWaitingWorker->CacheName());
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to purge the old waiting cache.");
       }
     }
@@ -1402,21 +1303,21 @@ ContinueUpdateRunnable::Run()
   AssertIsOnMainThread();
   nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
   nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
   upjob->ContinueInstall();
   return NS_OK;
 }
 
 void
-ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
+ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess)
 {
   // This does not start the job immediately if there are other jobs in the
   // queue, which captures the "atomic" behaviour we want.
-  mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
+  mJob->ContinueAfterInstallEvent(aSuccess);
 }
 
 static bool
 IsFromAuthenticatedOriginInternal(nsIDocument* aDoc)
 {
   nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
 
   bool authenticatedOrigin = false;
@@ -1616,355 +1517,26 @@ ServiceWorkerManager::AppendPendingOpera
   MOZ_ASSERT(aRunnable);
 
   if (!mShuttingDown) {
     PendingOperation* opt = mPendingOperations.AppendElement();
     opt->mRunnable = aRunnable;
   }
 }
 
-namespace {
-// Just holds a ref to a ServiceWorker until the Promise is fulfilled.
-class KeepAliveHandler : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-
-protected:
-  virtual ~KeepAliveHandler()
-  {}
-
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  explicit KeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
-    : mServiceWorker(aServiceWorker)
-  {}
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-#ifdef DEBUG
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
-#endif
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-#ifdef DEBUG
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
-#endif
-  }
-};
-
-NS_IMPL_ISUPPORTS0(KeepAliveHandler)
-
-void
-DummyCallback(nsITimer* aTimer, void* aClosure)
-{
-  // Nothing.
-}
-
-class AllowWindowInteractionKeepAliveHandler;
-
-class ClearWindowAllowedRunnable final : public WorkerRunnable
-{
-public:
-  ClearWindowAllowedRunnable(WorkerPrivate* aWorkerPrivate,
-                             AllowWindowInteractionKeepAliveHandler* aHandler)
-  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-  , mHandler(aHandler)
-  { }
-
-private:
-  bool
-  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    // WorkerRunnable asserts that the dispatch is from parent thread if
-    // the busy count modification is WorkerThreadUnchangedBusyCount.
-    // Since this runnable will be dispatched from the timer thread, we override
-    // PreDispatch and PostDispatch to skip the check.
-    return true;
-  }
-
-  void
-  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-               bool aDispatchResult) override
-  {
-    // Silence bad assertions.
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
-
-  nsRefPtr<AllowWindowInteractionKeepAliveHandler> mHandler;
-};
-
-class AllowWindowInteractionKeepAliveHandler final : public KeepAliveHandler
-{
-  friend class ClearWindowAllowedRunnable;
-  nsCOMPtr<nsITimer> mTimer;
-
-  ~AllowWindowInteractionKeepAliveHandler()
-  {
-    MOZ_ASSERT(!mTimer);
-  }
-
-  void
-  ClearWindowAllowed(WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (mTimer) {
-      aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
-      mTimer->Cancel();
-      mTimer = nullptr;
-      MOZ_ALWAYS_TRUE(aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), false));
-    }
-  }
-
-  void
-  StartClearWindowTimer(WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(!mTimer);
-
-    nsresult rv;
-    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    nsRefPtr<ClearWindowAllowedRunnable> r =
-      new ClearWindowAllowedRunnable(aWorkerPrivate, this);
-
-    nsRefPtr<TimerThreadEventTarget> target =
-      new TimerThreadEventTarget(aWorkerPrivate, r);
-
-    rv = timer->SetTarget(target);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    // The important stuff that *has* to be reversed.
-    if (NS_WARN_IF(!aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), true))) {
-      return;
-    }
-    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
-    timer.swap(mTimer);
-
-    // We swap first and then initialize the timer so that even if initializing
-    // fails, we still clean the busy count and interaction count correctly.
-    // The timer can't be initialized before modifying the busy count since the
-    // timer thread could run and call the timeout but the worker may
-    // already be terminating and modifying the busy count could fail.
-    rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, gDOMDisableOpenClickDelay, nsITimer::TYPE_ONE_SHOT);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      ClearWindowAllowed(aWorkerPrivate);
-      return;
-    }
-  }
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  AllowWindowInteractionKeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                                         WorkerPrivate* aWorkerPrivate)
-    : KeepAliveHandler(aServiceWorker)
-  {
-    StartClearWindowTimer(aWorkerPrivate);
-  }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
-    ClearWindowAllowed(worker);
-    KeepAliveHandler::ResolvedCallback(aCx, aValue);
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
-    ClearWindowAllowed(worker);
-    KeepAliveHandler::RejectedCallback(aCx, aValue);
-  }
-};
-
-NS_IMPL_ISUPPORTS_INHERITED0(AllowWindowInteractionKeepAliveHandler, KeepAliveHandler)
-
-bool
-ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-{
-  mHandler->ClearWindowAllowed(aWorkerPrivate);
-  return true;
-}
-
-// Returns a Promise if the event was successfully dispatched and no exceptions
-// were raised, otherwise returns null.
-already_AddRefed<Promise>
-DispatchExtendableEventOnWorkerScope(JSContext* aCx,
-                                     WorkerGlobalScope* aWorkerScope,
-                                     ExtendableEvent* aEvent)
-{
-  MOZ_ASSERT(aWorkerScope);
-  MOZ_ASSERT(aEvent);
-  nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
-  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
-
-  ErrorResult result;
-  result = aWorkerScope->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
-  if (result.Failed() || internalEvent->mFlags.mExceptionHasBeenRisen) {
-    result.SuppressException();
-    return nullptr;
-  }
-
-  nsRefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
-  if (!waitUntilPromise) {
-    ErrorResult result;
-    waitUntilPromise =
-      Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
-    if (NS_WARN_IF(result.Failed())) {
-      result.SuppressException();
-      return nullptr;
-    }
-  }
-
-  MOZ_ASSERT(waitUntilPromise);
-  return waitUntilPromise.forget();
-}
-} // namespace
-
-/*
- * Used to handle ExtendableEvent::waitUntil() and proceed with
- * installation/activation.
- */
-class LifecycleEventPromiseHandler final : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  bool mActivateImmediately;
-
-  virtual
-  ~LifecycleEventPromiseHandler()
-  { }
-
-public:
-  NS_DECL_ISUPPORTS
-
-  LifecycleEventPromiseHandler(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
-                               const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                               bool aActivateImmediately)
-    : mTask(aTask)
-    , mServiceWorker(aServiceWorker)
-    , mActivateImmediately(aActivateImmediately)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
-
-    nsRefPtr<ContinueLifecycleRunnable> r =
-      new ContinueLifecycleRunnable(mTask, true /* success */, mActivateImmediately);
-    NS_DispatchToMainThread(r);
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
-
-    nsRefPtr<ContinueLifecycleRunnable> r =
-      new ContinueLifecycleRunnable(mTask, false /* success */, mActivateImmediately);
-    NS_DispatchToMainThread(r);
-
-    JS::Rooted<JSObject*> obj(aCx, workerPrivate->GlobalScope()->GetWrapper());
-    JS::ExposeValueToActiveJS(aValue);
-
-    js::ErrorReport report(aCx);
-    if (NS_WARN_IF(!report.init(aCx, aValue))) {
-      JS_ClearPendingException(aCx);
-      return;
-    }
-
-    nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
-    xpcReport->Init(report.report(), report.message(), /* aIsChrome = */ false, /* aWindowID = */ 0);
-
-    nsRefPtr<AsyncErrorReporter> aer =
-      new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(), xpcReport);
-    NS_DispatchToMainThread(aer);
-  }
-};
-
-NS_IMPL_ISUPPORTS0(LifecycleEventPromiseHandler)
-
-bool
-LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-{
-  aWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-
-  nsRefPtr<ExtendableEvent> event;
-  nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
-
-  if (mEventName.EqualsASCII("install") || mEventName.EqualsASCII("activate")) {
-    // FIXME(nsm): Bug 982787 pass previous active worker.
-    ExtendableEventInit init;
-    init.mBubbles = false;
-    init.mCancelable = false;
-    event = ExtendableEvent::Constructor(target, mEventName, init);
-  } else {
-    MOZ_CRASH("Unexpected lifecycle event");
-  }
-
-  event->SetTrusted(true);
-
-  nsRefPtr<Promise> waitUntilPromise =
-    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
-  if (waitUntilPromise) {
-    nsRefPtr<LifecycleEventPromiseHandler> handler =
-      new LifecycleEventPromiseHandler(mTask, mServiceWorker, false /* activateImmediately */);
-    waitUntilPromise->AppendNativeHandler(handler);
-  } else {
-    // Continue with a canceled install.
-    // Although the spec has different routines to deal with popping stuff
-    // off it's internal queues, we can reuse the ContinueAfterInstallEvent()
-    // logic.
-    nsRefPtr<ContinueLifecycleRunnable> r =
-      new ContinueLifecycleRunnable(mTask, false /* success */, false /* activate immediately */);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
-  }
-
-  return true;
-}
-
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
   if (!IsControllingDocuments() || mWaitingWorker->SkipWaitingFlag()) {
     Activate();
   }
 }
 
 void
-ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */)
+ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess)
 {
   mRegistration->FinishActivate(aSuccess);
 }
 
 void
 ServiceWorkerRegistrationInfo::PurgeActiveWorker()
 {
   nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker.forget();
@@ -2010,39 +1582,28 @@ ServiceWorkerRegistrationInfo::Activate(
                                                                 this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
   nsCOMPtr<nsIRunnable> failRunnable =
     NS_NewRunnableMethodWithArg<bool>(this,
                                       &ServiceWorkerRegistrationInfo::FinishActivate,
                                       false /* success */);
 
-  MOZ_ASSERT(mActiveWorker);
-  nsRefPtr<ServiceWorker> serviceWorker;
-  nsresult rv =
-    swm->CreateServiceWorker(mPrincipal,
-                             mActiveWorker,
-                             failRunnable,
-                             getter_AddRefs(serviceWorker));
+  nsMainThreadPtrHandle<ContinueLifecycleTask> continueActivateTask(
+    new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this)));
+  nsRefPtr<LifeCycleEventCallback> callback =
+    new ContinueLifecycleRunnable(continueActivateTask);
+
+  ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate();
+  nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"),
+                                                  callback, failRunnable);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(failRunnable)));
     return;
   }
-
-  nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
-    new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this)));
-
-  nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-    new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
-  nsRefPtr<LifecycleEventWorkerRunnable> r =
-    new LifecycleEventWorkerRunnable(serviceWorkerHandle, NS_LITERAL_STRING("activate"), handle);
-
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  r->Dispatch(jsapi.cx());
 }
 
 /*
  * Implements the async aspects of the getRegistrations algorithm.
  */
 class GetRegistrationsRunnable : public nsRunnable
 {
   nsCOMPtr<nsPIDOMWindow> mWindow;
@@ -2288,305 +1849,69 @@ public:
     if (!swm->CheckReadyPromise(mWindow, docURI, mPromise)) {
       swm->StorePendingReadyPromise(mWindow, docURI, mPromise);
     }
 
     return NS_OK;
   }
 };
 
-#ifndef MOZ_SIMPLEPUSH
-
-class SendPushEventRunnable final : public WorkerRunnable
-{
-  Maybe<nsTArray<uint8_t>> mData;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-
-public:
-  SendPushEventRunnable(
-    WorkerPrivate* aWorkerPrivate,
-    nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
-      : SendPushEventRunnable(aWorkerPrivate, aServiceWorker,
-                              Nothing()) {}
-
-  SendPushEventRunnable(
-    WorkerPrivate* aWorkerPrivate,
-    const nsTArray<uint8_t>& aData,
-    nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
-      : SendPushEventRunnable(aWorkerPrivate, aServiceWorker,
-                              Some(aData)) {}
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
-
-
-    PushEventInit pei;
-    if (mData) {
-      const nsTArray<uint8_t>& bytes = mData.ref();
-      JSObject* data = Uint8Array::Create(aCx, bytes.Length(), bytes.Elements());
-      if (!data) {
-        return false;
-      }
-      pei.mData.Construct().SetAsArrayBufferView().Init(data);
-    }
-    pei.mBubbles = false;
-    pei.mCancelable = false;
-
-    ErrorResult result;
-    nsRefPtr<PushEvent> event =
-      PushEvent::Constructor(globalObj, NS_LITERAL_STRING("push"), pei, result);
-    if (NS_WARN_IF(result.Failed())) {
-      result.SuppressException();
-      return false;
-    }
-
-    event->SetTrusted(true);
-    event->PostInit(mServiceWorker);
-
-    nsRefPtr<Promise> waitUntilPromise =
-      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
-    if (waitUntilPromise) {
-      nsRefPtr<KeepAliveHandler> handler = new KeepAliveHandler(mServiceWorker);
-      waitUntilPromise->AppendNativeHandler(handler);
-    }
-
-    return true;
-  }
-
-private:
-  SendPushEventRunnable(
-    WorkerPrivate* aWorkerPrivate,
-    nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-    Maybe<nsTArray<uint8_t>> aData)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
-      , mData(aData)
-      , mServiceWorker(aServiceWorker)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-  }
-};
-
-class SendPushSubscriptionChangeEventRunnable final : public WorkerRunnable
-{
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-
-public:
-  SendPushSubscriptionChangeEventRunnable(
-    WorkerPrivate* aWorkerPrivate,
-    nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
-      , mServiceWorker(aServiceWorker)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-
-    WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
-
-    nsRefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
-
-    nsresult rv = event->InitEvent(NS_LITERAL_STRING("pushsubscriptionchange"),
-                                   false, false);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return false;
-    }
-
-    event->SetTrusted(true);
-
-    globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-    return true;
-  }
-};
-
-#endif /* ! MOZ_SIMPLEPUSH */
-
 NS_IMETHODIMP
 ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
                                     const nsACString& aScope,
                                     uint32_t aDataLength,
                                     uint8_t* aDataBytes,
                                     uint8_t optional_argc)
 {
 #ifdef MOZ_SIMPLEPUSH
   return NS_ERROR_NOT_AVAILABLE;
 #else
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsRefPtr<ServiceWorker> serviceWorker =
-    CreateServiceWorkerForScope(attrs, aScope, nullptr /* failure runnable */);
+  ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
   if (NS_WARN_IF(!serviceWorker)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-    new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
-
-  nsRefPtr<SendPushEventRunnable> r;
   if (optional_argc == 2) {
     nsTArray<uint8_t> data;
     if (!data.InsertElementsAt(0, aDataBytes, aDataLength, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    r = new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(), data,
-                                  serviceWorkerHandle);
+    return serviceWorker->WorkerPrivate()->SendPushEvent(Some(data));
   } else {
     MOZ_ASSERT(optional_argc == 0);
-    r = new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(),
-                                  serviceWorkerHandle);
+    return serviceWorker->WorkerPrivate()->SendPushEvent(Nothing());
   }
-
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-#endif
+#endif // MOZ_SIMPLEPUSH
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes,
                                                       const nsACString& aScope)
 {
 #ifdef MOZ_SIMPLEPUSH
   return NS_ERROR_NOT_AVAILABLE;
 #else
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsRefPtr<ServiceWorker> serviceWorker =
-    CreateServiceWorkerForScope(attrs, aScope, nullptr /* fail runnable */);
-  if (!serviceWorker) {
+  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
+  if (!info) {
     return NS_ERROR_FAILURE;
   }
-  nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-    new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
-
-  nsRefPtr<SendPushSubscriptionChangeEventRunnable> r =
-    new SendPushSubscriptionChangeEventRunnable(
-      serviceWorker->GetWorkerPrivate(), serviceWorkerHandle);
-
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
+  return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
 #endif
 }
 
-class SendNotificationClickEventRunnable final : public WorkerRunnable
-{
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  const nsString mID;
-  const nsString mTitle;
-  const nsString mDir;
-  const nsString mLang;
-  const nsString mBody;
-  const nsString mTag;
-  const nsString mIcon;
-  const nsString mData;
-  const nsString mBehavior;
-  const nsString mScope;
-
-public:
-  SendNotificationClickEventRunnable(
-    WorkerPrivate* aWorkerPrivate,
-    nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-    const nsAString& aID,
-    const nsAString& aTitle,
-    const nsAString& aDir,
-    const nsAString& aLang,
-    const nsAString& aBody,
-    const nsAString& aTag,
-    const nsAString& aIcon,
-    const nsAString& aData,
-    const nsAString& aBehavior,
-    const nsAString& aScope)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
-      , mServiceWorker(aServiceWorker)
-      , mID(aID)
-      , mTitle(aTitle)
-      , mDir(aDir)
-      , mLang(aLang)
-      , mBody(aBody)
-      , mTag(aTag)
-      , mIcon(aIcon)
-      , mData(aData)
-      , mBehavior(aBehavior)
-      , mScope(aScope)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-
-    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
-
-    ErrorResult result;
-    nsRefPtr<Notification> notification =
-      Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(), mID,
-                                        mTitle, mDir, mLang, mBody, mTag, mIcon,
-                                        mData, mScope, result);
-    if (NS_WARN_IF(result.Failed())) {
-      return false;
-    }
-
-    NotificationEventInit nei;
-    nei.mNotification = notification;
-    nei.mBubbles = false;
-    nei.mCancelable = false;
-
-    nsRefPtr<NotificationEvent> event =
-      NotificationEvent::Constructor(target, NS_LITERAL_STRING("notificationclick"), nei, result);
-    if (NS_WARN_IF(result.Failed())) {
-      return false;
-    }
-
-    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
-    event->SetTrusted(true);
-    nsRefPtr<Promise> waitUntilPromise =
-      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
-    // If the handler calls WaitUntil(), that will manage its own interaction
-    // 'stack'.
-    aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
-
-    if (waitUntilPromise) {
-      nsRefPtr<AllowWindowInteractionKeepAliveHandler> handler =
-        new AllowWindowInteractionKeepAliveHandler(mServiceWorker, aWorkerPrivate);
-      waitUntilPromise->AppendNativeHandler(handler);
-    }
-
-    return true;
-  }
-};
-
 NS_IMETHODIMP
 ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
                                                  const nsACString& aScope,
                                                  const nsAString& aID,
                                                  const nsAString& aTitle,
                                                  const nsAString& aDir,
                                                  const nsAString& aLang,
                                                  const nsAString& aBody,
@@ -2595,40 +1920,26 @@ ServiceWorkerManager::SendNotificationCl
                                                  const nsAString& aData,
                                                  const nsAString& aBehavior)
 {
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
-
-  nsRefPtr<ServiceWorker> serviceWorker =
-    CreateServiceWorkerForScope(attrs, aScope, nullptr);
-  if (!serviceWorker) {
+  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
+  if (!info) {
     return NS_ERROR_FAILURE;
   }
-  nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-    new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
-
-  nsRefPtr<SendNotificationClickEventRunnable> r =
-    new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(),
-                                           serviceWorkerHandle, aID, aTitle,
-                                           aDir, aLang, aBody, aTag, aIcon,
-                                           aData, aBehavior,
-                                           NS_ConvertUTF8toUTF16(aScope));
-
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
+
+  ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
+  return workerPrivate->SendNotificationClickEvent(aID, aTitle, aDir,
+                                                   aLang, aBody, aTag,
+                                                   aIcon, aData, aBehavior,
+                                                   NS_ConvertUTF8toUTF16(aScope));
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
                                       nsISupports** aPromise)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
@@ -2734,49 +2045,49 @@ ServiceWorkerManager::CheckReadyPromise(
       new ServiceWorkerRegistrationMainThread(aWindow, scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
-already_AddRefed<ServiceWorker>
-ServiceWorkerManager::CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes,
-                                                  const nsACString& aScope,
-                                                  nsIRunnable* aLoadFailedRunnable)
+ServiceWorkerInfo*
+ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
+                                              const nsACString& aScope)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   if (NS_FAILED(rv)) {
     return nullptr;
   }
   nsRefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aOriginAttributes, scopeURI);
   if (!registration) {
     return nullptr;
   }
 
-  if (!registration->mActiveWorker) {
+  return registration->mActiveWorker;
+}
+
+ServiceWorkerInfo*
+ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
+{
+  AssertIsOnMainThread();
+
+  nsRefPtr<ServiceWorkerRegistrationInfo> registration;
+  GetDocumentRegistration(aDocument, getter_AddRefs(registration));
+
+  if (!registration) {
     return nullptr;
   }
 
-  nsRefPtr<ServiceWorker> sw;
-  rv = CreateServiceWorker(registration->mPrincipal,
-                           registration->mActiveWorker,
-                           aLoadFailedRunnable,
-                           getter_AddRefs(sw));
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  return sw.forget();
+  return registration->mActiveWorker;
 }
 
 class ServiceWorkerUnregisterJob final : public ServiceWorkerJob
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   const nsCString mScope;
   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
   nsCOMPtr<nsIPrincipal> mPrincipal;
@@ -3035,67 +2346,16 @@ ServiceWorkerRegistrationInfo::FinishAct
   }
 
   // Activation never fails, so aSuccess is ignored.
   mActiveWorker->UpdateState(ServiceWorkerState::Activated);
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   swm->StoreRegistration(mPrincipal, this);
 }
 
-NS_IMETHODIMP
-ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
-                                                   ServiceWorkerInfo* aInfo,
-                                                   nsIRunnable* aLoadFailedRunnable,
-                                                   ServiceWorker** aServiceWorker)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(aInfo);
-
-  AutoJSAPI jsapi;
-  jsapi.Init(aWindow);
-  JSContext* cx = jsapi.cx();
-
-  WorkerLoadInfo loadInfo;
-  nsresult rv = WorkerPrivate::GetLoadInfo(cx, aWindow, nullptr,
-                                           NS_ConvertUTF8toUTF16(aInfo->ScriptSpec()),
-                                           false,
-                                           WorkerPrivate::OverrideLoadGroup,
-                                           WorkerTypeService,
-                                           &loadInfo);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT(!aInfo->CacheName().IsEmpty());
-  loadInfo.mServiceWorkerCacheName = aInfo->CacheName();
-  loadInfo.mServiceWorkerID = aInfo->ID();
-  loadInfo.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
-
-  RuntimeService* rs = RuntimeService::GetOrCreateService();
-  if (!rs) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<SharedWorker> sharedWorker;
-  rv = rs->CreateSharedWorkerForServiceWorkerFromLoadInfo(cx, &loadInfo,
-                                                          NS_ConvertUTF8toUTF16(aInfo->ScriptSpec()),
-                                                          aInfo->Scope(),
-                                                          getter_AddRefs(sharedWorker));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsRefPtr<ServiceWorker> serviceWorker =
-    new ServiceWorker(aWindow, aInfo, sharedWorker);
-
-  serviceWorker.forget(aServiceWorker);
-  return rv;
-}
-
 void
 ServiceWorkerManager::LoadRegistration(
                              const ServiceWorkerRegistrationData& aRegistration)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(aRegistration.principal());
@@ -3629,305 +2889,46 @@ ServiceWorkerManager::GetServiceWorkerFo
   } else {
     MOZ_CRASH("Invalid worker type");
   }
 
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
-  // TODO: How should we handle async failure of SW load for getters?  In
-  //       theory getting a handle to the DOM ServiceWorker object should not
-  //       require the worker thread to actually be running.
-  nsRefPtr<ServiceWorker> serviceWorker;
-  rv = CreateServiceWorkerForWindow(window,
-                                    info,
-                                    nullptr, // load failed runnable
-                                    getter_AddRefs(serviceWorker));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  nsRefPtr<ServiceWorker> serviceWorker = new ServiceWorker(window, info);
 
   serviceWorker->SetState(info->State());
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
-class FetchEventRunnable : public WorkerRunnable
-                         , public nsIHttpHeaderVisitor {
-  nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  nsTArray<nsCString> mHeaderNames;
-  nsTArray<nsCString> mHeaderValues;
-  nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
-  nsCString mSpec;
-  nsCString mMethod;
-  bool mIsReload;
-  DebugOnly<bool> mIsHttpChannel;
-  RequestMode mRequestMode;
-  RequestRedirect mRequestRedirect;
-  RequestCredentials mRequestCredentials;
-  nsContentPolicyType mContentPolicyType;
-  nsCOMPtr<nsIInputStream> mUploadStream;
-  nsCString mReferrer;
-public:
-  FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
-                     nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                     nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
-                     bool aIsReload)
-    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
-    , mInterceptedChannel(aChannel)
-    , mServiceWorker(aServiceWorker)
-    , mClientInfo(aClientInfo)
-    , mIsReload(aIsReload)
-    , mIsHttpChannel(false)
-    , mRequestMode(RequestMode::No_cors)
-    , mRequestRedirect(RequestRedirect::Follow)
-    // By default we set it to same-origin since normal HTTP fetches always
-    // send credentials to same-origin websites unless explicitly forbidden.
-    , mRequestCredentials(RequestCredentials::Same_origin)
-    , mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
-    , mReferrer(kFETCH_CLIENT_REFERRER_STR)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-  }
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-  NS_IMETHOD
-  VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
-  {
-    mHeaderNames.AppendElement(aHeader);
-    mHeaderValues.AppendElement(aValue);
-    return NS_OK;
-  }
-
-  nsresult
-  Init()
-  {
-    AssertIsOnMainThread();
-    nsCOMPtr<nsIChannel> channel;
-    nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIURI> uri;
-    rv = channel->GetURI(getter_AddRefs(uri));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = uri->GetSpec(mSpec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint32_t loadFlags;
-    rv = channel->GetLoadFlags(&loadFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsILoadInfo> loadInfo;
-    rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mContentPolicyType = loadInfo->InternalContentPolicyType();
-
-    nsCOMPtr<nsIURI> referrerURI;
-    rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
-    // We can't bail on failure since certain non-http channels like JAR
-    // channels are intercepted but don't have referrers.
-    if (NS_SUCCEEDED(rv) && referrerURI) {
-      rv = referrerURI->GetSpec(mReferrer);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
-    if (httpChannel) {
-      mIsHttpChannel = true;
-
-      rv = httpChannel->GetRequestMethod(mMethod);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
-      NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
-
-      mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
-
-      // This is safe due to static_asserts at top of file.
-      uint32_t redirectMode;
-      internalChannel->GetRedirectMode(&redirectMode);
-      mRequestRedirect = static_cast<RequestRedirect>(redirectMode);
-
-      if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
-        mRequestCredentials = RequestCredentials::Omit;
-      } else {
-        bool includeCrossOrigin;
-        internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin);
-        if (includeCrossOrigin) {
-          mRequestCredentials = RequestCredentials::Include;
-        }
-      }
-
-      rv = httpChannel->VisitNonDefaultRequestHeaders(this);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
-      if (uploadChannel) {
-        MOZ_ASSERT(!mUploadStream);
-        rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream));
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-    } else {
-      nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
-      // If it is not an HTTP channel it must be a JAR one.
-      NS_ENSURE_TRUE(jarChannel, NS_ERROR_NOT_AVAILABLE);
-
-      mMethod = "GET";
-
-      mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
-
-      if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
-        mRequestCredentials = RequestCredentials::Omit;
-      }
-    }
-
-    return NS_OK;
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    return DispatchFetchEvent(aCx, aWorkerPrivate);
-  }
-
-private:
-  ~FetchEventRunnable() {}
-
-  class ResumeRequest final : public nsRunnable {
-    nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
-  public:
-    explicit ResumeRequest(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
-      : mChannel(aChannel)
-    {
-    }
-
-    NS_IMETHOD Run()
-    {
-      AssertIsOnMainThread();
-      nsresult rv = mChannel->ResetInterception();
-      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to resume intercepted network request");
-      return rv;
-    }
-  };
-
-  bool
-  DispatchFetchEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aCx);
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-    GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
-
-    NS_ConvertUTF8toUTF16 local(mSpec);
-    RequestOrUSVString requestInfo;
-    requestInfo.SetAsUSVString().Rebind(local.Data(), local.Length());
-
-    RootedDictionary<RequestInit> reqInit(aCx);
-    reqInit.mMethod.Construct(mMethod);
-
-    nsRefPtr<InternalHeaders> internalHeaders = new InternalHeaders(HeadersGuardEnum::Request);
-    MOZ_ASSERT(mHeaderNames.Length() == mHeaderValues.Length());
-    for (uint32_t i = 0; i < mHeaderNames.Length(); i++) {
-      ErrorResult result;
-      internalHeaders->Set(mHeaderNames[i], mHeaderValues[i], result);
-      if (NS_WARN_IF(result.Failed())) {
-        result.SuppressException();
-        return false;
-      }
-    }
-
-    nsRefPtr<Headers> headers = new Headers(globalObj.GetAsSupports(), internalHeaders);
-    reqInit.mHeaders.Construct();
-    reqInit.mHeaders.Value().SetAsHeaders() = headers;
-
-    reqInit.mMode.Construct(mRequestMode);
-    reqInit.mRedirect.Construct(mRequestRedirect);
-    reqInit.mCredentials.Construct(mRequestCredentials);
-
-    ErrorResult result;
-    nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, result);
-    if (NS_WARN_IF(result.Failed())) {
-      result.SuppressException();
-      return false;
-    }
-    // For Telemetry, note that this Request object was created by a Fetch event.
-    nsRefPtr<InternalRequest> internalReq = request->GetInternalRequest();
-    MOZ_ASSERT(internalReq);
-    internalReq->SetCreatedByFetchEvent();
-
-    internalReq->SetBody(mUploadStream);
-    internalReq->SetReferrer(NS_ConvertUTF8toUTF16(mReferrer));
-
-    request->SetContentPolicyType(mContentPolicyType);
-
-    // TODO: remove conditional on http here once app protocol support is
-    //       removed from service worker interception
-    MOZ_ASSERT_IF(mIsHttpChannel && internalReq->IsNavigationRequest(),
-                  request->Redirect() == RequestRedirect::Manual);
-
-    RootedDictionary<FetchEventInit> init(aCx);
-    init.mRequest.Construct();
-    init.mRequest.Value() = request;
-    init.mBubbles = false;
-    init.mCancelable = true;
-    init.mIsReload.Construct(mIsReload);
-    nsRefPtr<FetchEvent> event =
-      FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, result);
-    if (NS_WARN_IF(result.Failed())) {
-      result.SuppressException();
-      return false;
-    }
-
-    event->PostInit(mInterceptedChannel, mServiceWorker, mClientInfo);
-    event->SetTrusted(true);
-
-    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
-    nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-    if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
-      nsCOMPtr<nsIRunnable> runnable;
-      if (event->DefaultPrevented(aCx)) {
-        runnable = new CancelChannelRunnable(mInterceptedChannel, NS_ERROR_INTERCEPTION_CANCELED);
-      } else {
-        runnable = new ResumeRequest(mInterceptedChannel);
-      }
-
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
-    }
-    return true;
-  }
-};
-
 namespace {
 
 class ContinueDispatchFetchEventRunnable : public nsRunnable
 {
-  WorkerPrivate* mWorkerPrivate;
-  nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
+  nsRefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
+  nsCOMPtr<nsIInterceptedChannel> mChannel;
+  nsCOMPtr<nsILoadGroup> mLoadGroup;
+  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
   bool mIsReload;
 public:
-  ContinueDispatchFetchEventRunnable(WorkerPrivate* aWorkerPrivate,
-                                     nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                                     nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
+  ContinueDispatchFetchEventRunnable(ServiceWorkerPrivate* aServiceWorkerPrivate,
+                                     nsIInterceptedChannel* aChannel,
+                                     nsILoadGroup* aLoadGroup,
+                                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                                      bool aIsReload)
-    : mWorkerPrivate(aWorkerPrivate)
+    : mServiceWorkerPrivate(aServiceWorkerPrivate)
     , mChannel(aChannel)
-    , mServiceWorker(aServiceWorker)
-    , mClientInfo(aClientInfo)
+    , mLoadGroup(aLoadGroup)
+    , mClientInfo(Move(aClientInfo))
     , mIsReload(aIsReload)
   {
+    MOZ_ASSERT(aServiceWorkerPrivate);
+    MOZ_ASSERT(aChannel);
   }
 
   void
   HandleError()
   {
     MOZ_ASSERT(NS_IsMainThread());
     NS_WARNING("Unexpected error while dispatching fetch event!");
     DebugOnly<nsresult> rv = mChannel->ResetInterception();
@@ -3951,112 +2952,84 @@ public:
     // if that happens.
     nsresult status;
     rv = channel->GetStatus(&status);
     if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
       HandleError();
       return NS_OK;
     }
 
-    nsRefPtr<FetchEventRunnable> event =
-      new FetchEventRunnable(mWorkerPrivate, mChannel, mServiceWorker,
-                             mClientInfo, mIsReload);
-    rv = event->Init();
+    rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup,
+                                               Move(mClientInfo), mIsReload);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       HandleError();
-      return NS_OK;
-    }
-
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    if (NS_WARN_IF(!event->Dispatch(jsapi.cx()))) {
-      HandleError();
-      return NS_OK;
     }
 
     return NS_OK;
   }
 };
 
 } // anonymous namespace
 
-NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
-
 already_AddRefed<nsIRunnable>
 ServiceWorkerManager::PrepareFetchEvent(const OriginAttributes& aOriginAttributes,
                                         nsIDocument* aDoc,
                                         nsIInterceptedChannel* aChannel,
                                         bool aIsReload,
                                         bool aIsSubresourceLoad,
                                         ErrorResult& aRv)
 {
   MOZ_ASSERT(aChannel);
   MOZ_ASSERT(NS_IsMainThread());
 
-  nsCOMPtr<nsISupports> serviceWorker;
-
-  // if the ServiceWorker script fails to load for some reason, just resume
-  // the original channel.
-  nsCOMPtr<nsIRunnable> failRunnable =
-    NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
-
-  nsAutoPtr<ServiceWorkerClientInfo> clientInfo;
+  nsRefPtr<ServiceWorkerInfo> serviceWorker;
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  UniquePtr<ServiceWorkerClientInfo> clientInfo;
 
   if (aIsSubresourceLoad) {
     MOZ_ASSERT(aDoc);
-    aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
-                                getter_AddRefs(serviceWorker));
-    clientInfo = new ServiceWorkerClientInfo(aDoc);
+    serviceWorker = GetActiveWorkerInfoForDocument(aDoc);
+    loadGroup = aDoc->GetDocumentLoadGroup();
+    clientInfo.reset(new ServiceWorkerClientInfo(aDoc));
   } else {
     nsCOMPtr<nsIChannel> internalChannel;
     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
+    internalChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+
     nsCOMPtr<nsIURI> uri;
     aRv = internalChannel->GetURI(getter_AddRefs(uri));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
     nsRefPtr<ServiceWorkerRegistrationInfo> registration =
       GetServiceWorkerRegistrationInfo(aOriginAttributes, uri);
     if (!registration) {
       NS_WARNING("No registration found when dispatching the fetch event");
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     // This should only happen if IsAvailable() returned true.
     MOZ_ASSERT(registration->mActiveWorker);
-
-    nsRefPtr<ServiceWorker> sw;
-    aRv = CreateServiceWorker(registration->mPrincipal,
-                              registration->mActiveWorker,
-                              failRunnable,
-                              getter_AddRefs(sw));
-    serviceWorker = sw.forget();
+    serviceWorker = registration->mActiveWorker;
   }
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(aRv.Failed()) || !serviceWorker) {
     return nullptr;
   }
 
-  nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
-    new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
-
-  nsRefPtr<ServiceWorker> sw = static_cast<ServiceWorker*>(serviceWorker.get());
-  nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
-    new nsMainThreadPtrHolder<ServiceWorker>(sw));
-
   nsCOMPtr<nsIRunnable> continueRunnable =
-    new ContinueDispatchFetchEventRunnable(sw->GetWorkerPrivate(), handle,
-                                           serviceWorkerHandle, clientInfo,
-                                           aIsReload);
+    new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(),
+                                           aChannel, loadGroup,
+                                           Move(clientInfo), aIsReload);
 
   return continueRunnable.forget();
 }
 
 void
 ServiceWorkerManager::DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel,
                                                  nsIRunnable* aPreparedRunnable,
                                                  ErrorResult& aRv)
@@ -4073,17 +3046,16 @@ ServiceWorkerManager::DispatchPreparedFe
 
   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(innerChannel);
 
   // If there is no upload stream, then continue immediately
   if (!uploadChannel) {
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPreparedRunnable->Run()));
     return;
   }
-
   // Otherwise, ensure the upload stream can be cloned directly.  This may
   // require some async copying, so provide a callback.
   aRv = uploadChannel->EnsureUploadStreamIsCloneable(aPreparedRunnable);
 }
 
 bool
 ServiceWorkerManager::IsAvailable(const OriginAttributes& aOriginAttributes,
                                   nsIURI* aURI)
@@ -4131,41 +3103,35 @@ ServiceWorkerManager::GetDocumentRegistr
 }
 
 /*
  * The .controller is for the registration associated with the document when
  * the document was loaded.
  */
 NS_IMETHODIMP
 ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow,
-                                            nsIRunnable* aLoadFailedRunnable,
                                             nsISupports** aServiceWorker)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   MOZ_ASSERT(window);
   if (!window || !window->GetExtantDoc()) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
 
   nsRefPtr<ServiceWorkerRegistrationInfo> registration;
   nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsRefPtr<ServiceWorker> serviceWorker;
-  rv = CreateServiceWorkerForWindow(window,
-                                    registration->mActiveWorker,
-                                    aLoadFailedRunnable,
-                                    getter_AddRefs(serviceWorker));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  MOZ_ASSERT(registration->mActiveWorker);
+  nsRefPtr<ServiceWorker> serviceWorker =
+    new ServiceWorker(window, registration->mActiveWorker);
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetInstalling(nsIDOMWindow* aWindow,
                                     const nsAString& aScope,
@@ -4191,106 +3157,16 @@ ServiceWorkerManager::GetActive(nsIDOMWi
                                 const nsAString& aScope,
                                 nsISupports** aServiceWorker)
 {
   return GetServiceWorkerForScope(aWindow, aScope,
                                   WhichServiceWorker::ACTIVE_WORKER,
                                   aServiceWorker);
 }
 
-NS_IMETHODIMP
-ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
-                                          ServiceWorkerInfo* aInfo,
-                                          nsIRunnable* aLoadFailedRunnable,
-                                          ServiceWorker** aServiceWorker)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aPrincipal);
-
-  // Ensure that the IndexedDatabaseManager is initialized
-  NS_WARN_IF(!indexedDB::IndexedDatabaseManager::GetOrCreate());
-
-  WorkerLoadInfo info;
-  nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aInfo->ScriptSpec(),
-                          nullptr, nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  info.mResolvedScriptURI = info.mBaseURI;
-  MOZ_ASSERT(!aInfo->CacheName().IsEmpty());
-  info.mServiceWorkerCacheName = aInfo->CacheName();
-  info.mServiceWorkerID = aInfo->ID();
-
-  rv = info.mBaseURI->GetHost(info.mDomain);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  info.mPrincipal = aPrincipal;
-
-  nsContentUtils::StorageAccess access =
-    nsContentUtils::StorageAllowedForPrincipal(aPrincipal);
-  info.mStorageAllowed =
-    access > nsContentUtils::StorageAccess::ePrivateBrowsing;
-
-  info.mPrivateBrowsing = false;
-
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = aPrincipal->GetCsp(getter_AddRefs(csp));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  info.mCSP = csp;
-  if (info.mCSP) {
-    rv = info.mCSP->GetAllowsEval(&info.mReportCSPViolations,
-                                  &info.mEvalAllowed);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  } else {
-    info.mEvalAllowed = true;
-    info.mReportCSPViolations = false;
-  }
-
-  // NOTE: this defaults the SW load context to:
-  //  - private browsing = false
-  //  - content = true
-  //  - use remote tabs = false
-  // Alternatively we could persist the original load group values and use
-  // them here.
-  WorkerPrivate::OverrideLoadInfoLoadGroup(info);
-
-  info.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
-
-  RuntimeService* rs = RuntimeService::GetOrCreateService();
-  if (!rs) {
-    return NS_ERROR_FAILURE;
-  }
-
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  nsRefPtr<SharedWorker> sharedWorker;
-  rv = rs->CreateSharedWorkerForServiceWorkerFromLoadInfo(jsapi.cx(), &info,
-                                                          NS_ConvertUTF8toUTF16(aInfo->ScriptSpec()),
-                                                          aInfo->Scope(),
-                                                          getter_AddRefs(sharedWorker));
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsRefPtr<ServiceWorker> serviceWorker =
-    new ServiceWorker(nullptr, aInfo, sharedWorker);
-
-  serviceWorker.forget(aServiceWorker);
-  return NS_OK;
-}
-
 void
 ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                                                 WhichServiceWorker aWhichOnes)
 {
   AssertIsOnMainThread();
   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
   while (it.HasMore()) {
     nsRefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
@@ -5285,16 +4161,37 @@ ServiceWorkerInfo::UpdateState(ServiceWo
   MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
 #endif
   mState = aState;
   for (uint32_t i = 0; i < mInstances.Length(); ++i) {
     mInstances[i]->QueueStateChangeEvent(mState);
   }
 }
 
+ServiceWorkerInfo::ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
+                                     const nsACString& aScriptSpec,
+                                     const nsAString& aCacheName)
+  : mRegistration(aReg)
+  , mScriptSpec(aScriptSpec)
+  , mCacheName(aCacheName)
+  , mState(ServiceWorkerState::EndGuard_)
+  , mServiceWorkerID(GetNextID())
+  , mServiceWorkerPrivate(new class ServiceWorkerPrivate(this))
+  , mSkipWaitingFlag(false)
+{
+  MOZ_ASSERT(mRegistration);
+  MOZ_ASSERT(!aCacheName.IsEmpty());
+}
+
+ServiceWorkerInfo::~ServiceWorkerInfo()
+{
+  MOZ_ASSERT(mServiceWorkerPrivate);
+  mServiceWorkerPrivate->TerminateWorker();
+}
+
 static uint64_t gServiceWorkerInfoCurrentID = 0;
 
 uint64_t
 ServiceWorkerInfo::GetNextID() const
 {
   return ++gServiceWorkerInfoCurrentID;
 }
 
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -44,16 +44,17 @@ class ServiceWorkerRegistrationListener;
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerClientInfo;
 class ServiceWorkerInfo;
 class ServiceWorkerJob;
 class ServiceWorkerJobQueue;
 class ServiceWorkerManagerChild;
+class ServiceWorkerPrivate;
 
 // Needs to inherit from nsISupports because NS_ProxyRelease() does not support
 // non-ISupports classes.
 class ServiceWorkerRegistrationInfo final : public nsISupports
 {
   uint32_t mControlledDocumentsCounter;
 
   virtual ~ServiceWorkerRegistrationInfo();
@@ -170,29 +171,43 @@ private:
   // workers to their corresponding serviceWorkerInfo.
   uint64_t mServiceWorkerID;
 
   // We hold rawptrs since the ServiceWorker constructor and destructor ensure
   // addition and removal.
   // There is a high chance of there being at least one ServiceWorker
   // associated with this all the time.
   nsAutoTArray<ServiceWorker*, 1> mInstances;
+
+  nsRefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
   bool mSkipWaitingFlag;
 
-  ~ServiceWorkerInfo()
-  { }
+  ~ServiceWorkerInfo();
 
   // Generates a unique id for the service worker, with zero being treated as
   // invalid.
   uint64_t
   GetNextID() const;
 
 public:
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
 
+  class ServiceWorkerPrivate*
+  WorkerPrivate() const
+  {
+    MOZ_ASSERT(mServiceWorkerPrivate);
+    return mServiceWorkerPrivate;
+  }
+
+  nsIPrincipal*
+  GetPrincipal() const
+  {
+    return mRegistration->mPrincipal;
+  }
+
   const nsCString&
   ScriptSpec() const
   {
     return mScriptSpec;
   }
 
   const nsCString&
   Scope() const
@@ -215,27 +230,17 @@ public:
   void SetSkipWaitingFlag()
   {
     AssertIsOnMainThread();
     mSkipWaitingFlag = true;
   }
 
   ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
                     const nsACString& aScriptSpec,
-                    const nsAString& aCacheName)
-    : mRegistration(aReg)
-    , mScriptSpec(aScriptSpec)
-    , mCacheName(aCacheName)
-    , mState(ServiceWorkerState::EndGuard_)
-    , mServiceWorkerID(GetNextID())
-    , mSkipWaitingFlag(false)
-  {
-    MOZ_ASSERT(mRegistration);
-    MOZ_ASSERT(!aCacheName.IsEmpty());
-  }
+                    const nsAString& aCacheName);
 
   ServiceWorkerState
   State() const
   {
     return mState;
   }
 
   const nsString&
@@ -449,38 +454,28 @@ private:
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   Update(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   GetDocumentRegistration(nsIDocument* aDoc, ServiceWorkerRegistrationInfo** aRegistrationInfo);
 
-  NS_IMETHOD
-  CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
-                               ServiceWorkerInfo* aInfo,
-                               nsIRunnable* aLoadFailedRunnable,
-                               ServiceWorker** aServiceWorker);
-
-  NS_IMETHOD
-  CreateServiceWorker(nsIPrincipal* aPrincipal,
-                      ServiceWorkerInfo* aInfo,
-                      nsIRunnable* aLoadFailedRunnable,
-                      ServiceWorker** aServiceWorker);
-
   NS_IMETHODIMP
   GetServiceWorkerForScope(nsIDOMWindow* aWindow,
                            const nsAString& aScope,
                            WhichServiceWorker aWhichWorker,
                            nsISupports** aServiceWorker);
 
-  already_AddRefed<ServiceWorker>
-  CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes,
-                              const nsACString& aScope,
-                              nsIRunnable* aLoadFailedRunnable);
+  ServiceWorkerInfo*
+  GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
+                              const nsACString& aScope);
+
+  ServiceWorkerInfo*
+  GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
 
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -0,0 +1,1088 @@
+/* -*- 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 "ServiceWorkerPrivate.h"
+
+#include "ServiceWorkerManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::workers;
+
+// Tracks the "dom.disable_open_click_delay" preference.  Modified on main
+// thread, read on worker threads.
+// It is updated every time a "notificationclick" event is dispatched. While
+// this is done without synchronization, at the worst, the thread will just get
+// an older value within which a popup is allowed to be displayed, which will
+// still be a valid value since it was set prior to dispatching the runnable.
+Atomic<uint32_t> gDOMDisableOpenClickDelay(0);
+
+ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo)
+  : mInfo(aInfo)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aInfo);
+}
+
+nsresult
+ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
+                                       JS::Handle<JS::Value> aMessage,
+                                       const Optional<Sequence<JS::Value>>& aTransferable,
+                                       UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+{
+  ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent, nullptr));
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  mWorkerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
+                                             Move(aClientInfo), rv);
+  return rv.StealNSResult();
+}
+
+namespace {
+
+class CheckScriptEvaluationWithCallback final : public WorkerRunnable
+{
+  nsRefPtr<nsRunnable> mCallback;
+
+public:
+  CheckScriptEvaluationWithCallback(WorkerPrivate* aWorkerPrivate,
+                                    nsRunnable* aCallback)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mCallback(aCallback)
+  {
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
+      nsresult rv = NS_DispatchToMainThread(mCallback);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch CheckScriptEvaluation callback.");
+      }
+    }
+
+    return true;
+  }
+};
+
+} // anonymous namespace
+
+nsresult
+ServiceWorkerPrivate::ContinueOnSuccessfulScriptEvaluation(nsRunnable* aCallback)
+{
+  nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(mWorkerPrivate,
+                                                                     aCallback);
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+namespace {
+
+// Returns a Promise if the event was successfully dispatched and no exceptions
+// were raised, otherwise returns null.
+static already_AddRefed<Promise>
+DispatchExtendableEventOnWorkerScope(JSContext* aCx,
+                                     WorkerGlobalScope* aWorkerScope,
+                                     ExtendableEvent* aEvent)
+{
+  MOZ_ASSERT(aWorkerScope);
+  MOZ_ASSERT(aEvent);
+  nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
+  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+
+  ErrorResult result;
+  result = aWorkerScope->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
+  if (NS_WARN_IF(result.Failed()) || internalEvent->mFlags.mExceptionHasBeenRisen) {
+    result.SuppressException();
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
+  if (!waitUntilPromise) {
+    ErrorResult result;
+    waitUntilPromise =
+      Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
+    if (NS_WARN_IF(result.Failed())) {
+      result.SuppressException();
+      return nullptr;
+    }
+  }
+
+  MOZ_ASSERT(waitUntilPromise);
+  return waitUntilPromise.forget();
+}
+
+/*
+ * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
+ * since it fires the event. This is ok since there can't be nested
+ * ServiceWorkers, so the parent thread -> worker thread requirement for
+ * runnables is satisfied.
+ */
+class LifecycleEventWorkerRunnable : public WorkerRunnable
+{
+  nsString mEventName;
+  nsRefPtr<LifeCycleEventCallback> mCallback;
+
+public:
+  LifecycleEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+                               const nsAString& aEventName,
+                               LifeCycleEventCallback* aCallback)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+      , mEventName(aEventName)
+      , mCallback(aCallback)
+  {
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    return DispatchLifecycleEvent(aCx, aWorkerPrivate);
+  }
+
+private:
+  bool
+  DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
+
+};
+
+/*
+ * Used to handle ExtendableEvent::waitUntil() and proceed with
+ * installation/activation.
+ */
+class LifecycleEventPromiseHandler final : public PromiseNativeHandler
+{
+  nsRefPtr<LifeCycleEventCallback> mCallback;
+
+  virtual
+  ~LifecycleEventPromiseHandler()
+  { }
+
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit LifecycleEventPromiseHandler(LifeCycleEventCallback* aCallback)
+    : mCallback(aCallback)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+
+    mCallback->SetResult(true);
+    nsresult rv = NS_DispatchToMainThread(mCallback);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
+    }
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+
+    mCallback->SetResult(false);
+    nsresult rv = NS_DispatchToMainThread(mCallback);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
+    }
+
+    JS::Rooted<JSObject*> obj(aCx, workerPrivate->GlobalScope()->GetWrapper());
+    JS::ExposeValueToActiveJS(aValue);
+
+    js::ErrorReport report(aCx);
+    if (NS_WARN_IF(!report.init(aCx, aValue))) {
+      JS_ClearPendingException(aCx);
+      return;
+    }
+
+    nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
+    xpcReport->Init(report.report(), report.message(),
+                    /* aIsChrome = */ false, /* aWindowID = */ 0);
+
+    nsRefPtr<AsyncErrorReporter> aer =
+      new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(), xpcReport);
+    NS_DispatchToMainThread(aer);
+  }
+};
+
+NS_IMPL_ISUPPORTS0(LifecycleEventPromiseHandler)
+
+bool
+LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx,
+                                                     WorkerPrivate* aWorkerPrivate)
+{
+  aWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+
+  nsRefPtr<ExtendableEvent> event;
+  nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
+
+  if (mEventName.EqualsASCII("install") || mEventName.EqualsASCII("activate")) {
+    ExtendableEventInit init;
+    init.mBubbles = false;
+    init.mCancelable = false;
+    event = ExtendableEvent::Constructor(target, mEventName, init);
+  } else {
+    MOZ_CRASH("Unexpected lifecycle event");
+  }
+
+  event->SetTrusted(true);
+
+  nsRefPtr<Promise> waitUntilPromise =
+    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
+  if (waitUntilPromise) {
+    nsRefPtr<LifecycleEventPromiseHandler> handler =
+      new LifecycleEventPromiseHandler(mCallback);
+    waitUntilPromise->AppendNativeHandler(handler);
+  } else {
+    mCallback->SetResult(false);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback)));
+  }
+
+  return true;
+}
+
+} // anonymous namespace
+
+nsresult
+ServiceWorkerPrivate::SendLifeCycleEvent(const nsAString& aEventType,
+                                         LifeCycleEventCallback* aCallback,
+                                         nsIRunnable* aLoadFailure)
+{
+  nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, aLoadFailure);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<WorkerRunnable> r = new LifecycleEventWorkerRunnable(mWorkerPrivate,
+                                                                aEventType,
+                                                                aCallback);
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+#ifndef MOZ_SIMPLEPUSH
+namespace {
+
+class SendPushEventRunnable final : public WorkerRunnable
+{
+  Maybe<nsTArray<uint8_t>> mData;
+
+public:
+  SendPushEventRunnable(WorkerPrivate* aWorkerPrivate,
+                        const Maybe<nsTArray<uint8_t>>& aData)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+      , mData(aData)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
+
+    PushEventInit pei;
+    if (mData) {
+      const nsTArray<uint8_t>& bytes = mData.ref();
+      JSObject* data = Uint8Array::Create(aCx, bytes.Length(), bytes.Elements());
+      if (!data) {
+        return false;
+      }
+      pei.mData.Construct().SetAsArrayBufferView().Init(data);
+    }
+    pei.mBubbles = false;
+    pei.mCancelable = false;
+
+    ErrorResult result;
+    nsRefPtr<PushEvent> event =
+      PushEvent::Constructor(globalObj, NS_LITERAL_STRING("push"), pei, result);
+    if (NS_WARN_IF(result.Failed())) {
+      result.SuppressException();
+      return false;
+    }
+
+    event->SetTrusted(true);
+
+    nsRefPtr<Promise> waitUntilPromise =
+      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
+    // We don't need to keep the worker alive based on the promise
+    // because we never close it due to inactivity, but this should change
+    // once we do.
+
+    return true;
+  }
+};
+
+class SendPushSubscriptionChangeEventRunnable final : public WorkerRunnable
+{
+public:
+  explicit SendPushSubscriptionChangeEventRunnable(
+    WorkerPrivate* aWorkerPrivate)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+
+    WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
+
+    nsRefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
+
+    nsresult rv = event->InitEvent(NS_LITERAL_STRING("pushsubscriptionchange"),
+                                   false, false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    event->SetTrusted(true);
+
+    globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+    return true;
+  }
+};
+
+} // anonymous namespace
+#endif // !MOZ_SIMPLEPUSH
+
+nsresult
+ServiceWorkerPrivate::SendPushEvent(const Maybe<nsTArray<uint8_t>>& aData)
+{
+#ifdef MOZ_SIMPLEPUSH
+  return NS_ERROR_NOT_AVAILABLE;
+#else
+  nsresult rv = SpawnWorkerIfNeeded(PushEvent, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<WorkerRunnable> r = new SendPushEventRunnable(mWorkerPrivate, aData);
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+#endif // MOZ_SIMPLEPUSH
+}
+
+nsresult
+ServiceWorkerPrivate::SendPushSubscriptionChangeEvent()
+{
+#ifdef MOZ_SIMPLEPUSH
+  return NS_ERROR_NOT_AVAILABLE;
+#else
+  nsresult rv = SpawnWorkerIfNeeded(PushSubscriptionChangeEvent, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<WorkerRunnable> r =
+    new SendPushSubscriptionChangeEventRunnable(mWorkerPrivate);
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+#endif // MOZ_SIMPLEPUSH
+}
+
+namespace {
+
+static void
+DummyNotificationTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  // Nothing.
+}
+
+class AllowWindowInteractionHandler;
+
+class ClearWindowAllowedRunnable final : public WorkerRunnable
+{
+public:
+  ClearWindowAllowedRunnable(WorkerPrivate* aWorkerPrivate,
+                             AllowWindowInteractionHandler* aHandler)
+  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+  , mHandler(aHandler)
+  { }
+
+private:
+  bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    // WorkerRunnable asserts that the dispatch is from parent thread if
+    // the busy count modification is WorkerThreadUnchangedBusyCount.
+    // Since this runnable will be dispatched from the timer thread, we override
+    // PreDispatch and PostDispatch to skip the check.
+    return true;
+  }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult) override
+  {
+    // Silence bad assertions.
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
+
+  nsRefPtr<AllowWindowInteractionHandler> mHandler;
+};
+
+class AllowWindowInteractionHandler final : public PromiseNativeHandler
+{
+  friend class ClearWindowAllowedRunnable;
+  nsCOMPtr<nsITimer> mTimer;
+
+  ~AllowWindowInteractionHandler()
+  {
+    MOZ_ASSERT(!mTimer);
+  }
+
+  void
+  ClearWindowAllowed(WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    if (mTimer) {
+      aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
+      mTimer->Cancel();
+      mTimer = nullptr;
+      MOZ_ALWAYS_TRUE(
+        aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(),
+                                                  false));
+    }
+  }
+
+  void
+  StartClearWindowTimer(WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(!mTimer);
+
+    nsresult rv;
+    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    nsRefPtr<ClearWindowAllowedRunnable> r =
+      new ClearWindowAllowedRunnable(aWorkerPrivate, this);
+
+    nsRefPtr<TimerThreadEventTarget> target =
+      new TimerThreadEventTarget(aWorkerPrivate, r);
+
+    rv = timer->SetTarget(target);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    // The important stuff that *has* to be reversed.
+    if (NS_WARN_IF(!aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), true))) {
+      return;
+    }
+    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
+    timer.swap(mTimer);
+
+    // We swap first and then initialize the timer so that even if initializing
+    // fails, we still clean the busy count and interaction count correctly.
+    // The timer can't be initialized before modifying the busy count since the
+    // timer thread could run and call the timeout but the worker may
+    // already be terminating and modifying the busy count could fail.
+    rv = mTimer->InitWithFuncCallback(DummyNotificationTimerCallback, nullptr,
+                                      gDOMDisableOpenClickDelay,
+                                      nsITimer::TYPE_ONE_SHOT);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ClearWindowAllowed(aWorkerPrivate);
+      return;
+    }
+  }
+
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit AllowWindowInteractionHandler(WorkerPrivate* aWorkerPrivate)
+  {
+    StartClearWindowTimer(aWorkerPrivate);
+  }
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
+    ClearWindowAllowed(worker);
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
+    ClearWindowAllowed(worker);
+  }
+};
+
+NS_IMPL_ISUPPORTS0(AllowWindowInteractionHandler)
+
+bool
+ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+{
+  mHandler->ClearWindowAllowed(aWorkerPrivate);
+  return true;
+}
+
+
+class SendNotificationClickEventRunnable final : public WorkerRunnable
+{
+  const nsString mID;
+  const nsString mTitle;
+  const nsString mDir;
+  const nsString mLang;
+  const nsString mBody;
+  const nsString mTag;
+  const nsString mIcon;
+  const nsString mData;
+  const nsString mBehavior;
+  const nsString mScope;
+
+public:
+  SendNotificationClickEventRunnable(
+    WorkerPrivate* aWorkerPrivate,
+    const nsAString& aID,
+    const nsAString& aTitle,
+    const nsAString& aDir,
+    const nsAString& aLang,
+    const nsAString& aBody,
+    const nsAString& aTag,
+    const nsAString& aIcon,
+    const nsAString& aData,
+    const nsAString& aBehavior,
+    const nsAString& aScope)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+      , mID(aID)
+      , mTitle(aTitle)
+      , mDir(aDir)
+      , mLang(aLang)
+      , mBody(aBody)
+      , mTag(aTag)
+      , mIcon(aIcon)
+      , mData(aData)
+      , mBehavior(aBehavior)
+      , mScope(aScope)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+
+    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
+
+    ErrorResult result;
+    nsRefPtr<Notification> notification =
+      Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(), mID,
+                                        mTitle, mDir, mLang, mBody, mTag, mIcon,
+                                        mData, mScope, result);
+    if (NS_WARN_IF(result.Failed())) {
+      return false;
+    }
+
+    NotificationEventInit nei;
+    nei.mNotification = notification;
+    nei.mBubbles = false;
+    nei.mCancelable = false;
+
+    nsRefPtr<NotificationEvent> event =
+      NotificationEvent::Constructor(target,
+                                     NS_LITERAL_STRING("notificationclick"),
+                                     nei, result);
+    if (NS_WARN_IF(result.Failed())) {
+      return false;
+    }
+
+    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
+    event->SetTrusted(true);
+    nsRefPtr<Promise> waitUntilPromise =
+      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
+                                           event);
+    // If the handler calls WaitUntil(), that will manage its own interaction
+    // 'stack'.
+    aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
+    if (waitUntilPromise) {
+      nsRefPtr<AllowWindowInteractionHandler> handler =
+        new AllowWindowInteractionHandler(aWorkerPrivate);
+      waitUntilPromise->AppendNativeHandler(handler);
+    }
+    // We don't need to keep the worker alive based on the promise
+    // because we never close it due to inactivity, but this should change
+    // once we do.
+    return true;
+  }
+};
+
+} // namespace anonymous
+
+nsresult
+ServiceWorkerPrivate::SendNotificationClickEvent(const nsAString& aID,
+                                                 const nsAString& aTitle,
+                                                 const nsAString& aDir,
+                                                 const nsAString& aLang,
+                                                 const nsAString& aBody,
+                                                 const nsAString& aTag,
+                                                 const nsAString& aIcon,
+                                                 const nsAString& aData,
+                                                 const nsAString& aBehavior,
+                                                 const nsAString& aScope)
+{
+  nsresult rv = SpawnWorkerIfNeeded(NotificationClickEvent, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
+
+  nsRefPtr<WorkerRunnable> r =
+    new SendNotificationClickEventRunnable(mWorkerPrivate, aID, aTitle, aDir,
+                                           aLang, aBody, aTag, aIcon,
+                                           aData, aBehavior, aScope);
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+namespace {
+
+class FetchEventRunnable : public WorkerRunnable
+                         , public nsIHttpHeaderVisitor {
+  nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
+  const nsCString mScriptSpec;
+  nsTArray<nsCString> mHeaderNames;
+  nsTArray<nsCString> mHeaderValues;
+  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+  nsCString mSpec;
+  nsCString mMethod;
+  bool mIsReload;
+  DebugOnly<bool> mIsHttpChannel;
+  RequestMode mRequestMode;
+  RequestRedirect mRequestRedirect;
+  RequestCredentials mRequestCredentials;
+  nsContentPolicyType mContentPolicyType;
+  nsCOMPtr<nsIInputStream> mUploadStream;
+  nsCString mReferrer;
+public:
+  FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
+                     nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
+                     // CSP checks might require the worker script spec
+                     // later on.
+                     const nsACString& aScriptSpec,
+                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                     bool aIsReload)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mInterceptedChannel(aChannel)
+    , mScriptSpec(aScriptSpec)
+    , mClientInfo(Move(aClientInfo))
+    , mIsReload(aIsReload)
+    , mIsHttpChannel(false)
+    , mRequestMode(RequestMode::No_cors)
+    , mRequestRedirect(RequestRedirect::Follow)
+    // By default we set it to same-origin since normal HTTP fetches always
+    // send credentials to same-origin websites unless explicitly forbidden.
+    , mRequestCredentials(RequestCredentials::Same_origin)
+    , mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
+    , mReferrer(kFETCH_CLIENT_REFERRER_STR)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_IMETHOD
+  VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
+  {
+    mHeaderNames.AppendElement(aHeader);
+    mHeaderValues.AppendElement(aValue);
+    return NS_OK;
+  }
+
+  nsresult
+  Init()
+  {
+    AssertIsOnMainThread();
+    nsCOMPtr<nsIChannel> channel;
+    nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIURI> uri;
+    rv = channel->GetURI(getter_AddRefs(uri));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = uri->GetSpec(mSpec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t loadFlags;
+    rv = channel->GetLoadFlags(&loadFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsILoadInfo> loadInfo;
+    rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mContentPolicyType = loadInfo->InternalContentPolicyType();
+
+    nsCOMPtr<nsIURI> referrerURI;
+    rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
+    // We can't bail on failure since certain non-http channels like JAR
+    // channels are intercepted but don't have referrers.
+    if (NS_SUCCEEDED(rv) && referrerURI) {
+      rv = referrerURI->GetSpec(mReferrer);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+    if (httpChannel) {
+      mIsHttpChannel = true;
+
+      rv = httpChannel->GetRequestMethod(mMethod);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
+      NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
+
+      mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
+
+      // This is safe due to static_asserts at top of file.
+      uint32_t redirectMode;
+      internalChannel->GetRedirectMode(&redirectMode);
+      mRequestRedirect = static_cast<RequestRedirect>(redirectMode);
+
+      if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
+        mRequestCredentials = RequestCredentials::Omit;
+      } else {
+        bool includeCrossOrigin;
+        internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin);
+        if (includeCrossOrigin) {
+          mRequestCredentials = RequestCredentials::Include;
+        }
+      }
+
+      rv = httpChannel->VisitNonDefaultRequestHeaders(this);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
+      if (uploadChannel) {
+        MOZ_ASSERT(!mUploadStream);
+        rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream));
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    } else {
+      nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
+      // If it is not an HTTP channel it must be a JAR one.
+      NS_ENSURE_TRUE(jarChannel, NS_ERROR_NOT_AVAILABLE);
+
+      mMethod = "GET";
+
+      mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
+
+      if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
+        mRequestCredentials = RequestCredentials::Omit;
+      }
+    }
+
+    return NS_OK;
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    return DispatchFetchEvent(aCx, aWorkerPrivate);
+  }
+
+private:
+  ~FetchEventRunnable() {}
+
+  class ResumeRequest final : public nsRunnable {
+    nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
+  public:
+    explicit ResumeRequest(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
+      : mChannel(aChannel)
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      AssertIsOnMainThread();
+      nsresult rv = mChannel->ResetInterception();
+      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to resume intercepted network request");
+      return rv;
+    }
+  };
+
+  bool
+  DispatchFetchEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(aCx);
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+    GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
+
+    NS_ConvertUTF8toUTF16 local(mSpec);
+    RequestOrUSVString requestInfo;
+    requestInfo.SetAsUSVString().Rebind(local.Data(), local.Length());
+
+    RootedDictionary<RequestInit> reqInit(aCx);
+    reqInit.mMethod.Construct(mMethod);
+
+    nsRefPtr<InternalHeaders> internalHeaders = new InternalHeaders(HeadersGuardEnum::Request);
+    MOZ_ASSERT(mHeaderNames.Length() == mHeaderValues.Length());
+    for (uint32_t i = 0; i < mHeaderNames.Length(); i++) {
+      ErrorResult result;
+      internalHeaders->Set(mHeaderNames[i], mHeaderValues[i], result);
+      if (NS_WARN_IF(result.Failed())) {
+        result.SuppressException();
+        return false;
+      }
+    }
+
+    nsRefPtr<Headers> headers = new Headers(globalObj.GetAsSupports(), internalHeaders);
+    reqInit.mHeaders.Construct();
+    reqInit.mHeaders.Value().SetAsHeaders() = headers;
+
+    reqInit.mMode.Construct(mRequestMode);
+    reqInit.mRedirect.Construct(mRequestRedirect);
+    reqInit.mCredentials.Construct(mRequestCredentials);
+
+    ErrorResult result;
+    nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, result);
+    if (NS_WARN_IF(result.Failed())) {
+      result.SuppressException();
+      return false;
+    }
+    // For Telemetry, note that this Request object was created by a Fetch event.
+    nsRefPtr<InternalRequest> internalReq = request->GetInternalRequest();
+    MOZ_ASSERT(internalReq);
+    internalReq->SetCreatedByFetchEvent();
+
+    internalReq->SetBody(mUploadStream);
+    internalReq->SetReferrer(NS_ConvertUTF8toUTF16(mReferrer));
+
+    request->SetContentPolicyType(mContentPolicyType);
+
+    // TODO: remove conditional on http here once app protocol support is
+    //       removed from service worker interception
+    MOZ_ASSERT_IF(mIsHttpChannel && internalReq->IsNavigationRequest(),
+                  request->Redirect() == RequestRedirect::Manual);
+
+    RootedDictionary<FetchEventInit> init(aCx);
+    init.mRequest.Construct();
+    init.mRequest.Value() = request;
+    init.mBubbles = false;
+    init.mCancelable = true;
+    init.mIsReload.Construct(mIsReload);
+    nsRefPtr<FetchEvent> event =
+      FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, result);
+    if (NS_WARN_IF(result.Failed())) {
+      result.SuppressException();
+      return false;
+    }
+
+    event->PostInit(mInterceptedChannel, mScriptSpec, Move(mClientInfo));
+    event->SetTrusted(true);
+
+    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
+    nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
+      nsCOMPtr<nsIRunnable> runnable;
+      if (event->DefaultPrevented(aCx)) {
+        runnable = new CancelChannelRunnable(mInterceptedChannel, NS_ERROR_INTERCEPTION_CANCELED);
+      } else {
+        runnable = new ResumeRequest(mInterceptedChannel);
+      }
+
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
+    }
+
+    return true;
+  }
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
+
+} // anonymous namespace
+
+nsresult
+ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
+                                     nsILoadGroup* aLoadGroup,
+                                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                                     bool aIsReload)
+{
+  // if the ServiceWorker script fails to load for some reason, just resume
+  // the original channel.
+  nsCOMPtr<nsIRunnable> failRunnable =
+    NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
+
+  nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
+    new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
+
+  if (NS_WARN_IF(!mInfo)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<FetchEventRunnable> r =
+    new FetchEventRunnable(mWorkerPrivate, handle, mInfo->ScriptSpec(),
+                           Move(aClientInfo), aIsReload);
+  rv = r->Init();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
+                                          nsIRunnable* aLoadFailedRunnable,
+                                          nsILoadGroup* aLoadGroup)
+{
+  AssertIsOnMainThread();
+
+  // XXXcatalinb: We need to have a separate load group that's linked to
+  // an existing tab child to pass security checks on b2g.
+  // This should be fixed in bug 1125961, but for now we enforce updating
+  // the overriden load group when intercepting a fetch.
+  MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup);
+
+  if (mWorkerPrivate) {
+    mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
+    return NS_OK;
+  }
+
+  // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups.
+
+  // Ensure that the IndexedDatabaseManager is initialized
+  NS_WARN_IF(!indexedDB::IndexedDatabaseManager::GetOrCreate());
+
+  WorkerLoadInfo info;
+  nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), mInfo->ScriptSpec(),
+                          nullptr, nullptr);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  info.mResolvedScriptURI = info.mBaseURI;
+  MOZ_ASSERT(!mInfo->CacheName().IsEmpty());
+  info.mServiceWorkerCacheName = mInfo->CacheName();
+  info.mServiceWorkerID = mInfo->ID();
+  info.mLoadGroup = aLoadGroup;
+  info.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
+
+  rv = info.mBaseURI->GetHost(info.mDomain);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  info.mPrincipal = mInfo->GetPrincipal();
+
+  nsContentUtils::StorageAccess access =
+    nsContentUtils::StorageAllowedForPrincipal(info.mPrincipal);
+  info.mStorageAllowed = access > nsContentUtils::StorageAccess::ePrivateBrowsing;
+  info.mPrivateBrowsing = false;
+
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = info.mPrincipal->GetCsp(getter_AddRefs(csp));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  info.mCSP = csp;
+  if (info.mCSP) {
+    rv = info.mCSP->GetAllowsEval(&info.mReportCSPViolations,
+                                  &info.mEvalAllowed);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else {
+    info.mEvalAllowed = true;
+    info.mReportCSPViolations = false;
+  }
+
+  WorkerPrivate::OverrideLoadInfoLoadGroup(info);
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  ErrorResult error;
+  NS_ConvertUTF8toUTF16 scriptSpec(mInfo->ScriptSpec());
+
+  mWorkerPrivate = WorkerPrivate::Constructor(jsapi.cx(),
+                                              scriptSpec,
+                                              false, WorkerTypeService,
+                                              mInfo->Scope(), &info, error);
+  return error.StealNSResult();
+}
+
+void
+ServiceWorkerPrivate::TerminateWorker()
+{
+  AssertIsOnMainThread();
+
+  if (mWorkerPrivate) {
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    NS_WARN_IF(!mWorkerPrivate->Cancel(jsapi.cx()));
+    mWorkerPrivate = nullptr;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -0,0 +1,129 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_workers_serviceworkerprivate_h
+#define mozilla_dom_workers_serviceworkerprivate_h
+
+#include "nsCOMPtr.h"
+
+#include "WorkerPrivate.h"
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+class ServiceWorkerInfo;
+
+class LifeCycleEventCallback : public nsRunnable
+{
+public:
+  // Called on the worker thread.
+  virtual void
+  SetResult(bool aResult) = 0;
+};
+
+// ServiceWorkerPrivate is a wrapper for managing the on-demand aspect of
+// service workers. It handles all event dispatching to the worker and ensures
+// the worker thread is running when needed.
+//
+// To spin up the worker thread we own a |WorkerPrivate| object which can be
+// cancelled if no events are received for a certain amount of time. Note
+// that, at the moment, we never close the worker due to inactivity.
+//
+// Adding an API function for a new event requires calling |SpawnWorkerIfNeeded|
+// before any runnable is dispatched to the worker.
+class ServiceWorkerPrivate final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerPrivate)
+
+  explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
+
+  nsresult
+  SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                   const Optional<Sequence<JS::Value>>& aTransferable,
+                   UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
+
+  // This is used to validate the worker script and continue the installation
+  // process. Note that the callback is dispatched to the main thread
+  // ONLY if the evaluation was successful. Failure is handled by the JS
+  // exception handler which will call ServiceWorkerManager::HandleError.
+  nsresult
+  ContinueOnSuccessfulScriptEvaluation(nsRunnable* aCallback);
+
+  nsresult
+  SendLifeCycleEvent(const nsAString& aEventType,
+                     LifeCycleEventCallback* aCallback,
+                     nsIRunnable* aLoadFailure);
+
+  nsresult
+  SendPushEvent(const Maybe<nsTArray<uint8_t>>& aData);
+
+  nsresult
+  SendPushSubscriptionChangeEvent();
+
+  nsresult
+  SendNotificationClickEvent(const nsAString& aID,
+                             const nsAString& aTitle,
+                             const nsAString& aDir,
+                             const nsAString& aLang,
+                             const nsAString& aBody,
+                             const nsAString& aTag,
+                             const nsAString& aIcon,
+                             const nsAString& aData,
+                             const nsAString& aBehavior,
+                             const nsAString& aScope);
+
+  nsresult
+  SendFetchEvent(nsIInterceptedChannel* aChannel,
+                 nsILoadGroup* aLoadGroup,
+                 UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                 bool aIsReload);
+
+  // This will terminate the current running worker thread and drop the
+  // workerPrivate reference.
+  // Called by ServiceWorkerInfo when [[Clear Registration]] is invoked
+  // or whenever the spec mandates that we terminate the worker.
+  // This is a no-op if the worker has already been stopped.
+  void
+  TerminateWorker();
+
+private:
+  enum WakeUpReason {
+    FetchEvent = 0,
+    PushEvent,
+    PushSubscriptionChangeEvent,
+    MessageEvent,
+    NotificationClickEvent,
+    LifeCycleEvent
+  };
+
+  // |aLoadFailedRunnable| is a runnable dispatched to the main thread
+  // if the script loader failed for some reason, but can be null.
+  nsresult
+  SpawnWorkerIfNeeded(WakeUpReason aWhy,
+                      nsIRunnable* aLoadFailedRunnable,
+                      nsILoadGroup* aLoadGroup = nullptr);
+
+  ~ServiceWorkerPrivate()
+  {
+    MOZ_ASSERT(!mWorkerPrivate);
+  }
+
+  // The info object owns us - this should never be null.
+  ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo;
+
+  // The WorkerPrivate object can only be closed by this class or by the
+  // RuntimeService class if gecko is shutting down. Closing the worker
+  // multiple times is OK, since the second attempt will be a no-op.
+  nsRefPtr<WorkerPrivate> mWorkerPrivate;
+};
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_workers_serviceworkerprivate_h
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -597,31 +597,31 @@ private:
     aWorkerPrivate->CloseHandlerFinished();
   }
 };
 
 class MessageEventRunnable final : public WorkerRunnable
                                  , public StructuredCloneHolder
 {
   // This is only used for messages dispatched to a service worker.
-  nsAutoPtr<ServiceWorkerClientInfo> mEventSource;
+  UniquePtr<ServiceWorkerClientInfo> mEventSource;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHolder(CloningSupported, TransferringSupported,
                           SameProcessDifferentThread)
   {
   }
 
   void
-  SetMessageSource(ServiceWorkerClientInfo* aSource)
+  SetMessageSource(UniquePtr<ServiceWorkerClientInfo>&& aSource)
   {
-    mEventSource = aSource;
+    mEventSource = Move(aSource);
   }
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
     nsCOMPtr<nsPIDOMWindow> parent;
     if (aIsMainThread) {
@@ -2417,17 +2417,17 @@ WorkerPrivateParent<Derived>::NotifyPriv
     if (mParentStatus >= aStatus) {
       return true;
     }
 
     pending = mParentStatus == Pending;
     mParentStatus = aStatus;
   }
 
-  if (IsSharedWorker() || IsServiceWorker()) {
+  if (IsSharedWorker()) {
     RuntimeService* runtime = RuntimeService::GetService();
     MOZ_ASSERT(runtime);
 
     runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
   }
 
   if (pending) {
     WorkerPrivate* self = ParentAsWorkerPrivate();
@@ -2689,17 +2689,17 @@ WorkerPrivateParent<Derived>::ForgetMain
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::PostMessageInternal(
                                             JSContext* aCx,
                                             JS::Handle<JS::Value> aMessage,
                                             const Optional<Sequence<JS::Value>>& aTransferable,
-                                            ServiceWorkerClientInfo* aClientInfo,
+                                            UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                                             ErrorResult& aRv)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     if (mParentStatus > Running) {
       return;
@@ -2729,33 +2729,43 @@ WorkerPrivateParent<Derived>::PostMessag
     new MessageEventRunnable(ParentAsWorkerPrivate(),
                              WorkerRunnable::WorkerThreadModifyBusyCount);
 
   runnable->Write(aCx, aMessage, transferable, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  runnable->SetMessageSource(aClientInfo);
+  runnable->SetMessageSource(Move(aClientInfo));
 
   if (!runnable->Dispatch(aCx)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
 template <class Derived>
 void
+WorkerPrivateParent<Derived>::PostMessage(
+                             JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                             const Optional<Sequence<JS::Value>>& aTransferable,
+                             ErrorResult& aRv)
+{
+  PostMessageInternal(aCx, aMessage, aTransferable, nullptr, aRv);
+}
+
+template <class Derived>
+void
 WorkerPrivateParent<Derived>::PostMessageToServiceWorker(
                              JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
-                             nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
+                             UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                              ErrorResult& aRv)
 {
   AssertIsOnMainThread();
-  PostMessageInternal(aCx, aMessage, aTransferable, aClientInfo.forget(), aRv);
+  PostMessageInternal(aCx, aMessage, aTransferable, Move(aClientInfo), aRv);
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateRuntimeOptions(
                                     JSContext* aCx,
                                     const JS::RuntimeOptions& aRuntimeOptions)
 {
@@ -2938,17 +2948,17 @@ WorkerPrivate::OfflineStatusChangeEventI
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
                                                    SharedWorker* aSharedWorker,
                                                    MessagePort* aPort)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
-  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
+  MOZ_ASSERT(IsSharedWorker());
   MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
 
   if (IsSharedWorker()) {
     nsRefPtr<MessagePortRunnable> runnable =
       new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
     if (!runnable->Dispatch(aCx)) {
       return false;
     }
@@ -4298,16 +4308,20 @@ WorkerPrivate::OverrideLoadInfoLoadGroup
 {
   MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
 
   aLoadInfo.mInterfaceRequestor =
     new WorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,
                                            aLoadInfo.mLoadGroup);
   aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
 
+  // NOTE: this defaults the load context to:
+  //  - private browsing = false
+  //  - content = true
+  //  - use remote tabs = false
   nsCOMPtr<nsILoadGroup> loadGroup =
     do_CreateInstance(NS_LOADGROUP_CONTRACTID);
 
   nsresult rv =
     loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
 
   aLoadInfo.mLoadGroup = loadGroup.forget();
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -221,17 +221,17 @@ private:
   TerminatePrivate(JSContext* aCx)
   {
     return NotifyPrivate(aCx, Terminating);
   }
 
   void
   PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const Optional<Sequence<JS::Value>>& aTransferable,
-                      ServiceWorkerClientInfo* aClientInfo,
+                      UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                       ErrorResult& aRv);
 
   nsresult
   DispatchPrivate(already_AddRefed<WorkerRunnable>&& aRunnable, nsIEventTarget* aSyncLoopTarget);
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -319,26 +319,23 @@ public:
   void
   ForgetOverridenLoadGroup(nsCOMPtr<nsILoadGroup>& aLoadGroupOut);
 
   void
   ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-              const Optional<Sequence<JS::Value> >& aTransferable,
-              ErrorResult& aRv)
-  {
-    PostMessageInternal(aCx, aMessage, aTransferable, nullptr, aRv);
-  }
+              const Optional<Sequence<JS::Value>>& aTransferable,
+              ErrorResult& aRv);
 
   void
   PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
-                             nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
+                             UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                              ErrorResult& aRv);
 
   void
   UpdateRuntimeOptions(JSContext* aCx,
                        const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdateLanguages(JSContext* aCx, const nsTArray<nsString>& aLanguages);
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -68,16 +68,17 @@ UNIFIED_SOURCES += [
     'ServiceWorkerContainer.cpp',
     'ServiceWorkerEvents.cpp',
     'ServiceWorkerManager.cpp',
     'ServiceWorkerManagerChild.cpp',
     'ServiceWorkerManagerParent.cpp',
     'ServiceWorkerManagerService.cpp',
     'ServiceWorkerMessageEvent.cpp',
     'ServiceWorkerPeriodicUpdater.cpp',
+    'ServiceWorkerPrivate.cpp',
     'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'ServiceWorkerScriptCache.cpp',
     'ServiceWorkerWindowClient.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
     'WorkerDebuggerManager.cpp',
     'WorkerPrivate.cpp',