Backed out 12 changesets (bug 1188545) for test_fetch_cors failures CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 30 Sep 2015 11:11:47 -0700
changeset 265282 f39b2e477fbe411fd60e24f5cc964982e50045b2
parent 265281 85b9ecca55e54bf4ccfde43b410f575278953d69
child 265283 74062a46a01aaae0019e7124097fcecf99dbbc2b
push id65897
push userkwierso@gmail.com
push dateWed, 30 Sep 2015 18:11:54 +0000
treeherdermozilla-inbound@f39b2e477fbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1188545
milestone44.0a1
backs oute04738ee72a35a68fde4afb0f9f9bbf6c5815eb9
1989893b59de437c47415f98598fffed6aef233d
11ff29cc25d843a0f191307b81d340471f89a3e9
4b6bdf8598451e6b45fd9ac1ec123b9e013bf358
76eb7ffeca2a2704f181a0e0ba38a7e1689033a7
4473e036b52ef6f11c46b63e7572a5df68680170
2a28cb794b23a5090c3fc6e3b88f8a1adb16c6cd
1fa2f55727f3869d5ee2d6d13683534ffe2a5126
032f4c24fc3448c4f46be914f0de13b1d48fa949
4be675dc1b37adca979606828fcf6ab9dc082dfd
d5d05def5b17335ef6608856c23b43e541cd411d
e94f12b0bcf347eddd4010bba97cee37ea88f993
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
Backed out 12 changesets (bug 1188545) for test_fetch_cors failures CLOSED TREE Backed out changeset e04738ee72a3 (bug 1188545) Backed out changeset 1989893b59de (bug 1188545) Backed out changeset 11ff29cc25d8 (bug 1188545) Backed out changeset 4b6bdf859845 (bug 1188545) Backed out changeset 76eb7ffeca2a (bug 1188545) Backed out changeset 4473e036b52e (bug 1188545) Backed out changeset 2a28cb794b23 (bug 1188545) Backed out changeset 1fa2f55727f3 (bug 1188545) Backed out changeset 032f4c24fc34 (bug 1188545) Backed out changeset 4be675dc1b37 (bug 1188545) Backed out changeset d5d05def5b17 (bug 1188545) Backed out changeset e94f12b0bcf3 (bug 1188545)
dom/interfaces/base/nsIServiceWorkerManager.idl
dom/push/test/lifetime_worker.js
dom/push/test/mochitest.ini
dom/push/test/test_serviceworker_lifetime.html
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
dom/workers/test/serviceworkers/chrome.ini
dom/workers/test/serviceworkers/mochitest.ini
dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
dom/workers/test/serviceworkers/unresolved_fetch_worker.js
modules/libpref/init/all.js
testing/web-platform/mozilla/meta/MANIFEST.json
testing/web-platform/mozilla/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-no-freshness-headers.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/onactivate-script-error.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/oninstall-script-error.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/request-end-to-end.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/sync-xhr-doesnt-deadlock.https.html.ini
testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/unregister-controlling-worker.html
testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-using-registration.https.html
--- 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(8fb9db4f-1d04-402b-9c37-542da06e03b9)]
+[scriptable, builtinclass, uuid(471b2d5d-64c3-4dea-bde1-219853dcaac8)]
 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,20 +88,22 @@ 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 object representing the active worker controlling this
-   * window.
+   * 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.
    */
-  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
+  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow,
+                                               in nsIRunnable aLoadFailedRunnable);
 
   /*
    * 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.
deleted file mode 100644
--- a/dom/push/test/lifetime_worker.js
+++ /dev/null
@@ -1,73 +0,0 @@
-var state = "from_scope";
-var resolvePromiseCallback;
-
-onfetch = function(event) {
-  if (event.request.url.indexOf("lifetime_frame.html") >= 0) {
-    event.respondWith(new Response("iframe_lifetime"));
-    return;
-  }
-
-  if (!event.client) {
-    dump("ERROR: no client to post the message to!\n");
-    dump("request.url=" + event.request.url + "\n");
-    return;
-  }
-
-  event.client.postMessage({type: "fetch", state: state});
-
-  if (event.request.url.indexOf("update") >= 0) {
-    state = "update";
-  } else if (event.request.url.indexOf("wait") >= 0) {
-    event.respondWith(new Promise(function(res, rej) {
-      if (resolvePromiseCallback) {
-        dump("ERROR: service worker was already waiting on a promise.\n");
-      }
-      resolvePromiseCallback = function() {
-        res(new Response("resolve_respondWithPromise"));
-      };
-    }));
-    state = "wait";
-  } else if (event.request.url.indexOf("release") >= 0) {
-    state = "release";
-    resolvePromise();
-  }
-}
-
-function resolvePromise() {
-  if (resolvePromiseCallback === undefined || resolvePromiseCallback == null) {
-    dump("ERROR: wait promise was not set.\n");
-    return;
-  }
-  resolvePromiseCallback();
-  resolvePromiseCallback = null;
-}
-
-onmessage = function(event) {
-  // FIXME(catalinb): we cannot treat these events as extendable
-  // yet. Bug 1143717
-  event.source.postMessage({type: "message", state: state});
-  state = event.data;
-  if (event.data === "release") {
-    resolvePromise();
-  }
-}
-
-onpush = function(event) {
-  // FIXME(catalinb): push message carry no data. So we assume the only
-  // push message we get is "wait"
-  clients.matchAll().then(function(client) {
-    if (client.length == 0) {
-      dump("ERROR: no clients to send the response to.\n");
-    }
-
-    client[0].postMessage({type: "push", state: state});
-
-    state = "wait";
-    event.waitUntil(new Promise(function(res, rej) {
-      if (resolvePromiseCallback) {
-        dump("ERROR: service worker was already waiting on a promise.\n");
-      }
-      resolvePromiseCallback = res;
-    }));
-  });
-}
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -1,16 +1,15 @@
 [DEFAULT]
 subsuite = push
 support-files =
   worker.js
   push-server.sjs
   frame.html
   webpush.js
-  lifetime_worker.js
 
 [test_has_permissions.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_permissions.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_register.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register.html]
@@ -21,10 +20,8 @@ skip-if = os == "android" || toolkit == 
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register_different_scope.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_data.html]
 skip-if = os == "android" || toolkit == "gonk"
 # Disabled for too many intermittent failures (bug 1164432)
 #  [test_try_registering_offline_disabled.html]
 #  skip-if = os == "android" || toolkit == "gonk"
-[test_serviceworker_lifetime.html]
-skip-if = os == "android" || toolkit == "gonk"
deleted file mode 100644
--- a/dom/push/test/test_serviceworker_lifetime.html
+++ /dev/null
@@ -1,331 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-  Test the lifetime management of service workers. We keep this test in
-  dom/push/tests to pass the external network check when connecting to
-  the mozilla push service.
-
-  How this test works:
-  - the service worker maintains a state variable and a promise used for
-    extending its lifetime. Note that the terminating the worker will reset
-    these variables to their default values.
-  - we send 3 types of requests to the service worker:
-    |update|, |wait| and |release|. All three requests will cause the sw to update
-    its state to the new value and reply with a message containing
-    its previous state. Furthermore, |wait| will set a waitUntil or a respondWith
-    promise that's not resolved until the next |release| message.
-  - Each subtest will use a combination of values for the timeouts and check
-    if the service worker is in the correct state as we send it different
-    events.
-  - We also wait and assert for service worker termination using an event dispatched
-    through nsIObserverService.
-  -->
-<head>
-  <title>Test for Bug 1188545</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
-</head>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188545">Mozilla Bug 118845</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-
-<script class="testbody" type="text/javascript">
-
-  function start() {
-    return navigator.serviceWorker.register("lifetime_worker.js", {scope: "./"})
-    .then((swr) => ({registration: swr}));
-  }
-
-  function waitForActiveServiceWorker(ctx) {
-    return navigator.serviceWorker.ready.then(function(result) {
-      ok(ctx.registration.active, "Service Worker is active");
-      return ctx;
-    });
-  }
-
-  function unregister(ctx) {
-    return ctx.registration.unregister().then(function(result) {
-      ok(result, "Unregister should return true.");
-    }, function(e) {
-      dump("Unregistering the SW failed with " + e + "\n");
-    });
-  }
-
-  function registerPushNotification(ctx) {
-    var p = new Promise(function(res, rej) {
-      ctx.registration.pushManager.subscribe().then(
-        function(pushSubscription) {
-          ok(true, "successful registered for push notification");
-          ctx.subscription = pushSubscription;
-          res(ctx);
-        }, function(error) {
-          ok(false, "could not register for push notification");
-          res(ctx);
-        });
-    });
-    return p;
-  }
-
-  function sendPushToPushServer(pushEndpoint) {
-    // Work around CORS for now.
-    var xhr = new XMLHttpRequest();
-    xhr.open('GET', "http://mochi.test:8888/tests/dom/push/test/push-server.sjs", true);
-    xhr.setRequestHeader("X-Push-Method", "PUT");
-    xhr.setRequestHeader("X-Push-Server", pushEndpoint);
-    xhr.send("version=24601");
-  }
-
-  function unregisterPushNotification(ctx) {
-    return ctx.subscription.unsubscribe().then(function(result) {
-      ok(result, "unsubscribe should succeed.");
-      ctx.subscription = null;
-      return ctx;
-    });
-  }
-
-  function createIframe(ctx) {
-    var p = new Promise(function(res, rej) {
-      var iframe = document.createElement('iframe');
-      // This file doesn't exist, the service worker will give us an empty
-      // document.
-      iframe.src = "http://mochi.test:8888/tests/dom/push/test/lifetime_frame.html";
-
-      iframe.onload = function() {
-        ctx.iframe = iframe;
-        res(ctx);
-      }
-      document.body.appendChild(iframe);
-    });
-    return p;
-  }
-
-  function closeIframe(ctx) {
-    ctx.iframe.parentNode.removeChild(ctx.iframe);
-    return new Promise(function(res, rej) {
-      // XXXcatalinb: give the worker more time to "notice" it stopped
-      // controlling documents
-      ctx.iframe = null;
-      setTimeout(res, 0);
-    }).then(() => ctx);
-  }
-
-  function waitAndCheckMessage(contentWindow, expected) {
-    function checkMessage(expected, resolve, event) {
-      ok(event.data.type == expected.type, "Received correct message type: " + expected.type);
-      ok(event.data.state == expected.state, "Service worker is in the correct state: " + expected.state);
-      this.navigator.serviceWorker.onmessage = null;
-      resolve();
-    }
-    return new Promise(function(res, rej) {
-      contentWindow.navigator.serviceWorker.onmessage =
-        checkMessage.bind(contentWindow, expected, res);
-    });
-  }
-
-  function fetchEvent(ctx, expected_state, new_state) {
-    var expected = { type: "fetch", state: expected_state };
-    var p = waitAndCheckMessage(ctx.iframe.contentWindow, expected);
-    ctx.iframe.contentWindow.fetch(new_state);
-    return p;
-  }
-
-  function pushEvent(ctx, expected_state, new_state) {
-    var expected = {type: "push", state: expected_state};
-    var p = waitAndCheckMessage(ctx.iframe.contentWindow, expected);
-    sendPushToPushServer(ctx.subscription.endpoint);
-    return p;
-  }
-
-  function messageEventIframe(ctx, expected_state, new_state) {
-    var expected = {type: "message", state: expected_state};
-    var p = waitAndCheckMessage(ctx.iframe.contentWindow, expected);
-    ctx.iframe.contentWindow.navigator.serviceWorker.controller.postMessage(new_state);
-    return p;
-  }
-
-  function messageEvent(ctx, expected_state, new_state) {
-    var expected = {type: "message", state: expected_state};
-    var p = waitAndCheckMessage(window, expected);
-    ctx.registration.active.postMessage(new_state);
-    return p;
-  }
-
-  function checkStateAndUpdate(eventFunction, expected_state, new_state) {
-    return function(ctx) {
-      return eventFunction(ctx, expected_state, new_state)
-        .then(() => ctx);
-    }
-  }
-
-  function setShutdownObserver(expectingEvent) {
-    return function(ctx) {
-      cancelShutdownObserver(ctx);
-
-      ctx.observer_promise = new Promise(function(res, rej) {
-        ctx.observer = {
-          observe: function(subject, topic, data) {
-            ok((topic == "service-worker-shutdown") && expectingEvent, "Service worker was terminated.");
-            this.remove(ctx);
-          },
-          remove: function(ctx) {
-            SpecialPowers.removeObserver(this, "service-worker-shutdown");
-            ctx.observer = null;
-            res(ctx);
-          }
-        }
-        SpecialPowers.addObserver(ctx.observer, "service-worker-shutdown", false);
-      });
-
-      return ctx;
-    }
-  }
-
-  function waitOnShutdownObserver(ctx) {
-    return ctx.observer_promise;
-  }
-
-  function cancelShutdownObserver(ctx) {
-    if (ctx.observer) {
-      ctx.observer.remove(ctx);
-    }
-    return ctx.observer_promise;
-  }
-
-  function subTest(test) {
-    return function(ctx) {
-      return new Promise(function(res, rej) {
-        function run() {
-          test.steps(ctx).catch(function(e) {
-            ok(false, "Some test failed with error: " + e);
-          }).then((ctx) => res(ctx));
-        }
-
-        SpecialPowers.pushPrefEnv({"set" : test.prefs}, run);
-      });
-    }
-  }
-
-  var test1 = {
-    prefs: [
-      ["dom.serviceWorkers.idle_timeout", 0],
-      ["dom.serviceWorkers.idle_extended_timeout", 2999999]
-    ],
-    // Test that service workers are terminated after the grace period expires
-    // when there are no pending waitUntil or respondWith promises.
-    steps: function(ctx) {
-      // Test with fetch events and respondWith promises
-      return createIframe(ctx)
-        .then(setShutdownObserver(true))
-        .then(checkStateAndUpdate(fetchEvent, "from_scope", "update"))
-        .then(waitOnShutdownObserver)
-        .then(setShutdownObserver(false))
-        .then(checkStateAndUpdate(fetchEvent, "from_scope", "wait"))
-        .then(checkStateAndUpdate(fetchEvent, "wait", "update"))
-        .then(checkStateAndUpdate(fetchEvent, "update", "update"))
-        .then(setShutdownObserver(true))
-        // The service worker should be terminated when the promise is resolved.
-        .then(checkStateAndUpdate(fetchEvent, "update", "release"))
-        .then(waitOnShutdownObserver)
-        .then(setShutdownObserver(false))
-        .then(closeIframe)
-        .then(cancelShutdownObserver)
-
-        // Test with push events and message events
-        .then(createIframe)
-        .then(setShutdownObserver(false))
-        .then(checkStateAndUpdate(pushEvent, "from_scope", "wait"))
-        .then(setShutdownObserver(true))
-        .then(checkStateAndUpdate(messageEventIframe, "wait", "update"))
-        .then(waitOnShutdownObserver)
-        .then(closeIframe)
-    }
-  }
-
-  var test2 = {
-    prefs: [
-      ["dom.serviceWorkers.idle_timeout", 0],
-      ["dom.serviceWorkers.idle_extended_timeout", 2999999]
-    ],
-    steps: function(ctx) {
-      // Non push workers are terminated when they stop controlling documents.
-      return createIframe(ctx)
-        .then(setShutdownObserver(true))
-        .then(checkStateAndUpdate(fetchEvent, "from_scope", "wait"))
-        .then(closeIframe)
-        .then(waitOnShutdownObserver)
-
-      // Push workers are exempt from this rule.
-        .then(createIframe)
-        .then(setShutdownObserver(false))
-        .then(checkStateAndUpdate(pushEvent, "from_scope", "wait"))
-        .then(closeIframe)
-        .then(setShutdownObserver(true))
-        .then(checkStateAndUpdate(messageEvent, "wait", "release"))
-        .then(waitOnShutdownObserver)
-    }
-  };
-
-  var test3 = {
-    prefs: [
-      ["dom.serviceWorkers.idle_timeout", 2999999],
-      ["dom.serviceWorkers.idle_extended_timeout", 0]
-    ],
-    steps: function(ctx) {
-      // set the grace period to 0 and dispatch a message which will reset
-      // the internal sw timer to the new value.
-      var test3_1 = {
-        prefs: [
-          ["dom.serviceWorkers.idle_timeout", 0]
-        ],
-        steps: function(ctx) {
-          return new Promise(function(res, rej) {
-            ctx.registration.active.postMessage("update");
-            res(ctx);
-          });
-        }
-      }
-
-      // Test that service worker is closed when the extended timeout expired
-      return createIframe(ctx)
-        .then(setShutdownObserver(false))
-        .then(checkStateAndUpdate(messageEvent, "from_scope", "update"))
-        .then(checkStateAndUpdate(messageEventIframe, "update", "update"))
-        .then(checkStateAndUpdate(fetchEvent, "update", "wait"))
-        .then(setShutdownObserver(true))
-        .then(subTest(test3_1)) // This should cause the internal timer to expire.
-        .then(waitOnShutdownObserver)
-        .then(closeIframe)
-    }
-  }
-
-  function runTest() {
-    start()
-      .then(waitForActiveServiceWorker)
-      .then(registerPushNotification)
-      .then(subTest(test1))
-      .then(subTest(test2))
-      .then(subTest(test3))
-      .then(unregisterPushNotification)
-      .then(unregister)
-      .catch(function(e) {
-        ok(false, "Some test failed with error " + e)
-      }).then(SimpleTest.finish);
-  }
-
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.push.enabled", true],
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true]
-    ]}, runTest);
-  SpecialPowers.addPermission('push', true, document);
-  SimpleTest.waitForExplicitFinish();
-</script>
-</body>
-</html>
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -273,37 +273,55 @@ GetWorkerPref(const nsACString& aPref,
     else {
       result = aDefault;
     }
   }
 
   return result;
 }
 
-// This function creates a key for a SharedWorker composed by "name|scriptSpec".
+// 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".
 // 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();
-  aKey.SetCapacity(aScriptSpec.Length() + aName.Length() + 3);
+  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.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();
@@ -1430,25 +1448,26 @@ 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);
   }
 
-  nsCString sharedWorkerScriptSpec;
-  if (isSharedWorker) {
+  const bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
+  if (isSharedWorker || isServiceWorker) {
     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?!");
@@ -1508,20 +1527,26 @@ RuntimeService::RegisterWorker(JSContext
     }
     else if (isServiceWorker) {
       domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
     }
     else {
       domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
     }
 
-    if (isSharedWorker) {
-      const nsCString& sharedWorkerName = aWorkerPrivate->WorkerName();
+    if (isSharedWorker || isServiceWorker) {
+      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);
     }
@@ -1550,30 +1575,26 @@ RuntimeService::RegisterWorker(JSContext
       // The navigator overridden properties should have already been read.
 
       Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
       mNavigatorPropertiesLoaded = true;
     }
 
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
-    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());
-      }
+    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) {
@@ -1621,25 +1642,31 @@ RuntimeService::UnregisterWorker(JSConte
     }
     else {
       MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
                  "Don't know about this worker!");
       domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
     }
 
 
-    if (aWorkerPrivate->IsSharedWorker()) {
+    if (aWorkerPrivate->IsSharedWorker() ||
+        aWorkerPrivate->IsServiceWorker()) {
       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()) {
@@ -1673,20 +1700,20 @@ RuntimeService::UnregisterWorker(JSConte
       aWorkerPrivate->IsServiceWorker()) {
     AssertIsOnMainThread();
     aWorkerPrivate->CloseAllSharedWorkers();
   }
 
   if (parent) {
     parent->RemoveChildWorker(aCx, aWorkerPrivate);
   }
-  else if (aWorkerPrivate->IsSharedWorker()) {
+  else if (aWorkerPrivate->IsSharedWorker() || aWorkerPrivate->IsServiceWorker()) {
     mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
   }
-  else if (aWorkerPrivate->IsDedicatedWorker()) {
+  else {
     // May be null.
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
     nsTArray<WorkerPrivate*>* windowArray;
     MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray));
 
     MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate));
 
@@ -2279,17 +2306,17 @@ RuntimeService::RemoveSharedWorkerFromWi
                                   void* aUserArg)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aData.get());
   MOZ_ASSERT(aUserArg);
 
   auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
 
-  MOZ_ASSERT(workerPrivate->IsSharedWorker());
+  MOZ_ASSERT(workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker());
 
   if (aData->RemoveElement(workerPrivate)) {
     MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
 
     if (aData->IsEmpty()) {
       return PL_DHASH_REMOVE;
     }
   }
@@ -2342,17 +2369,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()) {
+      if (worker->IsSharedWorker() || worker->IsServiceWorker()) {
         worker->CloseSharedWorkersForWindow(aWindow);
       } else if (!worker->Cancel(cx)) {
         JS_ReportPendingException(cx);
       }
     }
   }
 }
 
@@ -2400,64 +2427,69 @@ RuntimeService::ThawWorkersForWindow(nsP
       if (!workers[index]->Thaw(cx, aWindow)) {
         JS_ReportPendingException(cx);
       }
     }
   }
 }
 
 nsresult
-RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal,
-                                   const nsAString& aScriptURL,
-                                   const nsACString& aName,
-                                   SharedWorker** aSharedWorker)
+RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal,
+                                           const nsAString& aScriptURL,
+                                           const nsACString& aName,
+                                           WorkerType aType,
+                                           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,
-                                           WorkerTypeShared, &loadInfo);
+                                           aType, &loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName,
+  return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName, aType,
                                         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,
-                            aLoadInfo->mPrivateBrowsing, key);
+                            NS_ConvertUTF16toUTF8(aLoadInfo->mServiceWorkerCacheName),
+                            aType, 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
@@ -2466,17 +2498,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,
-                                 WorkerTypeShared, aName, aLoadInfo, rv);
+                                 aType, 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);
@@ -2517,30 +2549,36 @@ RuntimeService::CreateSharedWorkerFromLo
   return NS_OK;
 }
 
 void
 RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWorkerPrivate);
-  MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
+  MOZ_ASSERT(aWorkerPrivate->IsSharedWorker() ||
+             aWorkerPrivate->IsServiceWorker());
 
   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,17 +147,32 @@ public:
 
   void
   ThawWorkersForWindow(nsPIDOMWindow* aWindow);
 
   nsresult
   CreateSharedWorker(const GlobalObject& aGlobal,
                      const nsAString& aScriptURL,
                      const nsACString& aName,
-                     SharedWorker** aSharedWorker);
+                     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);
+  }
 
   void
   ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
 
   const NavigatorProperties&
   GetNavigatorProperties() const
   {
     return mNavigatorProperties;
@@ -289,18 +304,26 @@ 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 "ServiceWorkerPrivate.h"
+#include "SharedWorker.h"
 #include "WorkerPrivate.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 
 #ifdef XP_WIN
 #undef PostMessage
@@ -37,22 +37,25 @@ 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)
+                             ServiceWorkerInfo* aInfo,
+                             SharedWorker* aSharedWorker)
   : DOMEventTargetHelper(aWindow),
-    mInfo(aInfo)
+    mInfo(aInfo),
+    mSharedWorker(aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aInfo);
+  MOZ_ASSERT(mSharedWorker);
 
   // This will update our state too.
   mInfo->AppendWorker(this);
 }
 
 ServiceWorker::~ServiceWorker()
 {
   AssertIsOnMainThread();
@@ -60,16 +63,19 @@ 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);
 }
 
@@ -91,19 +97,33 @@ 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;
   }
 
-  UniquePtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
-  ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate();
-  aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
+  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();
 }
 
 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,16 +25,17 @@ 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
@@ -47,16 +48,18 @@ 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
@@ -66,24 +69,34 @@ 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);
+  ServiceWorker(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo,
+                SharedWorker* aSharedWorker);
 
   // 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,16 +235,17 @@ 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,16 +1,17 @@
 /* -*- 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"
@@ -67,22 +68,22 @@ FetchEvent::FetchEvent(EventTarget* aOwn
 }
 
 FetchEvent::~FetchEvent()
 {
 }
 
 void
 FetchEvent::PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     const nsACString& aScriptSpec,
-                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
+                     nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo)
 {
   mChannel = aChannel;
-  mScriptSpec.Assign(aScriptSpec);
-  mClientInfo = Move(aClientInfo);
+  mServiceWorker = aServiceWorker;
+  mClientInfo = aClientInfo;
 }
 
 /*static*/ already_AddRefed<FetchEvent>
 FetchEvent::Constructor(const GlobalObject& aGlobal,
                         const nsAString& aType,
                         const FetchEventInit& aOptions,
                         ErrorResult& aRv)
 {
@@ -101,29 +102,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 nsACString& aScriptSpec)
+                 const ChannelInfo& aWorkerChannelInfo)
     : mChannel(aChannel)
+    , mServiceWorker(aServiceWorker)
     , mInternalResponse(aInternalResponse)
     , mWorkerChannelInfo(aWorkerChannelInfo)
-    , mScriptSpec(aScriptSpec)
   {
   }
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
 
@@ -164,17 +165,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 = mScriptSpec;
+      url = mServiceWorker->Info()->ScriptSpec();
     }
     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);
@@ -186,79 +187,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;
-  bool mRequestWasHandled;
 public:
   NS_DECL_ISUPPORTS
 
   RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
+                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
                      RequestMode aRequestMode, bool aIsClientRequest,
-                     bool aIsNavigationRequest,
-                     const nsACString& aScriptSpec)
+                     bool aIsNavigationRequest)
     : mInterceptedChannel(aChannel)
+    , mServiceWorker(aServiceWorker)
     , mRequestMode(aRequestMode)
     , mIsClientRequest(aIsClientRequest)
     , mIsNavigationRequest(aIsNavigationRequest)
-    , mScriptSpec(aScriptSpec)
-    , mRequestWasHandled(false)
   {
   }
 
   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()
-  {
-    if (!mRequestWasHandled) {
-      CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
-    }
-  }
+  ~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 nsCString& aScriptSpec)
+                     const ChannelInfo& aWorkerChannelInfo)
     : 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->mScriptSpec);
+                               data->mWorkerChannelInfo);
   } else {
     event = new CancelChannelRunnable(data->mInterceptedChannel,
                                       NS_ERROR_INTERCEPTION_FAILED);
   }
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(event)));
 }
 
 class MOZ_STACK_CLASS AutoCancel
@@ -351,19 +345,18 @@ RespondWithHandler::ResolvedCallback(JSC
     return;
   }
 
   nsRefPtr<InternalResponse> ir = response->GetInternalResponse();
   if (NS_WARN_IF(!ir)) {
     return;
   }
 
-  nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, ir,
-                                                               worker->GetChannelInfo(),
-                                                               mScriptSpec));
+  nsAutoPtr<RespondWithClosure> closure(
+      new RespondWithClosure(mInterceptedChannel, mServiceWorker, ir, worker->GetChannelInfo()));
   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));
@@ -384,52 +377,47 @@ RespondWithHandler::ResolvedCallback(JSC
       return;
     }
   } else {
     RespondWithCopyComplete(closure.forget(), NS_OK);
   }
 
   MOZ_ASSERT(!closure);
   autoCancel.Reset();
-  mRequestWasHandled = true;
 }
 
 void
 RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
 {
   CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
 }
 
 void
 RespondWithHandler::CancelRequest(nsresult aStatus)
 {
   nsCOMPtr<nsIRunnable> runnable =
     new CancelChannelRunnable(mInterceptedChannel, aStatus);
   NS_DispatchToMainThread(runnable);
-  mRequestWasHandled = true;
 }
 
 } // namespace
 
 void
 FetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv)
 {
   if (mWaitToRespond) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  if (!mPromise) {
-    mPromise = &aArg;
-  }
   nsRefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
   mWaitToRespond = true;
   nsRefPtr<RespondWithHandler> handler =
-    new RespondWithHandler(mChannel, mRequest->Mode(), ir->IsClientRequest(),
-                           ir->IsNavigationRequest(), mScriptSpec);
+    new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode(),
+                           ir->IsClientRequest(), ir->IsNavigationRequest());
   aArg.AppendNativeHandler(handler);
 }
 
 already_AddRefed<ServiceWorkerClient>
 FetchEvent::GetClient()
 {
   if (!mClient) {
     if (!mClientInfo) {
@@ -447,18 +435,17 @@ FetchEvent::GetClient()
 }
 
 NS_IMPL_ADDREF_INHERITED(FetchEvent, Event)
 NS_IMPL_RELEASE_INHERITED(FetchEvent, Event)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, Event, mRequest, mClient,
-                                   mPromise)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, Event, mRequest, mClient)
 
 ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
   : Event(aOwner, nullptr, nullptr)
 {
 }
 
 void
 ExtendableEvent::WaitUntil(Promise& aPromise, ErrorResult& aRv)
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -46,21 +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;
-  nsCString mScriptSpec;
-  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
-  nsRefPtr<Promise> mPromise;
+  nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
   bool mIsReload;
   bool mWaitToRespond;
 protected:
   explicit FetchEvent(EventTarget* aOwner);
   ~FetchEvent();
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -68,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,
-                const nsACString& aScriptSpec,
-                UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
+                nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
+                nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo);
 
   static already_AddRefed<FetchEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const FetchEventInit& aOptions,
               ErrorResult& aRv);
 
   bool
@@ -102,23 +101,16 @@ public:
   {
     return mIsReload;
   }
 
   void
   RespondWith(Promise& aArg, ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  GetPromise() const
-  {
-    nsRefPtr<Promise> p = mPromise;
-    return p.forget();
-  }
-
-  already_AddRefed<Promise>
   ForwardTo(const nsAString& aUrl);
 
   already_AddRefed<Promise>
   Default();
 };
 
 class ExtendableEvent : public Event
 {
@@ -206,16 +198,17 @@ 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)
@@ -237,18 +230,22 @@ public:
               const nsAString& aType,
               const PushEventInit& aOptions,
               ErrorResult& aRv)
   {
     nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
     return Constructor(owner, aType, aOptions, aRv);
   }
 
-  PushMessageData*
-  GetData() const
+  void PostInit(nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
+  {
+    mServiceWorker = aServiceWorker;
+  }
+
+  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,17 +57,16 @@
 #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"
@@ -106,16 +105,26 @@ 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
@@ -323,45 +332,43 @@ ServiceWorkerJob::Done(nsresult aStatus)
     mQueue->Done(this);
   }
 }
 
 void
 ServiceWorkerRegistrationInfo::Clear()
 {
   if (mInstallingWorker) {
+    // FIXME(nsm): Terminate installing worker.
     mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
-    mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
     mInstallingWorker = nullptr;
     // FIXME(nsm): Abort any inflight requests from installing worker.
   }
 
   if (mWaitingWorker) {
     mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
 
     nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal,
                                                        mWaitingWorker->CacheName());
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to purge the waiting cache.");
     }
 
-    mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
     mWaitingWorker = nullptr;
   }
 
   if (mActiveWorker) {
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
 
     nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal,
                                                        mActiveWorker->CacheName());
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to purge the active cache.");
     }
 
-    mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
     mActiveWorker = nullptr;
   }
 
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->InvalidateServiceWorkerRegistrationWorker(this,
                                                  WhichServiceWorker::INSTALLING_WORKER |
                                                  WhichServiceWorker::WAITING_WORKER |
@@ -398,16 +405,18 @@ 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);
 }
@@ -446,76 +455,112 @@ class ContinueLifecycleTask : public nsI
 {
   NS_DECL_ISUPPORTS
 
 protected:
   virtual ~ContinueLifecycleTask()
   { }
 
 public:
-  virtual void ContinueAfterWorkerEvent(bool aSuccess) = 0;
+  virtual void ContinueAfterWorkerEvent(bool aSuccess,
+                                        bool aActivateImmediately) = 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) override;
+  void ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately) override;
 };
 
 class ContinueActivateTask final : public ContinueLifecycleTask
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
 
 public:
   explicit ContinueActivateTask(ServiceWorkerRegistrationInfo* aReg)
     : mRegistration(aReg)
   { }
 
   void
-  ContinueAfterWorkerEvent(bool aSuccess) override;
+  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) override;
 };
 
-class ContinueLifecycleRunnable final : public LifeCycleEventCallback
+class ContinueLifecycleRunnable final : public nsRunnable
 {
   nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mSuccess;
+  bool mActivateImmediately;
 
 public:
-  explicit ContinueLifecycleRunnable(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask)
+  ContinueLifecycleRunnable(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
+                            bool aSuccess,
+                            bool aActivateImmediately)
     : mTask(aTask)
-    , mSuccess(false)
+    , mSuccess(aSuccess)
+    , mActivateImmediately(aActivateImmediately)
   {
-    AssertIsOnMainThread();
-  }
-
-  void
-  SetResult(bool aResult) override
-  {
-    mSuccess = aResult;
+    MOZ_ASSERT(!NS_IsMainThread());
   }
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
-    mTask->ContinueAfterWorkerEvent(mSuccess);
+    mTask->ContinueAfterWorkerEvent(mSuccess, mActivateImmediately);
     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()
@@ -582,22 +627,52 @@ public:
 
 class ContinueUpdateRunnable final : public nsRunnable
 {
   nsMainThreadPtrHandle<nsISupports> mJob;
 public:
   explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<nsISupports> aJob)
     : mJob(aJob)
   {
-    AssertIsOnMainThread();
+    MOZ_ASSERT(!NS_IsMainThread());
   }
 
   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:
@@ -1000,27 +1075,38 @@ 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));
-    nsRefPtr<nsRunnable> callback = new ContinueUpdateRunnable(handle);
-
-    ServiceWorkerPrivate* workerPrivate =
-      mUpdateAndInstallInfo->WorkerPrivate();
-    rv = workerPrivate->ContinueOnSuccessfulScriptEvaluation(callback);
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
+
+    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)) {
       return FailScopeUpdate(swm, scopeKey);
     }
   }
 
   void
   FailScopeUpdate(ServiceWorkerManager* aSwm, const nsACString& aScopeKey)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -1080,18 +1166,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);
 
@@ -1102,35 +1188,46 @@ public:
     nsCOMPtr<nsIRunnable> upr =
       NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
         swm,
         &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
         mRegistration);
 
     NS_DispatchToMainThread(upr);
 
-    // Call ContinueAfterInstallEvent(false) on main thread if the SW
+    // Call ContinueAfterInstallEvent(false, false) on main thread if the SW
     // script fails to load.
-    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);
+    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();
 
     // This triggers Step 4.7 "Queue a task to run the following substeps..."
     // which sends the install event to the worker.
-    ServiceWorkerPrivate* workerPrivate =
-      mRegistration->mInstallingWorker->WorkerPrivate();
-    rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
-                                           callback, failRunnable);
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      ContinueAfterInstallEvent(false /* aSuccess */);
-    }
+    r->Dispatch(jsapi.cx());
   }
 
 private:
   void
   Update()
   {
     // Since Update() is called synchronously from Start(), we can assert this.
     MOZ_ASSERT(!mCanceled);
@@ -1147,18 +1244,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
@@ -1219,17 +1316,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)
+  ContinueAfterInstallEvent(bool aInstallEventSuccess, bool aActivateImmediately)
   {
     if (mCanceled) {
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     if (!mRegistration->mInstallingWorker) {
       NS_WARNING("mInstallingWorker was null.");
       return Done(NS_ERROR_DOM_ABORT_ERR);
@@ -1244,17 +1341,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) {
-      mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker();
+      // FIXME(nsm): Terminate
       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.");
       }
     }
@@ -1305,21 +1402,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)
+ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
 {
   // 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);
+  mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
 }
 
 static bool
 IsFromAuthenticatedOriginInternal(nsIDocument* aDoc)
 {
   nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
 
   bool authenticatedOrigin = false;
@@ -1519,26 +1616,355 @@ 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)
+ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */)
 {
   mRegistration->FinishActivate(aSuccess);
 }
 
 void
 ServiceWorkerRegistrationInfo::PurgeActiveWorker()
 {
   nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker.forget();
@@ -1584,28 +2010,39 @@ ServiceWorkerRegistrationInfo::Activate(
                                                                 this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
   nsCOMPtr<nsIRunnable> failRunnable =
     NS_NewRunnableMethodWithArg<bool>(this,
                                       &ServiceWorkerRegistrationInfo::FinishActivate,
                                       false /* success */);
 
-  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);
+  MOZ_ASSERT(mActiveWorker);
+  nsRefPtr<ServiceWorker> serviceWorker;
+  nsresult rv =
+    swm->CreateServiceWorker(mPrincipal,
+                             mActiveWorker,
+                             failRunnable,
+                             getter_AddRefs(serviceWorker));
   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;
@@ -1851,69 +2288,305 @@ 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;
   }
 
-  ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
+  nsRefPtr<ServiceWorker> serviceWorker =
+    CreateServiceWorkerForScope(attrs, aScope, nullptr /* failure runnable */);
   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;
     }
-    return serviceWorker->WorkerPrivate()->SendPushEvent(Some(data));
+    r = new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(), data,
+                                  serviceWorkerHandle);
   } else {
     MOZ_ASSERT(optional_argc == 0);
-    return serviceWorker->WorkerPrivate()->SendPushEvent(Nothing());
-  }
-#endif // MOZ_SIMPLEPUSH
+    r = new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(),
+                                  serviceWorkerHandle);
+  }
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+#endif
 }
 
 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;
   }
 
-  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
-  if (!info) {
+  nsRefPtr<ServiceWorker> serviceWorker =
+    CreateServiceWorkerForScope(attrs, aScope, nullptr /* fail runnable */);
+  if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
-  return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
+  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;
 #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,
@@ -1922,26 +2595,40 @@ ServiceWorkerManager::SendNotificationCl
                                                  const nsAString& aData,
                                                  const nsAString& aBehavior)
 {
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
-  if (!info) {
+  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
+
+  nsRefPtr<ServiceWorker> serviceWorker =
+    CreateServiceWorkerForScope(attrs, aScope, nullptr);
+  if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
-
-  ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
-  return workerPrivate->SendNotificationClickEvent(aID, aTitle, aDir,
-                                                   aLang, aBody, aTag,
-                                                   aIcon, aData, aBehavior,
-                                                   NS_ConvertUTF8toUTF16(aScope));
+  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;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
                                       nsISupports** aPromise)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
@@ -2047,49 +2734,49 @@ ServiceWorkerManager::CheckReadyPromise(
       new ServiceWorkerRegistrationMainThread(aWindow, scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
-ServiceWorkerInfo*
-ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
-                                              const nsACString& aScope)
+already_AddRefed<ServiceWorker>
+ServiceWorkerManager::CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes,
+                                                  const nsACString& aScope,
+                                                  nsIRunnable* aLoadFailedRunnable)
 {
   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;
   }
 
-  return registration->mActiveWorker;
-}
-
-ServiceWorkerInfo*
-ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
-{
-  AssertIsOnMainThread();
-
-  nsRefPtr<ServiceWorkerRegistrationInfo> registration;
-  GetDocumentRegistration(aDocument, getter_AddRefs(registration));
-
-  if (!registration) {
+  if (!registration->mActiveWorker) {
     return nullptr;
   }
 
-  return registration->mActiveWorker;
+  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();
 }
 
 class ServiceWorkerUnregisterJob final : public ServiceWorkerJob
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   const nsCString mScope;
   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
   nsCOMPtr<nsIPrincipal> mPrincipal;
@@ -2348,16 +3035,67 @@ 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());
@@ -2747,23 +3485,16 @@ void
 ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
 {
   aRegistration->StopControllingADocument();
   if (!aRegistration->IsControllingDocuments()) {
     if (aRegistration->mPendingUninstall) {
       aRegistration->Clear();
       RemoveRegistration(aRegistration);
     } else {
-      // If the registration has an active worker that is running
-      // this might be a good time to stop it.
-      if (aRegistration->mActiveWorker) {
-        ServiceWorkerPrivate* serviceWorkerPrivate =
-          aRegistration->mActiveWorker->WorkerPrivate();
-        serviceWorkerPrivate->NoteStoppedControllingDocuments();
-      }
       aRegistration->TryToActivate();
     }
   }
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
                                      const nsAString& aUrl, nsAString& aScope)
@@ -2898,46 +3629,305 @@ ServiceWorkerManager::GetServiceWorkerFo
   } else {
     MOZ_CRASH("Invalid worker type");
   }
 
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
-  nsRefPtr<ServiceWorker> serviceWorker = new ServiceWorker(window, info);
+  // 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;
+  }
 
   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
 {
-  nsRefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
-  nsCOMPtr<nsIInterceptedChannel> mChannel;
-  nsCOMPtr<nsILoadGroup> mLoadGroup;
-  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+  WorkerPrivate* mWorkerPrivate;
+  nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
+  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
+  nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
   bool mIsReload;
 public:
-  ContinueDispatchFetchEventRunnable(ServiceWorkerPrivate* aServiceWorkerPrivate,
-                                     nsIInterceptedChannel* aChannel,
-                                     nsILoadGroup* aLoadGroup,
-                                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+  ContinueDispatchFetchEventRunnable(WorkerPrivate* aWorkerPrivate,
+                                     nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
+                                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
+                                     nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                                      bool aIsReload)
-    : mServiceWorkerPrivate(aServiceWorkerPrivate)
+    : mWorkerPrivate(aWorkerPrivate)
     , mChannel(aChannel)
-    , mLoadGroup(aLoadGroup)
-    , mClientInfo(Move(aClientInfo))
+    , mServiceWorker(aServiceWorker)
+    , mClientInfo(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();
@@ -2961,84 +3951,112 @@ public:
     // if that happens.
     nsresult status;
     rv = channel->GetStatus(&status);
     if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
       HandleError();
       return NS_OK;
     }
 
-    rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup,
-                                               Move(mClientInfo), mIsReload);
+    nsRefPtr<FetchEventRunnable> event =
+      new FetchEventRunnable(mWorkerPrivate, mChannel, mServiceWorker,
+                             mClientInfo, mIsReload);
+    rv = event->Init();
     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());
 
-  nsRefPtr<ServiceWorkerInfo> serviceWorker;
-  nsCOMPtr<nsILoadGroup> loadGroup;
-  UniquePtr<ServiceWorkerClientInfo> clientInfo;
+  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;
 
   if (aIsSubresourceLoad) {
     MOZ_ASSERT(aDoc);
-    serviceWorker = GetActiveWorkerInfoForDocument(aDoc);
-    loadGroup = aDoc->GetDocumentLoadGroup();
-    clientInfo.reset(new ServiceWorkerClientInfo(aDoc));
+    aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
+                                getter_AddRefs(serviceWorker));
+    clientInfo = 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);
-    serviceWorker = registration->mActiveWorker;
-  }
-
-  if (NS_WARN_IF(aRv.Failed()) || !serviceWorker) {
+
+    nsRefPtr<ServiceWorker> sw;
+    aRv = CreateServiceWorker(registration->mPrincipal,
+                              registration->mActiveWorker,
+                              failRunnable,
+                              getter_AddRefs(sw));
+    serviceWorker = sw.forget();
+  }
+
+  if (NS_WARN_IF(aRv.Failed())) {
     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(serviceWorker->WorkerPrivate(),
-                                           aChannel, loadGroup,
-                                           Move(clientInfo), aIsReload);
+    new ContinueDispatchFetchEventRunnable(sw->GetWorkerPrivate(), handle,
+                                           serviceWorkerHandle, clientInfo,
+                                           aIsReload);
 
   return continueRunnable.forget();
 }
 
 void
 ServiceWorkerManager::DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel,
                                                  nsIRunnable* aPreparedRunnable,
                                                  ErrorResult& aRv)
@@ -3055,16 +4073,17 @@ 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)
@@ -3112,35 +4131,41 @@ 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;
   }
 
-  MOZ_ASSERT(registration->mActiveWorker);
-  nsRefPtr<ServiceWorker> serviceWorker =
-    new ServiceWorker(window, registration->mActiveWorker);
+  nsRefPtr<ServiceWorker> serviceWorker;
+  rv = CreateServiceWorkerForWindow(window,
+                                    registration->mActiveWorker,
+                                    aLoadFailedRunnable,
+                                    getter_AddRefs(serviceWorker));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetInstalling(nsIDOMWindow* aWindow,
                                     const nsAString& aScope,
@@ -3166,16 +4191,106 @@ 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();
@@ -4170,37 +5285,16 @@ 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->NoteDeadServiceWorkerInfo();
-}
-
 static uint64_t gServiceWorkerInfoCurrentID = 0;
 
 uint64_t
 ServiceWorkerInfo::GetNextID() const
 {
   return ++gServiceWorkerInfoCurrentID;
 }
 
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -44,17 +44,16 @@ 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();
@@ -171,43 +170,29 @@ 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
@@ -230,17 +215,27 @@ public:
   void SetSkipWaitingFlag()
   {
     AssertIsOnMainThread();
     mSkipWaitingFlag = true;
   }
 
   ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
                     const nsACString& aScriptSpec,
-                    const nsAString& aCacheName);
+                    const nsAString& aCacheName)
+    : mRegistration(aReg)
+    , mScriptSpec(aScriptSpec)
+    , mCacheName(aCacheName)
+    , mState(ServiceWorkerState::EndGuard_)
+    , mServiceWorkerID(GetNextID())
+    , mSkipWaitingFlag(false)
+  {
+    MOZ_ASSERT(mRegistration);
+    MOZ_ASSERT(!aCacheName.IsEmpty());
+  }
 
   ServiceWorkerState
   State() const
   {
     return mState;
   }
 
   const nsString&
@@ -454,28 +449,38 @@ 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);
 
-  ServiceWorkerInfo*
-  GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
-                              const nsACString& aScope);
-
-  ServiceWorkerInfo*
-  GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
+  already_AddRefed<ServiceWorker>
+  CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes,
+                              const nsACString& aScope,
+                              nsIRunnable* aLoadFailedRunnable);
 
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
deleted file mode 100644
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ /dev/null
@@ -1,1346 +0,0 @@
-/* -*- 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;
-
-BEGIN_WORKERS_NAMESPACE
-
-NS_IMPL_ISUPPORTS0(ServiceWorkerPrivate)
-
-// 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);
-
-// Used to keep track of pending waitUntil as well as in-flight extendable events.
-// When the last token is released, we attempt to terminate the worker.
-class KeepAliveToken final : public nsISupports
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit KeepAliveToken(ServiceWorkerPrivate* aPrivate)
-    : mPrivate(aPrivate)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aPrivate);
-    mPrivate->AddToken();
-  }
-
-private:
-  ~KeepAliveToken()
-  {
-    AssertIsOnMainThread();
-    mPrivate->ReleaseToken();
-  }
-
-  nsRefPtr<ServiceWorkerPrivate> mPrivate;
-};
-
-NS_IMPL_ISUPPORTS0(KeepAliveToken)
-
-ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo)
-  : mInfo(aInfo)
-  , mIsPushWorker(false)
-  , mTokenCount(0)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aInfo);
-
-  mIdleWorkerTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  MOZ_ASSERT(mIdleWorkerTimer);
-}
-
-ServiceWorkerPrivate::~ServiceWorkerPrivate()
-{
-  MOZ_ASSERT(!mWorkerPrivate);
-  MOZ_ASSERT(!mTokenCount);
-  MOZ_ASSERT(!mInfo);
-
-  mIdleWorkerTimer->Cancel();
-}
-
-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();
-  }
-
-  // FIXME(catalinb): Bug 1143717
-  // Keep the worker alive when dispatching a message event.
-  mWorkerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
-                                             Move(aClientInfo), rv);
-  return rv.StealNSResult();
-}
-
-namespace {
-
-class CheckScriptEvaluationWithCallback final : public WorkerRunnable
-{
-  nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-  nsRefPtr<nsRunnable> mCallback;
-
-public:
-  CheckScriptEvaluationWithCallback(WorkerPrivate* aWorkerPrivate,
-                                    KeepAliveToken* aKeepAliveToken,
-                                    nsRunnable* aCallback)
-    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
-    , mKeepAliveToken(new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken))
-    , 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);
-
-  MOZ_ASSERT(mKeepAliveToken);
-  nsRefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(mWorkerPrivate,
-                                                                     mKeepAliveToken,
-                                                                     aCallback);
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-namespace {
-
-// Holds the worker alive until the waitUntil promise is resolved or
-// rejected.
-class KeepAliveHandler final : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-
-  virtual ~KeepAliveHandler()
-  {}
-
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit KeepAliveHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken)
-    : mKeepAliveToken(aKeepAliveToken)
-  { }
-
-  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)
-
-class ExtendableEventWorkerRunnable : public WorkerRunnable
-{
-protected:
-  nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-
-public:
-  ExtendableEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
-                                KeepAliveToken* aKeepAliveToken)
-    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aKeepAliveToken);
-
-    mKeepAliveToken =
-      new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken);
-  }
-
-  void
-  DispatchExtendableEventOnWorkerScope(JSContext* aCx,
-                                       WorkerGlobalScope* aWorkerScope,
-                                       ExtendableEvent* aEvent,
-                                       Promise** aWaitUntilPromise)
-  {
-    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;
-    }
-
-    nsRefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
-    if (!waitUntilPromise) {
-      waitUntilPromise =
-        Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
-      if (NS_WARN_IF(result.Failed())) {
-        result.SuppressException();
-        return;
-      }
-    }
-
-    MOZ_ASSERT(waitUntilPromise);
-    nsRefPtr<KeepAliveHandler> keepAliveHandler =
-      new KeepAliveHandler(mKeepAliveToken);
-    waitUntilPromise->AppendNativeHandler(keepAliveHandler);
-
-    if (aWaitUntilPromise) {
-      waitUntilPromise.forget(aWaitUntilPromise);
-    }
-  }
-};
-
-/*
- * 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 ExtendableEventWorkerRunnable
-{
-  nsString mEventName;
-  nsRefPtr<LifeCycleEventCallback> mCallback;
-
-public:
-  LifecycleEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
-                               KeepAliveToken* aToken,
-                               const nsAString& aEventName,
-                               LifeCycleEventCallback* aCallback)
-      : ExtendableEventWorkerRunnable(aWorkerPrivate, aToken)
-      , 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> waitUntil;
-  DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                       event, getter_AddRefs(waitUntil));
-  if (waitUntil) {
-    nsRefPtr<LifecycleEventPromiseHandler> handler =
-      new LifecycleEventPromiseHandler(mCallback);
-    waitUntil->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);
-
-  MOZ_ASSERT(mKeepAliveToken);
-  nsRefPtr<WorkerRunnable> r = new LifecycleEventWorkerRunnable(mWorkerPrivate,
-                                                                mKeepAliveToken,
-                                                                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 ExtendableEventWorkerRunnable
-{
-  Maybe<nsTArray<uint8_t>> mData;
-
-public:
-  SendPushEventRunnable(WorkerPrivate* aWorkerPrivate,
-                        KeepAliveToken* aKeepAliveToken,
-                        const Maybe<nsTArray<uint8_t>>& aData)
-      : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
-      , 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);
-
-    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                         event, nullptr);
-
-    return true;
-  }
-};
-
-class SendPushSubscriptionChangeEventRunnable final : public ExtendableEventWorkerRunnable
-{
-
-public:
-  explicit SendPushSubscriptionChangeEventRunnable(
-    WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken)
-      : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
-  {
-    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);
-
-  MOZ_ASSERT(mKeepAliveToken);
-  nsRefPtr<WorkerRunnable> r = new SendPushEventRunnable(mWorkerPrivate,
-                                                         mKeepAliveToken,
-                                                         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);
-
-  MOZ_ASSERT(mKeepAliveToken);
-  nsRefPtr<WorkerRunnable> r =
-    new SendPushSubscriptionChangeEventRunnable(mWorkerPrivate, mKeepAliveToken);
-  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()
-  {
-  }
-
-  void
-  ClearWindowAllowed(WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (!mTimer) {
-      return;
-    }
-
-    // XXXcatalinb: This *might* be executed after the global was unrooted, in
-    // which case GlobalScope() will return null. Making the check here just
-    // to be safe.
-    WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
-    if (!globalScope) {
-      return;
-    }
-
-    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* workerPrivate = GetWorkerPrivateFromContext(aCx);
-    ClearWindowAllowed(workerPrivate);
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
-    ClearWindowAllowed(workerPrivate);
-  }
-};
-
-NS_IMPL_ISUPPORTS0(AllowWindowInteractionHandler)
-
-bool
-ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-{
-  mHandler->ClearWindowAllowed(aWorkerPrivate);
-  return true;
-}
-
-class SendNotificationClickEventRunnable final : public ExtendableEventWorkerRunnable
-{
-  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,
-                                     KeepAliveToken* aKeepAliveToken,
-                                     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)
-      : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
-      , 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;
-    }
-
-    event->SetTrusted(true);
-    nsRefPtr<Promise> waitUntil;
-    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
-    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                         event, getter_AddRefs(waitUntil));
-      aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
-    if (waitUntil) {
-      nsRefPtr<AllowWindowInteractionHandler> allowWindowInteraction =
-        new AllowWindowInteractionHandler(aWorkerPrivate);
-      waitUntil->AppendNativeHandler(allowWindowInteraction);
-    }
-
-    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, mKeepAliveToken,
-                                           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 {
-
-// Inheriting ExtendableEventWorkerRunnable so that the worker is not terminated
-// while handling the fetch event, though that's very unlikely.
-class FetchEventRunnable : public ExtendableEventWorkerRunnable
-                         , 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,
-                     KeepAliveToken* aKeepAliveToken,
-                     nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     // CSP checks might require the worker script spec
-                     // later on.
-                     const nsACString& aScriptSpec,
-                     UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
-                     bool aIsReload)
-    : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
-    , 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)));
-    }
-
-    nsRefPtr<Promise> respondWithPromise = event->GetPromise();
-    if (respondWithPromise) {
-      nsRefPtr<KeepAliveHandler> keepAliveHandler =
-        new KeepAliveHandler(mKeepAliveToken);
-      respondWithPromise->AppendNativeHandler(keepAliveHandler);
-    }
-    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, mKeepAliveToken, 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);
-    ResetIdleTimeout(aWhy);
-
-    return NS_OK;
-  }
-
-  if (NS_WARN_IF(!mInfo)) {
-    NS_WARNING("Trying to wake up a dead service worker.");
-    return NS_ERROR_FAILURE;
-  }
-
-  // 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);
-  if (NS_WARN_IF(error.Failed())) {
-    return error.StealNSResult();
-  }
-
-  mIsPushWorker = false;
-  ResetIdleTimeout(aWhy);
-
-  return NS_OK;
-}
-
-void
-ServiceWorkerPrivate::TerminateWorker()
-{
-  AssertIsOnMainThread();
-
-  mIdleWorkerTimer->Cancel();
-  mKeepAliveToken = nullptr;
-  if (mWorkerPrivate) {
-    if (Preferences::GetBool("dom.serviceWorkers.testing.enabled")) {
-      nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-      if (os) {
-        os->NotifyObservers(this, "service-worker-shutdown", nullptr);
-      }
-    }
-
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    NS_WARN_IF(!mWorkerPrivate->Terminate(jsapi.cx()));
-    mWorkerPrivate = nullptr;
-  }
-}
-
-void
-ServiceWorkerPrivate::NoteDeadServiceWorkerInfo()
-{
-  AssertIsOnMainThread();
-  mInfo = nullptr;
-  TerminateWorker();
-}
-
-void
-ServiceWorkerPrivate::NoteStoppedControllingDocuments()
-{
-  AssertIsOnMainThread();
-  if (mIsPushWorker) {
-    return;
-  }
-
-  TerminateWorker();
-}
-
-/* static */ void
-ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aPrivate);
-
-  nsRefPtr<ServiceWorkerPrivate> swp = static_cast<ServiceWorkerPrivate*>(aPrivate);
-
-  MOZ_ASSERT(aTimer == swp->mIdleWorkerTimer, "Invalid timer!");
-
-  // Release ServiceWorkerPrivate's token, since the grace period has ended.
-  swp->mKeepAliveToken = nullptr;
-
-  if (swp->mWorkerPrivate) {
-    // If we still have a workerPrivate at this point it means there are pending
-    // waitUntil promises. Wait a bit more until we forcibly terminate the
-    // worker.
-    uint32_t timeout = Preferences::GetInt("dom.serviceWorkers.idle_extended_timeout");
-    DebugOnly<nsresult> rv =
-      swp->mIdleWorkerTimer->InitWithFuncCallback(ServiceWorkerPrivate::TerminateWorkerCallback,
-                                                  aPrivate,
-                                                  timeout,
-                                                  nsITimer::TYPE_ONE_SHOT);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-  }
-}
-
-/* static */ void
-ServiceWorkerPrivate::TerminateWorkerCallback(nsITimer* aTimer, void *aPrivate)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aPrivate);
-
-  nsRefPtr<ServiceWorkerPrivate> serviceWorkerPrivate =
-    static_cast<ServiceWorkerPrivate*>(aPrivate);
-
-  MOZ_ASSERT(aTimer == serviceWorkerPrivate->mIdleWorkerTimer,
-      "Invalid timer!");
-
-  serviceWorkerPrivate->TerminateWorker();
-}
-
-void
-ServiceWorkerPrivate::ResetIdleTimeout(WakeUpReason aWhy)
-{
-  // We should have an active worker if we're reseting the idle timeout
-  MOZ_ASSERT(mWorkerPrivate);
-
-  if (aWhy == PushEvent || aWhy == PushSubscriptionChangeEvent) {
-    mIsPushWorker = true;
-  }
-
-  uint32_t timeout = Preferences::GetInt("dom.serviceWorkers.idle_timeout");
-  DebugOnly<nsresult> rv =
-    mIdleWorkerTimer->InitWithFuncCallback(ServiceWorkerPrivate::NoteIdleWorkerCallback,
-                                           this, timeout,
-                                           nsITimer::TYPE_ONE_SHOT);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (!mKeepAliveToken) {
-    mKeepAliveToken = new KeepAliveToken(this);
-  }
-}
-
-void
-ServiceWorkerPrivate::AddToken()
-{
-  AssertIsOnMainThread();
-  ++mTokenCount;
-}
-
-void
-ServiceWorkerPrivate::ReleaseToken()
-{
-  AssertIsOnMainThread();
-
-  MOZ_ASSERT(mTokenCount > 0);
-  --mTokenCount;
-  if (!mTokenCount) {
-    TerminateWorker();
-  }
-}
-
-END_WORKERS_NAMESPACE
deleted file mode 100644
--- a/dom/workers/ServiceWorkerPrivate.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/* -*- 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 KeepAliveToken;
-
-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.
-//
-// Lifetime management: 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. The worker is kept alive by holding a |KeepAliveToken|
-// reference.
-//
-// Extendable events hold tokens for the duration of their handler execution
-// and until their waitUntil promise is resolved, while ServiceWorkerPrivate
-// will hold a token for |dom.serviceWorkers.idle_timeout| seconds after each
-// new event.
-//
-// Note: All timer events must be handled on the main thread because the
-// worker may block indefinitely the worker thread (e. g. infinite loop in the
-// script).
-//
-// There are 3 cases where we may ignore keep alive tokens:
-// 1. When ServiceWorkerPrivate's token expired, if there are still waitUntil
-// handlers holding tokens, we wait another |dom.serviceWorkers.idle_extended_timeout|
-// seconds before forcibly terminating the worker.
-// 2. If the worker stopped controlling documents and it is not handling push
-// events.
-// 3. The content process is shutting down.
-//
-// Adding an API function for a new event requires calling |SpawnWorkerIfNeeded|
-// with an appropriate reason before any runnable is dispatched to the worker.
-// If the event is extendable then the runnable should inherit
-// ExtendableEventWorkerRunnable.
-class ServiceWorkerPrivate final : public nsISupports
-{
-  friend class KeepAliveToken;
-
-public:
-  NS_DECL_ISUPPORTS
-
-  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();
-
-  void
-  NoteDeadServiceWorkerInfo();
-
-  void
-  NoteStoppedControllingDocuments();
-
-private:
-  enum WakeUpReason {
-    FetchEvent = 0,
-    PushEvent,
-    PushSubscriptionChangeEvent,
-    MessageEvent,
-    NotificationClickEvent,
-    LifeCycleEvent
-  };
-
-  // Timer callbacks
-  static void
-  NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate);
-
-  static void
-  TerminateWorkerCallback(nsITimer* aTimer, void *aPrivate);
-
-  void
-  ResetIdleTimeout(WakeUpReason aWhy);
-
-  void
-  AddToken();
-
-  void
-  ReleaseToken();
-
-  // |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();
-
-  // The info object owns us. It is possible to outlive it for a brief period
-  // of time if there are pending waitUntil promises, in which case it
-  // will be null and |SpawnWorkerIfNeeded| will always fail.
-  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;
-
-  nsCOMPtr<nsITimer> mIdleWorkerTimer;
-
-  // We keep track if this worker received any push events since it was last
-  // woken up. The flag is reset to false every time a new WorkerPrivate
-  // is created.
-  bool mIsPushWorker;
-
-  // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
-  // worker a grace period after each event.
-  nsRefPtr<KeepAliveToken> mKeepAliveToken;
-
-  uint64_t mTokenCount;
-};
-
-} // 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.
-  UniquePtr<ServiceWorkerClientInfo> mEventSource;
+  nsAutoPtr<ServiceWorkerClientInfo> mEventSource;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHolder(CloningSupported, TransferringSupported,
                           SameProcessDifferentThread)
   {
   }
 
   void
-  SetMessageSource(UniquePtr<ServiceWorkerClientInfo>&& aSource)
+  SetMessageSource(ServiceWorkerClientInfo* aSource)
   {
-    mEventSource = Move(aSource);
+    mEventSource = aSource;
   }
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
     nsCOMPtr<nsPIDOMWindow> parent;
     if (aIsMainThread) {
@@ -1011,17 +1011,17 @@ private:
         return true;
       }
 
       if (aWorkerPrivate->IsServiceWorker() || aWorkerPrivate->IsSharedWorker()) {
         if (aWorkerPrivate->IsServiceWorker() && !JSREPORT_IS_WARNING(mFlags)) {
           nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
           MOZ_ASSERT(swm);
           bool handled = swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
-                                          aWorkerPrivate->WorkerName(),
+                                          aWorkerPrivate->SharedWorkerName(),
                                           aWorkerPrivate->ScriptURL(),
                                           mMessage,
                                           mFilename, mLine, mLineNumber,
                                           mColumnNumber, mFlags, mExnType);
           if (handled) {
             return true;
           }
         }
@@ -2095,32 +2095,32 @@ typename WorkerPrivateParent<Derived>::c
 
 template <class Derived>
 WorkerPrivateParent<Derived>::WorkerPrivateParent(
                                            JSContext* aCx,
                                            WorkerPrivate* aParent,
                                            const nsAString& aScriptURL,
                                            bool aIsChromeWorker,
                                            WorkerType aWorkerType,
-                                           const nsACString& aWorkerName,
+                                           const nsACString& aSharedWorkerName,
                                            WorkerLoadInfo& aLoadInfo)
 : mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
   mParent(aParent), mScriptURL(aScriptURL),
-  mWorkerName(aWorkerName), mLoadingWorkerScript(false),
+  mSharedWorkerName(aSharedWorkerName), mLoadingWorkerScript(false),
   mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
   mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
   mWorkerType(aWorkerType),
   mCreationTimeStamp(TimeStamp::Now()),
   mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
 {
   MOZ_ASSERT_IF(!IsDedicatedWorker(),
-                !aWorkerName.IsVoid() && NS_IsMainThread());
-  MOZ_ASSERT_IF(IsDedicatedWorker(), aWorkerName.IsEmpty());
+                !aSharedWorkerName.IsVoid() && NS_IsMainThread());
+  MOZ_ASSERT_IF(IsDedicatedWorker(), aSharedWorkerName.IsEmpty());
 
   if (aLoadInfo.mWindow) {
     AssertIsOnMainThread();
     MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
                "Should have inner window here!");
     BindToOwner(aLoadInfo.mWindow);
   }
 
@@ -2417,17 +2417,17 @@ WorkerPrivateParent<Derived>::NotifyPriv
     if (mParentStatus >= aStatus) {
       return true;
     }
 
     pending = mParentStatus == Pending;
     mParentStatus = aStatus;
   }
 
-  if (IsSharedWorker()) {
+  if (IsSharedWorker() || IsServiceWorker()) {
     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,
-                                            UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                                            ServiceWorkerClientInfo* aClientInfo,
                                             ErrorResult& aRv)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     if (mParentStatus > Running) {
       return;
@@ -2729,43 +2729,33 @@ WorkerPrivateParent<Derived>::PostMessag
     new MessageEventRunnable(ParentAsWorkerPrivate(),
                              WorkerRunnable::WorkerThreadModifyBusyCount);
 
   runnable->Write(aCx, aMessage, transferable, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  runnable->SetMessageSource(Move(aClientInfo));
+  runnable->SetMessageSource(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,
-                             UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                             nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                              ErrorResult& aRv)
 {
   AssertIsOnMainThread();
-  PostMessageInternal(aCx, aMessage, aTransferable, Move(aClientInfo), aRv);
+  PostMessageInternal(aCx, aMessage, aTransferable, aClientInfo.forget(), aRv);
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateRuntimeOptions(
                                     JSContext* aCx,
                                     const JS::RuntimeOptions& aRuntimeOptions)
 {
@@ -2948,17 +2938,17 @@ WorkerPrivate::OfflineStatusChangeEventI
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
                                                    SharedWorker* aSharedWorker,
                                                    MessagePort* aPort)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
-  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
   MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
 
   if (IsSharedWorker()) {
     nsRefPtr<MessagePortRunnable> runnable =
       new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
     if (!runnable->Dispatch(aCx)) {
       return false;
     }
@@ -3826,21 +3816,21 @@ WorkerDebugger::ReportErrorToDebuggerOnM
 
   LogErrorToConsole(aMessage, aFilename, nsString(), aLineno, 0, 0, 0);
 }
 
 WorkerPrivate::WorkerPrivate(JSContext* aCx,
                              WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
-                             const nsACString& aWorkerName,
+                             const nsACString& aSharedWorkerName,
                              WorkerLoadInfo& aLoadInfo)
   : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
                                        aIsChromeWorker, aWorkerType,
-                                       aWorkerName, aLoadInfo)
+                                       aSharedWorkerName, aLoadInfo)
   , mJSContext(nullptr)
   , mPRThread(nullptr)
   , mDebuggerEventLoopLevel(0)
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
   , mFrozen(false)
   , mTimerRunning(false)
@@ -3850,18 +3840,18 @@ WorkerPrivate::WorkerPrivate(JSContext* 
   , mPendingEventQueueClearing(false)
   , mMemoryReporterRunning(false)
   , mBlockedForMemoryReporter(false)
   , mCancelAllPendingRunnables(false)
   , mPeriodicGCTimerRunning(false)
   , mIdleGCTimerRunning(false)
   , mWorkerScriptExecutedSuccessfully(false)
 {
-  MOZ_ASSERT_IF(!IsDedicatedWorker(), !aWorkerName.IsVoid());
-  MOZ_ASSERT_IF(IsDedicatedWorker(), aWorkerName.IsEmpty());
+  MOZ_ASSERT_IF(!IsDedicatedWorker(), !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(IsDedicatedWorker(), aSharedWorkerName.IsEmpty());
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
     aParent->GetAllPreferences(mPreferences);
     mOnLine = aParent->OnLine();
   }
   else {
     AssertIsOnMainThread();
@@ -3930,46 +3920,45 @@ ChromeWorkerPrivate::WorkerAvailable(JSC
   return GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
 }
 
 // static
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
-                           const nsACString& aWorkerName,
+                           const nsACString& aSharedWorkerName,
                            WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
   return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
-                     aWorkerName, aLoadInfo, aRv);
+                     aSharedWorkerName, aLoadInfo, aRv);
 }
 
 // static
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(JSContext* aCx,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
-                           const nsACString& aWorkerName,
+                           const nsACString& aSharedWorkerName,
                            WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
   WorkerPrivate* parent = NS_IsMainThread() ?
                           nullptr :
                           GetCurrentThreadWorkerPrivate();
   if (parent) {
     parent->AssertIsOnWorkerThread();
   } else {
     AssertIsOnMainThread();
   }
 
-  // Only service and shared workers can have names.
   MOZ_ASSERT_IF(aWorkerType != WorkerTypeDedicated,
-                !aWorkerName.IsVoid());
+                !aSharedWorkerName.IsVoid());
   MOZ_ASSERT_IF(aWorkerType == WorkerTypeDedicated,
-                aWorkerName.IsEmpty());
+                aSharedWorkerName.IsEmpty());
 
   Maybe<WorkerLoadInfo> stackLoadInfo;
   if (!aLoadInfo) {
     stackLoadInfo.emplace();
 
     nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
                               aIsChromeWorker, InheritLoadGroup,
                               aWorkerType, stackLoadInfo.ptr());
@@ -3999,17 +3988,17 @@ WorkerPrivate::Constructor(JSContext* aC
   else {
     runtimeService = RuntimeService::GetService();
   }
 
   MOZ_ASSERT(runtimeService);
 
   nsRefPtr<WorkerPrivate> worker =
     new WorkerPrivate(aCx, parent, aScriptURL, aIsChromeWorker,
-                      aWorkerType, aWorkerName, *aLoadInfo);
+                      aWorkerType, aSharedWorkerName, *aLoadInfo);
 
   if (!runtimeService->RegisterWorker(aCx, worker)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   worker->EnableDebugger();
 
@@ -4309,20 +4298,16 @@ 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();
@@ -6308,19 +6293,19 @@ WorkerPrivate::ConnectMessagePort(JSCont
 WorkerGlobalScope*
 WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
 {
   AssertIsOnWorkerThread();
 
   if (!mScope) {
     nsRefPtr<WorkerGlobalScope> globalScope;
     if (IsSharedWorker()) {
-      globalScope = new SharedWorkerGlobalScope(this, WorkerName());
+      globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName());
     } else if (IsServiceWorker()) {
-      globalScope = new ServiceWorkerGlobalScope(this, WorkerName());
+      globalScope = new ServiceWorkerGlobalScope(this, SharedWorkerName());
     } else {
       globalScope = new DedicatedWorkerGlobalScope(this);
     }
 
     JS::Rooted<JSObject*> global(aCx);
     NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
 
     JSAutoCompartment ac(aCx, global);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -157,19 +157,17 @@ protected:
 
   // Protected by mMutex.
   nsRefPtr<EventTarget> mEventTarget;
   nsTArray<nsRefPtr<WorkerRunnable>> mPreStartRunnables;
 
 private:
   WorkerPrivate* mParent;
   nsString mScriptURL;
-  // This is the worker name for shared workers or the worker scope
-  // for service workers.
-  nsCString mWorkerName;
+  nsCString mSharedWorkerName;
   LocationInfo mLocationInfo;
   // The lifetime of these objects within LoadInfo is managed explicitly;
   // they do not need to be cycle collected.
   WorkerLoadInfo mLoadInfo;
 
   Atomic<bool> mLoadingWorkerScript;
 
   // Only used for top level workers.
@@ -223,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,
-                      UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                      ServiceWorkerClientInfo* aClientInfo,
                       ErrorResult& aRv);
 
   nsresult
   DispatchPrivate(already_AddRefed<WorkerRunnable>&& aRunnable, nsIEventTarget* aSyncLoopTarget);
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -321,23 +319,26 @@ 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);
+              const Optional<Sequence<JS::Value> >& aTransferable,
+              ErrorResult& aRv)
+  {
+    PostMessageInternal(aCx, aMessage, aTransferable, nullptr, aRv);
+  }
 
   void
   PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
-                             UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                             nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                              ErrorResult& aRv);
 
   void
   UpdateRuntimeOptions(JSContext* aCx,
                        const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdateLanguages(JSContext* aCx, const nsTArray<nsString>& aLanguages);
@@ -731,20 +732,19 @@ public:
       return nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER;
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid worker type");
       return nsIContentPolicy::TYPE_INVALID;
     }
   }
 
   const nsCString&
-  WorkerName() const
+  SharedWorkerName() const
   {
-    MOZ_ASSERT(IsServiceWorker() || IsSharedWorker());
-    return mWorkerName;
+    return mSharedWorkerName;
   }
 
   bool
   IsStorageAllowed() const
   {
     return mLoadInfo.mStorageAllowed;
   }
 
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -68,17 +68,16 @@ 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',
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -1,10 +1,9 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   app/*
   app2/*
 
-[test_aboutserviceworkers.html]
-skip-if = true #bug 1193319
 [test_app_installation.html]
 [test_privateBrowsing.html]
+[test_aboutserviceworkers.html]
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -159,17 +159,16 @@ support-files =
   sw_clients/dummy.html
   fetch/plugin/worker.js
   fetch/plugin/plugins.html
   eventsource/*
   sw_clients/file_blob_upload_frame.html
   redirect_post.sjs
   xslt_worker.js
   xslt/*
-  unresolved_fetch_worker.js
 
 [test_app_protocol.html]
 skip-if = release_build
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
@@ -253,9 +252,8 @@ skip-if = toolkit == "android" || toolki
 [test_workerupdatefoundevent.html]
 [test_opaque_intercept.html]
 [test_fetch_event_client_postmessage.html]
 [test_xslt.html]
 [test_escapedSlashes.html]
 [test_eventsource_intercept.html]
 [test_not_intercept_plugin.html]
 [test_file_blob_upload.html]
-[test_unresolved_fetch_interception.html]
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
+++ /dev/null
@@ -1,95 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-  Test that an unresolved respondWith promise will reset the channel when
-  the service worker is terminated due to idling.
-  -->
-<head>
-  <title>Test for Bug 1188545</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
-</head>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188545">Mozilla Bug 118845</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-
-<script class="testbody" type="text/javascript">
-
-  function start() {
-    return navigator.serviceWorker.register("unresolved_fetch_worker.js", {scope: "./"})
-    .then((swr) => ({registration: swr}));
-  }
-
-  function waitControlled(ctx) {
-    var p = new Promise(function(res, rej) {
-      if (navigator.serviceWorker.controller) {
-        res(ctx);
-      } else {
-        navigator.serviceWorker.oncontrollerchange = function() {
-          res(ctx);
-          navigator.serviceWorker.oncontrollerchange = null;
-        }
-      }
-    });
-
-    return p;
-  }
-
-  function unregister(ctx) {
-    return ctx.registration.unregister().then(function(result) {
-      ok(result, "Unregister should return true.");
-    }, function(e) {
-      dump("Unregistering the SW failed with " + e + "\n");
-    });
-  }
-
-  function testFetch(ctx) {
-    ok(navigator.serviceWorker.controller, "Controlled");
-
-    var p = fetch("something_that_doesnt_exist_abcd.html")
-      .catch(function() {
-        ok(true, "channel was reset");
-      }).then(function() { return ctx; });
-
-    navigator.serviceWorker.onmessage = function(event) {
-      ok(event.data == "continue", "Got continue message from worker.");
-      // close worker
-      SpecialPowers.pushPrefEnv({"set": [
-        ["dom.serviceWorkers.idle_extended_timeout", 0]
-      ]}, function() {
-        navigator.serviceWorker.controller.postMessage("shutdown");
-      });
-
-      navigator.serviceWorker.onmessage = null;
-    }
-
-    return p;
-  }
-
-  function runTest() {
-    start()
-      .then(waitControlled)
-      .then(testFetch)
-      .then(unregister)
-      .catch(function(e) {
-        ok(false, "Some test failed with error " + e)
-      }).then(SimpleTest.finish);
-  }
-
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.serviceWorkers.idle_timeout", 0],
-    ["dom.serviceWorkers.idle_extended_timeout", 299999],
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true]
-    ]}, runTest);
-  SimpleTest.waitForExplicitFinish();
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/unresolved_fetch_worker.js
+++ /dev/null
@@ -1,14 +0,0 @@
-onfetch = function(event) {
-  if (!event.client) {
-    dump("ERROR: event doesnt have a client");
-  }
-
-  event.client.postMessage("continue");
-
-  // never resolve
-  event.respondWith(new Promise(function(res, rej) {}));
-}
-
-onactivate = function(event) {
-  event.waitUntil(clients.claim());
-}
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -149,22 +149,16 @@ pref("dom.workers.maxPerDomain", 20);
 pref("dom.serviceWorkers.enabled", false);
 
 // Allow service workers to intercept network requests using the fetch event
 pref("dom.serviceWorkers.interception.enabled", false);
 
 // Allow service workers to intercept opaque (cross origin) responses
 pref("dom.serviceWorkers.interception.opaque.enabled", false);
 
-// The amount of time (milliseconds) service workers keep running after each event.
-pref("dom.serviceWorkers.idle_timeout", 30000);
-
-// The amount of time (milliseconds) service workers can be kept running using waitUntil promises.
-pref("dom.serviceWorkers.idle_extended_timeout", 300000);
-
 // Whether nonzero values can be returned from performance.timing.*
 pref("dom.enable_performance", true);
 
 // Whether resource timing will be gathered and returned by performance.GetEntries*
 pref("dom.enable_resource_timing", true);
 
 // Enable high-resolution timing markers for users
 pref("dom.enable_user_timing", true);
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -140,17 +140,16 @@
           {
             "path": "service-workers/service-worker/fetch-event-network-error.https.html",
             "url": "/_mozilla/service-workers/service-worker/fetch-event-network-error.https.html"
           }
         ],
         "service-workers/service-worker/fetch-event-redirect.https.html": [
           {
             "path": "service-workers/service-worker/fetch-event-redirect.https.html",
-            "timeout": "long",
             "url": "/_mozilla/service-workers/service-worker/fetch-event-redirect.https.html"
           }
         ],
         "service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html": [
           {
             "path": "service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html",
             "url": "/_mozilla/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html"
           }
@@ -171,24 +170,22 @@
           {
             "path": "service-workers/service-worker/fetch-header-visibility.https.html",
             "url": "/_mozilla/service-workers/service-worker/fetch-header-visibility.https.html"
           }
         ],
         "service-workers/service-worker/fetch-mixed-content-to-inscope.https.html": [
           {
             "path": "service-workers/service-worker/fetch-mixed-content-to-inscope.https.html",
-            "timeout": "long",
             "url": "/_mozilla/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html"
           }
         ],
         "service-workers/service-worker/fetch-mixed-content-to-outscope.https.html": [
           {
             "path": "service-workers/service-worker/fetch-mixed-content-to-outscope.https.html",
-            "timeout": "long",
             "url": "/_mozilla/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html"
           }
         ],
         "service-workers/service-worker/fetch-request-css-base-url.https.html": [
           {
             "path": "service-workers/service-worker/fetch-request-css-base-url.https.html",
             "url": "/_mozilla/service-workers/service-worker/fetch-request-css-base-url.https.html"
           }
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini
@@ -1,6 +1,6 @@
 [registration-attribute.https.html]
   type: testharness
-  expected: TIMEOUT
+  expected: ERROR
   [Verify registration attribute on ServiceWorkerGlobalScope]
     expected: TIMEOUT
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-no-freshness-headers.https.html.ini
@@ -0,0 +1,3 @@
+[fetch-request-no-freshness-headers.https.html]
+  type: testharness
+  expected: ERROR
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/onactivate-script-error.https.html.ini
@@ -0,0 +1,3 @@
+[onactivate-script-error.https.html]
+  type: testharness
+  expected: ERROR
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/oninstall-script-error.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/oninstall-script-error.https.html.ini
@@ -1,9 +1,9 @@
 [oninstall-script-error.https.html]
   type: testharness
-  expected: OK
+  expected: ERROR
   [install handler throws an error that is cancelled]
     expected: FAIL
 
   [install handler throws an error and prevents default]
     expected: FAIL
 
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/request-end-to-end.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/request-end-to-end.https.html.ini
@@ -1,6 +1,6 @@
 [request-end-to-end.https.html]
   type: testharness
-  expected: TIMEOUT
+  expected: ERROR
   [Request: end-to-end]
     expected: TIMEOUT
 
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/sync-xhr-doesnt-deadlock.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/sync-xhr-doesnt-deadlock.https.html.ini
@@ -1,6 +1,10 @@
 [sync-xhr-doesnt-deadlock.https.html]
   type: testharness
-  expected: OK
+  expected:
+    if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH
+    if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH
+    if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH
+    if debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): CRASH
   [Verify SyncXHR does not deadlock]
     expected: FAIL
 
deleted file mode 100644
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
@@ -62,17 +62,17 @@ promise_test(function(t) {
             undefined,
             'After unregister(), the registration should not found');
           return service_worker_unregister_and_done(t, scope);
         });
   }, 'Unregister on activate event');
 
 promise_test(function(t) {
     var script = 'resources/unregister-worker.js';
-    var scope = 'resources/unregister-controlling-worker.html';
+    var scope = 'resources/unregister-controlling-worker';
 
     var controller;
     var frame;
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
           return wait_for_state(t, registration.installing, 'activated');
         })
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <title>Service Worker: Fetch Event Redirect Handling</title>
-<meta name=timeout content=long>
 <script src="/resources/testharness.js"></script>
 <script src="resources/testharness-helpers.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/get-host-info.sub.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
 <body>
 <script>
 
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <title>Service Worker: Mixed content of fetch()</title>
-<meta name=timeout content=long>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/get-host-info.sub.js"></script>
 <script src="resources/test-helpers.sub.js?pipe=sub"></script>
 <body></body>
 <script>
 if (window.testRunner) {
   // In Chromium we need to change the setting to disallow displaying insecure
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <title>Service Worker: Mixed content of fetch()</title>
-<meta name=timeout content=long>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/get-host-info.sub.js"></script>
 <script src="resources/test-helpers.sub.js?pipe=sub"></script>
 <body></body>
 <script>
 if (window.testRunner) {
   // In Chromium we need to change the setting to disallow displaying insecure
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-using-registration.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-using-registration.https.html
@@ -45,18 +45,16 @@ promise_test(function(t) {
       .then(function(registration) {
           sw_registration = registration;
           add_completion_callback(function() {
               registration.unregister();
             });
           return saw_controllerchanged;
         })
       .then(function() {
-          // XXXcatalinb: Removing the iframe here would terminate the worker
-          // discarding inflight events.
-          // frame.remove();
+          frame.remove();
           assert_not_equals(sw_registration.active, null,
                             'Registration active worker should not be null');
           fetch_tests_from_worker(sw_registration.active);
         });
   }, 'Test skipWaiting while a client is using the registration');
 
 </script>