From 65d6aee9e0d586b5a069c6f816e7fe3d18cc381e Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Tue, 18 Jun 2019 20:09:32 +0800 Subject: [PATCH] import change from rmottola/Arctic-Fox: - Bug 1132141 - Update storage when ServiceWorker registration fails. (5bf56ab4f) - Bug 1001691 - WorkerPrivate::LoadInfo -> WorkerLoadInfo (bc017200f) - Bug 1001691 - Move WorkerType out of WorkerPrivate.h (02751f7b6) - Bug 1131882 - Associate ServiceWorkers with underlying ServiceWorkerInfo. (4492ae042) - Bug 1131874 - ServiceWorker persistence activation fixes. (cd4f32309) - Bug 1142015 - Add source for messages dispatched to a Service Worker. (25b685a06) - Bug 1053275 - Exempt ServiceWorkers from per domain thread limits. (f67251f0d) - Bug 1139561 - Various small ServiceWorker test fixes. (dbd0beae4) - Bug 1130688 - Implement additional scope checking in service worker registration. (cbd8fee66) - Bug 1142841: Convert all nsRefPtr to nsCOMPtr. r=ehsan (9d4e51880) - Bug 1134462 - Synthesize status and headers from Response returned by ServiceWorker. (8203ae32b) - Bug 1134462 - allow null body. (1490bb9bd) - Bug 1141332 - Disable content decoding and use decoded length on intercepted channels. (2eec7968b) - Bug 1134330 - Mark fetch events as reloads appropriately. (a3025a42a) - Bug 1136757 - Add direct Response overload for FetchEvent.respondWith(). (a33248935) - Bug 1134462 - Cleanup Promise usage in fetch test SW. (fbe9f4cd5) - Bug 1134462 followup: Add missing MOZ_OVERRIDE annotation to SynthesizeStatus impls in SynthesizeStatus.h. (fb34b64d4) - Bug 1142124 - Never revalidate cache entries for synthesized responses. (0f4842e41) - Bug 1143155 - Filtered response stores internal response and allows access to headers. (956c334b1) - Bug 1133861 - Enable the Fetch API by default. (e05918105) - Bug 1140791 - Run fetch tests on main thread and workers. (e672969d6) - Bug 1144819 - Change JS_DefineProperty APIs to treat getter=nullptr and setter=nullptr as indicating class getter/setter ops only for data properties. (e030ab7d6) --- docshell/base/nsDocShell.cpp | 3 +- docshell/shistory/src/nsSHistory.cpp | 2 +- dom/base/nsDocument.cpp | 4 +- dom/base/nsFrameLoader.cpp | 2 +- dom/base/nsFrameMessageManager.cpp | 2 +- dom/base/nsGlobalWindow.cpp | 2 +- dom/cache/FetchPut.cpp | 2 +- dom/cache/TypeUtils.cpp | 55 ++- dom/cache/test/mochitest/test_cache.html | 3 +- dom/camera/CameraPreviewMediaStream.cpp | 2 +- dom/events/MessageEvent.cpp | 13 +- dom/events/MessageEvent.h | 13 +- dom/fetch/FetchDriver.cpp | 6 +- dom/fetch/Headers.cpp | 26 -- dom/fetch/InternalResponse.cpp | 44 +- dom/fetch/InternalResponse.h | 73 +++- .../base/nsIServiceWorkerManager.idl | 7 +- dom/media/MediaRecorder.cpp | 4 +- dom/media/MediaStreamGraph.cpp | 4 +- dom/media/MediaStreamGraph.h | 2 +- dom/media/gmp/GMPService.cpp | 4 +- dom/media/gtest/TestGMPCrossOrigin.cpp | 10 +- dom/media/mediasource/MediaSource.cpp | 2 +- .../agnostic/gmp/MediaDataDecoderProxy.cpp | 8 +- dom/media/webaudio/AudioNode.cpp | 2 +- dom/promise/Promise.cpp | 6 +- .../mochitest/fetch/fetch_test_framework.js | 86 ++++ .../mochitest/fetch/test_fetch_basic.html | 8 +- .../fetch/test_fetch_basic_http.html | 8 +- .../fetch/test_fetch_basic_worker.html | 6 +- .../mochitest/fetch/test_fetch_cors.html | 8 +- dom/tests/mochitest/fetch/test_headers.html | 10 +- dom/tests/mochitest/fetch/test_request.html | 22 + dom/tests/mochitest/fetch/test_response.html | 22 + .../mochitest/general/test_interfaces.html | 6 +- dom/webidl/Fetch.webidl | 2 +- dom/webidl/FetchEvent.webidl | 1 + dom/webidl/Headers.webidl | 3 +- dom/webidl/MessageEvent.webidl | 7 +- dom/webidl/Request.webidl | 3 +- dom/webidl/Response.webidl | 3 +- dom/webidl/ServiceWorkerContainer.webidl | 6 +- dom/workers/RuntimeService.cpp | 86 +--- dom/workers/RuntimeService.h | 31 +- dom/workers/ServiceWorker.cpp | 33 +- dom/workers/ServiceWorker.h | 24 +- dom/workers/ServiceWorkerContainer.cpp | 35 +- dom/workers/ServiceWorkerContainer.h | 4 +- dom/workers/ServiceWorkerEvents.cpp | 86 +++- dom/workers/ServiceWorkerEvents.h | 4 + dom/workers/ServiceWorkerManager.cpp | 294 ++++++++----- dom/workers/ServiceWorkerManager.h | 54 ++- dom/workers/ServiceWorkerRegistration.cpp | 22 - dom/workers/ServiceWorkerRegistration.h | 3 - dom/workers/WorkerPrivate.cpp | 409 ++++++++++-------- dom/workers/WorkerPrivate.h | 121 +----- dom/workers/Workers.h | 89 +++- dom/workers/test/fetch/test_interfaces.html | 6 +- dom/workers/test/fetch/test_request.html | 6 +- dom/workers/test/fetch/test_response.html | 6 +- .../serviceworkers/fetch/deliver-gzip.sjs | 17 + .../test/serviceworkers/fetch/fetch_tests.js | 51 +++ .../test/serviceworkers/fetch_event_worker.js | 147 ++++--- dom/workers/test/serviceworkers/mochitest.ini | 4 + .../test/serviceworkers/scope/scope_worker.js | 2 + .../serviceworkers/simpleregister/index.html | 9 - .../source_message_posting_worker.js | 12 + .../test/serviceworkers/test_controller.html | 1 + .../test/serviceworkers/test_fetch_event.html | 20 +- .../serviceworkers/test_install_event.html | 1 + .../test_installation_simple.html | 1 + .../test/serviceworkers/test_match_all.html | 1 + .../test/serviceworkers/test_navigator.html | 1 + .../serviceworkers/test_post_message.html | 1 + .../test_post_message_advanced.html | 1 + .../test_post_message_source.html | 65 +++ .../test/serviceworkers/test_scopes.html | 39 +- .../test_serviceworker_interfaces.html | 1 - .../test_serviceworker_interfaces.js | 2 +- .../test/serviceworkers/test_unregister.html | 33 +- .../serviceworkers/test_workerUnregister.html | 27 +- .../serviceworkers/test_workerUpdate.html | 1 + .../test/serviceworkers/unregister/index.html | 17 +- .../serviceworkers/unregister/unregister.html | 2 +- .../serviceworkers/workerUpdate/update.html | 2 +- dom/workers/test/test_worker_interfaces.js | 6 +- gfx/gl/AndroidSurfaceTexture.h | 2 +- js/src/jsapi.cpp | 10 +- layout/ipc/VsyncParent.cpp | 2 +- .../src/peerconnection/PeerConnectionCtx.cpp | 2 +- .../src/peerconnection/PeerConnectionCtx.h | 4 +- .../peerconnection/PeerConnectionMedia.cpp | 6 +- .../src/peerconnection/PeerConnectionMedia.h | 4 +- modules/libpref/init/all.js | 3 - netwerk/base/NetworkActivityMonitor.cpp | 2 +- netwerk/base/OfflineObserver.cpp | 4 +- netwerk/base/Tickler.cpp | 2 +- netwerk/base/nsAsyncRedirectVerifyHelper.cpp | 2 +- .../base/nsINetworkInterceptController.idl | 8 +- netwerk/cache/nsCacheEntryDescriptor.cpp | 2 +- netwerk/cache/nsDiskCacheDeviceSQL.cpp | 2 +- netwerk/cache2/CacheIOThread.cpp | 2 +- netwerk/cache2/CacheIOThread.h | 2 +- netwerk/protocol/http/HttpChannelChild.cpp | 7 +- netwerk/protocol/http/HttpChannelChild.h | 3 +- netwerk/protocol/http/InterceptedChannel.cpp | 62 ++- netwerk/protocol/http/InterceptedChannel.h | 9 + .../http/nsHttpActivityDistributor.cpp | 2 +- netwerk/protocol/http/nsHttpChannel.cpp | 6 +- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 4 +- widget/android/AndroidJNIWrapper.cpp | 2 +- xpcom/base/AvailableMemoryTracker.cpp | 2 +- xpcom/base/CycleCollectedJSRuntime.cpp | 2 +- xpcom/base/CycleCollectedJSRuntime.h | 4 +- xpcom/glue/nsProxyRelease.cpp | 2 +- xpcom/io/nsInputStreamTee.cpp | 2 +- xpcom/threads/nsEventQueue.cpp | 2 +- xpcom/threads/nsThread.cpp | 2 +- xpcom/threads/nsThreadPool.cpp | 4 +- 119 files changed, 1551 insertions(+), 902 deletions(-) create mode 100644 dom/tests/mochitest/fetch/fetch_test_framework.js create mode 100644 dom/tests/mochitest/fetch/test_request.html create mode 100644 dom/tests/mochitest/fetch/test_response.html create mode 100644 dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs create mode 100644 dom/workers/test/serviceworkers/scope/scope_worker.js create mode 100644 dom/workers/test/serviceworkers/source_message_posting_worker.js create mode 100644 dom/workers/test/serviceworkers/test_post_message_source.html diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index bcb1023264..d80afaef7c 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -14210,7 +14210,8 @@ nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) } } - return swm->DispatchFetchEvent(doc, aChannel); + bool isReload = mLoadType & LOAD_CMD_RELOAD; + return swm->DispatchFetchEvent(doc, aChannel, isReload); } NS_IMETHODIMP diff --git a/docshell/shistory/src/nsSHistory.cpp b/docshell/shistory/src/nsSHistory.cpp index 817ea2c139..a6314df746 100644 --- a/docshell/shistory/src/nsSHistory.cpp +++ b/docshell/shistory/src/nsSHistory.cpp @@ -1406,7 +1406,7 @@ nsSHistory::RemoveEntries(nsTArray& aIDs, int32_t aStartIndex) --index; } if (didRemove && mRootDocShell) { - nsRefPtr ev = + nsCOMPtr ev = NS_NewRunnableMethod(static_cast(mRootDocShell), &nsDocShell::FireDummyOnLocationChange); NS_DispatchToCurrentThread(ev); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 7192fa35e0..038eb26241 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -4403,7 +4403,7 @@ nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet, } if (!mSSApplicableStateNotificationPending) { - nsRefPtr notification = NS_NewRunnableMethod(this, + nsCOMPtr notification = NS_NewRunnableMethod(this, &nsDocument::NotifyStyleSheetApplicableStateChanged); mSSApplicableStateNotificationPending = NS_SUCCEEDED(NS_DispatchToCurrentThread(notification)); @@ -5206,7 +5206,7 @@ nsDocument::UnblockDOMContentLoaded() MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); if (!mSynchronousDOMContentLoaded) { - nsRefPtr ev = + nsCOMPtr ev = NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); NS_DispatchToCurrentThread(ev); } else { diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 986d9f7859..2dd2e9f720 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -2360,7 +2360,7 @@ nsFrameLoader::DoSendAsyncMessage(JSContext* aCx, } if (mChildMessageManager) { - nsRefPtr ev = new nsAsyncMessageToChild(aCx, this, aMessage, + nsCOMPtr ev = new nsAsyncMessageToChild(aCx, this, aMessage, aData, aCpows, aPrincipal); NS_DispatchToCurrentThread(ev); diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index aa23f85bb7..29a777c5f6 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1742,7 +1742,7 @@ public: JS::Handle aCpows, nsIPrincipal* aPrincipal) override { - nsRefPtr ev = + nsCOMPtr ev = new nsAsyncMessageToSameProcessChild(aCx, aMessage, aData, aCpows, aPrincipal); NS_DispatchToCurrentThread(ev); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 351c923680..5ed5f49db0 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -9046,7 +9046,7 @@ private: void nsGlobalWindow::NotifyWindowIDDestroyed(const char* aTopic) { - nsRefPtr runnable = new WindowDestroyedEvent(this, mWindowID, aTopic); + nsCOMPtr runnable = new WindowDestroyedEvent(this, mWindowID, aTopic); nsresult rv = NS_DispatchToCurrentThread(runnable); if (NS_SUCCEEDED(rv)) { mNotifiedIDDestroyed = true; diff --git a/dom/cache/FetchPut.cpp b/dom/cache/FetchPut.cpp index aea24423ae..b72463f52c 100644 --- a/dom/cache/FetchPut.cpp +++ b/dom/cache/FetchPut.cpp @@ -162,7 +162,7 @@ FetchPut::DispatchToMainThread() { MOZ_ASSERT(!mRunnable); - nsRefPtr runnable = new Runnable(this); + nsCOMPtr runnable = new Runnable(this); nsresult rv = NS_DispatchToMainThread(runnable, nsIThread::DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp index b643d69f60..e702836a74 100644 --- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -229,7 +229,7 @@ TypeUtils::ToPCacheResponseWithoutBody(PCacheResponse& aOut, aOut.status() = aIn.GetStatus(); aOut.statusText() = aIn.GetStatusText(); - nsRefPtr headers = aIn.Headers(); + nsRefPtr headers = aIn.UnfilteredHeaders(); MOZ_ASSERT(headers); headers->GetPHeaders(aOut.headers()); aOut.headersGuard() = headers->Guard(); @@ -279,37 +279,14 @@ TypeUtils::ToPCacheQueryParams(PCacheQueryParams& aOut, already_AddRefed TypeUtils::ToResponse(const PCacheResponse& aIn) { - nsRefPtr ir; - switch (aIn.type()) - { - case ResponseType::Error: - ir = InternalResponse::NetworkError(); - break; - case ResponseType::Opaque: - ir = InternalResponse::OpaqueResponse(); - break; - case ResponseType::Default: - ir = new InternalResponse(aIn.status(), aIn.statusText()); - break; - case ResponseType::Basic: - { - nsRefPtr inner = new InternalResponse(aIn.status(), - aIn.statusText()); - ir = InternalResponse::BasicResponse(inner); - break; - } - case ResponseType::Cors: - { - nsRefPtr inner = new InternalResponse(aIn.status(), - aIn.statusText()); - ir = InternalResponse::CORSResponse(inner); - break; - } - default: - MOZ_CRASH("Unexpected ResponseType!"); + if (aIn.type() == ResponseType::Error) { + nsRefPtr error = InternalResponse::NetworkError(); + nsRefPtr r = new Response(GetGlobalObject(), error); + return r.forget(); } - MOZ_ASSERT(ir); + nsRefPtr ir = new InternalResponse(aIn.status(), + aIn.statusText()); ir->SetUrl(NS_ConvertUTF16toUTF8(aIn.url())); nsRefPtr internalHeaders = @@ -325,6 +302,24 @@ TypeUtils::ToResponse(const PCacheResponse& aIn) nsCOMPtr stream = ReadStream::Create(aIn.body()); ir->SetBody(stream); + switch (aIn.type()) + { + case ResponseType::Default: + break; + case ResponseType::Opaque: + ir = ir->OpaqueResponse(); + break; + case ResponseType::Basic: + ir = ir->BasicResponse(); + break; + case ResponseType::Cors: + ir = ir->CORSResponse(); + break; + default: + MOZ_CRASH("Unexpected ResponseType!"); + } + MOZ_ASSERT(ir); + nsRefPtr ref = new Response(GetGlobalObject(), ir); return ref.forget(); } diff --git a/dom/cache/test/mochitest/test_cache.html b/dom/cache/test/mochitest/test_cache.html index ff7bad44b6..fae5ac90d5 100644 --- a/dom/cache/test/mochitest/test_cache.html +++ b/dom/cache/test/mochitest/test_cache.html @@ -13,8 +13,7 @@ diff --git a/dom/tests/mochitest/fetch/test_fetch_basic_http.html b/dom/tests/mochitest/fetch/test_fetch_basic_http.html index b4e75836da..1601d9407f 100644 --- a/dom/tests/mochitest/fetch/test_fetch_basic_http.html +++ b/dom/tests/mochitest/fetch/test_fetch_basic_http.html @@ -42,12 +42,8 @@ function testOnWorker(done) { // Driver // -SpecialPowers.pushPrefEnv({"set": [ - ["dom.fetch.enabled", true] -]}, function() { - testOnWorker(function() { - SimpleTest.finish(); - }); +testOnWorker(function() { + SimpleTest.finish(); }); diff --git a/dom/tests/mochitest/fetch/test_fetch_basic_worker.html b/dom/tests/mochitest/fetch/test_fetch_basic_worker.html index 54f15e5ce9..a373528f0a 100644 --- a/dom/tests/mochitest/fetch/test_fetch_basic_worker.html +++ b/dom/tests/mochitest/fetch/test_fetch_basic_worker.html @@ -36,11 +36,7 @@ SimpleTest.waitForExplicitFinish(); - SpecialPowers.pushPrefEnv({"set": [ - ["dom.fetch.enabled", true] - ]}, function() { - runTest(); - }); + runTest(); diff --git a/dom/tests/mochitest/fetch/test_fetch_cors.html b/dom/tests/mochitest/fetch/test_fetch_cors.html index 30b73081ce..222132bc4d 100644 --- a/dom/tests/mochitest/fetch/test_fetch_cors.html +++ b/dom/tests/mochitest/fetch/test_fetch_cors.html @@ -42,12 +42,8 @@ function testOnWorker(done) { // Driver // -SpecialPowers.pushPrefEnv({"set": [ - ["dom.fetch.enabled", true] -]}, function() { - testOnWorker(function() { - SimpleTest.finish(); - }); +testOnWorker(function() { + SimpleTest.finish(); }); diff --git a/dom/tests/mochitest/fetch/test_headers.html b/dom/tests/mochitest/fetch/test_headers.html index 103c88e629..4e461f6949 100644 --- a/dom/tests/mochitest/fetch/test_headers.html +++ b/dom/tests/mochitest/fetch/test_headers.html @@ -53,13 +53,9 @@ function testOnMainThread(done) { // Driver // -SpecialPowers.pushPrefEnv({"set": [ - ["dom.fetch.enabled", true] -]}, function() { - testOnMainThread(function() { - testOnWorker(function() { - SimpleTest.finish(); - }); +testOnMainThread(function() { + testOnWorker(function() { + SimpleTest.finish(); }); }); diff --git a/dom/tests/mochitest/fetch/test_request.html b/dom/tests/mochitest/fetch/test_request.html new file mode 100644 index 0000000000..6222205455 --- /dev/null +++ b/dom/tests/mochitest/fetch/test_request.html @@ -0,0 +1,22 @@ + + + + + Bug XXXXXX - Test Request object in worker + + + + +

+ +

+
+
+
+
+
diff --git a/dom/tests/mochitest/fetch/test_response.html b/dom/tests/mochitest/fetch/test_response.html
new file mode 100644
index 0000000000..8086bb2251
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_response.html
@@ -0,0 +1,22 @@
+
+
+
+
+  Bug 1039846 - Test Response object in worker
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html
index f33e1aa93f..6de57e596c 100644
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -437,7 +437,7 @@ var interfaceNamesInGlobalScope =
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HashChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "Headers", pref: "dom.fetch.enabled"},
+    "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "History",
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -873,9 +873,9 @@ var interfaceNamesInGlobalScope =
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Rect",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "Request", pref: "dom.fetch.enabled"},
+    "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "Response", pref: "dom.fetch.enabled"},
+    "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RGBColor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
diff --git a/dom/webidl/Fetch.webidl b/dom/webidl/Fetch.webidl
index 22410305a8..ab077c5fcd 100644
--- a/dom/webidl/Fetch.webidl
+++ b/dom/webidl/Fetch.webidl
@@ -27,7 +27,7 @@ interface Body {
 
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface GlobalFetch {
-  [Throws, Func="mozilla::dom::Headers::PrefEnabled"]
+  [Throws]
   Promise fetch(RequestInfo input, optional RequestInit init);
 };
 
diff --git a/dom/webidl/FetchEvent.webidl b/dom/webidl/FetchEvent.webidl
index 5f53dfb5f5..d9114d237f 100644
--- a/dom/webidl/FetchEvent.webidl
+++ b/dom/webidl/FetchEvent.webidl
@@ -18,6 +18,7 @@ interface FetchEvent : Event {
   readonly attribute boolean isReload;
 
   [Throws] void respondWith(Promise r);
+  [Throws] void respondWith(Response r);
   Promise forwardTo(USVString url);
   Promise default();
 };
diff --git a/dom/webidl/Headers.webidl b/dom/webidl/Headers.webidl
index a240e37295..f27a1ec054 100644
--- a/dom/webidl/Headers.webidl
+++ b/dom/webidl/Headers.webidl
@@ -19,8 +19,7 @@ enum HeadersGuardEnum {
 };
 
 [Constructor(optional HeadersInit init),
- Exposed=(Window,Worker),
- Func="mozilla::dom::Headers::PrefEnabled"]
+ Exposed=(Window,Worker)]
 interface Headers {
   [Throws] void append(ByteString name, ByteString value);
   [Throws] void delete(ByteString name);
diff --git a/dom/webidl/MessageEvent.webidl b/dom/webidl/MessageEvent.webidl
index a663540f26..8693d310dd 100644
--- a/dom/webidl/MessageEvent.webidl
+++ b/dom/webidl/MessageEvent.webidl
@@ -31,9 +31,12 @@ interface MessageEvent : Event {
   readonly attribute DOMString lastEventId;
 
   /**
-   * The window or the port which originated this event.
+   * The window, port or client which originated this event.
+   * FIXME(catalinb): Update this when the spec changes are implemented.
+   * https://www.w3.org/Bugs/Public/show_bug.cgi?id=28199
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1143717
    */
-  readonly attribute (WindowProxy or MessagePort)? source;
+  readonly attribute (WindowProxy or MessagePort or Client)? source;
 
   /**
    * Initializes this event with the given data, in a manner analogous to
diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl
index 0ca4ff1160..df8db1ac56 100644
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -10,8 +10,7 @@
 typedef (Request or USVString) RequestInfo;
 
 [Constructor(RequestInfo input, optional RequestInit init),
- Exposed=(Window,Worker),
- Func="mozilla::dom::Headers::PrefEnabled"]
+ Exposed=(Window,Worker)]
 interface Request {
   readonly attribute ByteString method;
   readonly attribute USVString url;
diff --git a/dom/webidl/Response.webidl b/dom/webidl/Response.webidl
index fb6b5cc744..3f15985582 100644
--- a/dom/webidl/Response.webidl
+++ b/dom/webidl/Response.webidl
@@ -8,8 +8,7 @@
  */
 
 [Constructor(optional BodyInit body, optional ResponseInit init),
- Exposed=(Window,Worker),
- Func="mozilla::dom::Headers::PrefEnabled"]
+ Exposed=(Window,Worker)]
 interface Response {
   [NewObject] static Response error();
   [Throws,
diff --git a/dom/webidl/ServiceWorkerContainer.webidl b/dom/webidl/ServiceWorkerContainer.webidl
index c06f29eb3c..271c76298a 100644
--- a/dom/webidl/ServiceWorkerContainer.webidl
+++ b/dom/webidl/ServiceWorkerContainer.webidl
@@ -21,7 +21,7 @@ interface ServiceWorkerContainer : EventTarget {
 
   [Throws]
   Promise register(USVString scriptURL,
-                                              optional RegistrationOptionList options);
+                                              optional RegistrationOptions options);
 
   [Throws]
   Promise getRegistration(optional USVString documentURL = "");
@@ -47,6 +47,6 @@ partial interface ServiceWorkerContainer {
   DOMString getControllingWorkerScriptURLForPath(DOMString path);
 };
 
-dictionary RegistrationOptionList {
-  USVString scope = "/";
+dictionary RegistrationOptions {
+  USVString scope;
 };
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index 6a6230e0c0..3b556882ef 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -59,7 +59,6 @@
 #endif
 
 #include "Principal.h"
-#include "ServiceWorker.h"
 #include "SharedWorker.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -155,7 +154,6 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
 #define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled"
 #endif
 
-#define PREF_DOM_FETCH_ENABLED         "dom.fetch.enabled"
 #define PREF_DOM_CACHES_ENABLED        "dom.caches.enabled"
 #define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion"
 #define PREF_INTL_ACCEPT_LANGUAGES     "intl.accept_languages"
@@ -1433,6 +1431,13 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
   }
 
+  bool exemptFromPerDomainMax = false;
+  if (aWorkerPrivate->IsServiceWorker()) {
+    AssertIsOnMainThread();
+    exemptFromPerDomainMax = Preferences::GetBool("dom.serviceWorkers.exemptFromPerDomainMax",
+                                                  false);
+  }
+
   const nsCString& domain = aWorkerPrivate->Domain();
 
   WorkerDomainInfo* domainInfo;
@@ -1450,7 +1455,8 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 
     queued = gMaxWorkersPerDomain &&
              domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
-             !domain.IsEmpty();
+             !domain.IsEmpty() &&
+             !exemptFromPerDomainMax;
 
     if (queued) {
       domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
@@ -1838,10 +1844,6 @@ RuntimeService::Init()
                                   PREF_DOM_WINDOW_DUMP_ENABLED,
                                   reinterpret_cast(WORKERPREF_DUMP))) ||
 #endif
-      NS_FAILED(Preferences::RegisterCallbackAndCall(
-                                  WorkerPrefChanged,
-                                  PREF_DOM_FETCH_ENABLED,
-                                  reinterpret_cast(WORKERPREF_DOM_FETCH))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_ENABLED,
@@ -2039,10 +2041,6 @@ RuntimeService::Cleanup()
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_ENABLED,
                                   reinterpret_cast(WORKERPREF_DOM_CACHES))) ||
-        NS_FAILED(Preferences::UnregisterCallback(
-                                  WorkerPrefChanged,
-                                  PREF_DOM_FETCH_ENABLED,
-                                  reinterpret_cast(WORKERPREF_DOM_FETCH))) ||
 #if DUMP_CONTROLLED_BY_PREF
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
@@ -2260,61 +2258,6 @@ RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
   }
 }
 
-nsresult
-RuntimeService::CreateServiceWorker(const GlobalObject& aGlobal,
-                                    const nsAString& aScriptURL,
-                                    const nsACString& aScope,
-                                    ServiceWorker** aServiceWorker)
-{
-  nsresult rv;
-
-  nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports());
-  MOZ_ASSERT(window);
-
-  nsRefPtr sharedWorker;
-  rv = CreateSharedWorkerInternal(aGlobal, aScriptURL, aScope,
-                                  WorkerTypeService,
-                                  getter_AddRefs(sharedWorker));
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsRefPtr serviceWorker =
-    new ServiceWorker(window, sharedWorker);
-
-  serviceWorker->mURL = aScriptURL;
-
-  serviceWorker.forget(aServiceWorker);
-  return rv;
-}
-
-nsresult
-RuntimeService::CreateServiceWorkerFromLoadInfo(JSContext* aCx,
-                                               WorkerPrivate::LoadInfo* aLoadInfo,
-                                               const nsAString& aScriptURL,
-                                               const nsACString& aScope,
-                                               ServiceWorker** aServiceWorker)
-{
-
-  nsRefPtr sharedWorker;
-  nsresult rv = CreateSharedWorkerFromLoadInfo(aCx, aLoadInfo, aScriptURL, aScope,
-                                               WorkerTypeService,
-                                               getter_AddRefs(sharedWorker));
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsRefPtr serviceWorker =
-    new ServiceWorker(nullptr, sharedWorker);
-
-  serviceWorker->mURL = aScriptURL;
-
-  serviceWorker.forget(aServiceWorker);
-  return rv;
-}
-
 nsresult
 RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal,
                                            const nsAString& aScriptURL,
@@ -2330,7 +2273,7 @@ RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal,
 
   JSContext* cx = aGlobal.Context();
 
-  WorkerPrivate::LoadInfo loadInfo;
+  WorkerLoadInfo loadInfo;
   nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
                                            false,
                                            WorkerPrivate::OverrideLoadGroup,
@@ -2343,7 +2286,7 @@ RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal,
 
 nsresult
 RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
-                                               WorkerPrivate::LoadInfo* aLoadInfo,
+                                               WorkerLoadInfo* aLoadInfo,
                                                const nsAString& aScriptURL,
                                                const nsACString& aName,
                                                WorkerType aType,
@@ -2628,18 +2571,11 @@ RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
   }
 #endif
 
-  if (key == WORKERPREF_DOM_FETCH) {
-    key = WORKERPREF_DOM_FETCH;
-    sDefaultPreferences[WORKERPREF_DOM_FETCH] =
-      Preferences::GetBool(PREF_DOM_FETCH_ENABLED, false);
-  }
-
   if (key == WORKERPREF_DOM_CACHES) {
     key = WORKERPREF_DOM_CACHES;
     sDefaultPreferences[WORKERPREF_DOM_CACHES] =
       Preferences::GetBool(PREF_DOM_CACHES_ENABLED, false);
   }
-
   // This function should never be registered as a callback for a preference it
   // does not handle.
   MOZ_ASSERT(key != WORKERPREF_COUNT);
diff --git a/dom/workers/RuntimeService.h b/dom/workers/RuntimeService.h
index 94bff10eee..00d598b681 100644
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -8,7 +8,6 @@
 #define mozilla_dom_workers_runtimeservice_h__
 
 #include "Workers.h"
-#include "WorkerPrivate.h" // For the WorkerType enum.
 
 #include "nsIObserver.h"
 
@@ -16,7 +15,6 @@
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
-#include "WorkerPrivate.h"
 
 class nsIRunnable;
 class nsITimer;
@@ -24,7 +22,6 @@ class nsPIDOMWindow;
 
 BEGIN_WORKERS_NAMESPACE
 
-class ServiceWorker;
 class SharedWorker;
 class WorkerThread;
 
@@ -148,17 +145,25 @@ public:
   }
 
   nsresult
-  CreateServiceWorker(const GlobalObject& aGlobal,
-                      const nsAString& aScriptURL,
-                      const nsACString& aScope,
-                      ServiceWorker** aServiceWorker);
+  CreateSharedWorkerForServiceWorker(const GlobalObject& aGlobal,
+                                     const nsAString& aScriptURL,
+                                     const nsACString& aScope,
+                                     SharedWorker** aSharedWorker)
+  {
+    return CreateSharedWorkerInternal(aGlobal, aScriptURL, aScope,
+                                      WorkerTypeService, aSharedWorker);
+  }
 
   nsresult
-  CreateServiceWorkerFromLoadInfo(JSContext* aCx,
-                                  WorkerPrivate::LoadInfo* aLoadInfo,
-                                  const nsAString& aScriptURL,
-                                  const nsACString& aScope,
-                                  ServiceWorker** aServiceWorker);
+  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);
@@ -308,7 +313,7 @@ private:
 
   nsresult
   CreateSharedWorkerFromLoadInfo(JSContext* aCx,
-                                 WorkerPrivate::LoadInfo* aLoadInfo,
+                                 WorkerLoadInfo* aLoadInfo,
                                  const nsAString& aScriptURL,
                                  const nsACString& aName,
                                  WorkerType aType,
diff --git a/dom/workers/ServiceWorker.cpp b/dom/workers/ServiceWorker.cpp
index 68c63d5ae0..11f88ab0c5 100644
--- a/dom/workers/ServiceWorker.cpp
+++ b/dom/workers/ServiceWorker.cpp
@@ -6,6 +6,8 @@
 #include "ServiceWorker.h"
 
 #include "nsPIDOMWindow.h"
+#include "ServiceWorkerClient.h"
+#include "ServiceWorkerManager.h"
 #include "SharedWorker.h"
 #include "WorkerPrivate.h"
 
@@ -38,18 +40,24 @@ ServiceWorkerVisible(JSContext* aCx, JSObject* aObj)
 }
 
 ServiceWorker::ServiceWorker(nsPIDOMWindow* aWindow,
+                             ServiceWorkerInfo* aInfo,
                              SharedWorker* aSharedWorker)
   : DOMEventTargetHelper(aWindow),
-    mState(ServiceWorkerState::Installing),
+    mInfo(aInfo),
     mSharedWorker(aSharedWorker)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aInfo);
   MOZ_ASSERT(mSharedWorker);
+
+  // This will update our state too.
+  mInfo->AppendWorker(this);
 }
 
 ServiceWorker::~ServiceWorker()
 {
   AssertIsOnMainThread();
+  mInfo->RemoveWorker(this);
 }
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
@@ -69,6 +77,12 @@ ServiceWorker::WrapObject(JSContext* aCx)
   return ServiceWorkerBinding::Wrap(aCx, this);
 }
 
+void
+ServiceWorker::GetScriptURL(nsString& aURL) const
+{
+  CopyUTF8toUTF16(mInfo->ScriptSpec(), aURL);
+}
+
 void
 ServiceWorker::PostMessage(JSContext* aCx, JS::Handle aMessage,
                            const Optional>& aTransferable,
@@ -82,7 +96,12 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle aMessage,
     return;
   }
 
-  workerPrivate->PostMessage(aCx, aMessage, aTransferable, aRv);
+  nsCOMPtr window = do_QueryInterface(GetParentObject());
+  nsCOMPtr doc = window->GetExtantDoc();
+  nsAutoPtr clientInfo(new ServiceWorkerClientInfo(doc));
+
+  workerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
+                                            clientInfo, aRv);
 }
 
 WorkerPrivate*
@@ -95,6 +114,16 @@ ServiceWorker::GetWorkerPrivate() const
   return mSharedWorker->GetWorkerPrivate();
 }
 
+void
+ServiceWorker::QueueStateChangeEvent(ServiceWorkerState aState)
+{
+  nsCOMPtr r =
+    NS_NewRunnableMethodWithArg(this,
+                                                    &ServiceWorker::DispatchStateChange,
+                                                    aState);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+}
+
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/workers/ServiceWorker.h b/dom/workers/ServiceWorker.h
index 76452b02be..417570ca05 100644
--- a/dom/workers/ServiceWorker.h
+++ b/dom/workers/ServiceWorker.h
@@ -19,6 +19,8 @@ class Promise;
 
 namespace workers {
 
+class ServiceWorkerInfo;
+class ServiceWorkerManager;
 class SharedWorker;
 
 bool
@@ -26,7 +28,7 @@ ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
 
 class ServiceWorker final : public DOMEventTargetHelper
 {
-  friend class RuntimeService;
+  friend class ServiceWorkerManager;
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorker, DOMEventTargetHelper)
@@ -50,16 +52,17 @@ public:
   }
 
   void
-  GetScriptURL(nsString& aURL) const
+  GetScriptURL(nsString& aURL) const;
+
+  void
+  DispatchStateChange(ServiceWorkerState aState)
   {
-    aURL = mURL;
+    SetState(aState);
+    DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
   }
 
   void
-  DispatchStateChange()
-  {
-    DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
-  }
+  QueueStateChangeEvent(ServiceWorkerState aState);
 
 #ifdef XP_WIN
 #undef PostMessage
@@ -74,14 +77,15 @@ public:
   GetWorkerPrivate() const;
 
 private:
-  // This class can only be created from the RuntimeService.
-  ServiceWorker(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker);
+  // This class can only be created from the ServiceWorkerManager.
+  ServiceWorker(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo,
+                SharedWorker* aSharedWorker);
 
   // This class is reference-counted and will be destroyed from Release().
   ~ServiceWorker();
 
   ServiceWorkerState mState;
-  nsString mURL;
+  const nsRefPtr mInfo;
 
   // To allow ServiceWorkers to potentially drop the backing DOMEventTargetHelper and
   // re-instantiate it later, they simply own a SharedWorker member that
diff --git a/dom/workers/ServiceWorkerContainer.cpp b/dom/workers/ServiceWorkerContainer.cpp
index 9d37400ef1..b1633f46e1 100644
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -73,7 +73,7 @@ ServiceWorkerContainer::WrapObject(JSContext* aCx)
 
 already_AddRefed
 ServiceWorkerContainer::Register(const nsAString& aScriptURL,
-                                 const RegistrationOptionList& aOptions,
+                                 const RegistrationOptions& aOptions,
                                  ErrorResult& aRv)
 {
   nsCOMPtr promise;
@@ -84,7 +84,38 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
     return nullptr;
   }
 
-  aRv = swm->Register(GetOwner(), aOptions.mScope, aScriptURL, getter_AddRefs(promise));
+  nsCOMPtr window = GetOwner();
+  MOZ_ASSERT(window);
+
+  nsresult rv;
+  nsCOMPtr scriptURI;
+  rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr,
+                 window->GetDocBaseURI());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.ThrowTypeError(MSG_INVALID_URL, &aScriptURL);
+    return nullptr;
+  }
+
+  // In ServiceWorkerContainer.register() the scope argument is parsed against
+  // different base URLs depending on whether it was passed or not.
+  nsCOMPtr scopeURI;
+
+  // Step 4. If none passed, parse against script's URL
+  if (!aOptions.mScope.WasPassed()) {
+    nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), NS_LITERAL_CSTRING("./"),
+                            nullptr, scriptURI);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+  } else {
+    // Step 5. Parse against entry settings object's base URL.
+    nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(),
+                            nullptr, window->GetDocBaseURI());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRv.ThrowTypeError(MSG_INVALID_URL, &aOptions.mScope.Value());
+      return nullptr;
+    }
+  }
+
+  aRv = swm->Register(window, scopeURI, scriptURI, getter_AddRefs(promise));
   if (aRv.Failed()) {
     return nullptr;
   }
diff --git a/dom/workers/ServiceWorkerContainer.h b/dom/workers/ServiceWorkerContainer.h
index 382a2462b4..636683e264 100644
--- a/dom/workers/ServiceWorkerContainer.h
+++ b/dom/workers/ServiceWorkerContainer.h
@@ -15,7 +15,7 @@ namespace mozilla {
 namespace dom {
 
 class Promise;
-struct RegistrationOptionList;
+struct RegistrationOptions;
 
 namespace workers {
 class ServiceWorker;
@@ -40,7 +40,7 @@ public:
 
   already_AddRefed
   Register(const nsAString& aScriptURL,
-           const RegistrationOptionList& aOptions,
+           const RegistrationOptions& aOptions,
            ErrorResult& aRv);
 
   already_AddRefed
diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
index e535f14b7a..eae7534dbb 100644
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -23,6 +23,8 @@
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/workers/bindings/ServiceWorker.h"
 
+#include "WorkerPrivate.h"
+
 using namespace mozilla::dom;
 
 BEGIN_WORKERS_NAMESPACE
@@ -102,7 +104,7 @@ public:
   }
 
   NS_IMETHOD
-      Run()
+  Run()
   {
     AssertIsOnMainThread();
 
@@ -115,6 +117,14 @@ public:
       }
     }
 
+    mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText());
+
+    nsAutoTArray entries;
+    mInternalResponse->Headers()->GetEntries(entries);
+    for (uint32_t i = 0; i < entries.Length(); ++i) {
+       mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
+    }
+
     rv = mChannel->FinishSynthesizedResponse();
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to finish synthesized response");
     return rv;
@@ -203,36 +213,50 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu
     return;
   }
 
+  // FIXME(nsm) Bug 1136200 deal with opaque and no-cors (fetch spec 4.2.2.2).
+  if (response->Type() == ResponseType::Error) {
+    return;
+  }
+
+  if (NS_WARN_IF(response->BodyUsed())) {
+    return;
+  }
+
   nsRefPtr ir = response->GetInternalResponse();
   if (NS_WARN_IF(!ir)) {
     return;
   }
 
+  nsAutoPtr closure(new RespondWithClosure(mInterceptedChannel, ir));
   nsCOMPtr body;
   response->GetBody(getter_AddRefs(body));
-  if (NS_WARN_IF(!body) || NS_WARN_IF(response->BodyUsed())) {
-    return;
-  }
-  response->SetBodyUsed();
+  // Errors and redirects may not have a body.
+  if (body) {
+    response->SetBodyUsed();
 
-  nsCOMPtr responseBody;
-  rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  nsAutoPtr closure(new RespondWithClosure(mInterceptedChannel, ir));
-
-  nsCOMPtr stsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-  if (NS_WARN_IF(!stsThread)) {
-    return;
-  }
-  rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
-                    RespondWithCopyComplete, closure.forget());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
+    nsCOMPtr responseBody;
+    rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    nsCOMPtr stsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+    if (NS_WARN_IF(!stsThread)) {
+      return;
+    }
+
+    // XXXnsm, Fix for Bug 1141332 means that if we decide to make this
+    // streaming at some point, we'll need a different solution to that bug.
+    rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
+                      RespondWithCopyComplete, closure.forget());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  } else {
+    RespondWithCopyComplete(closure.forget(), NS_OK);
   }
 
+  MOZ_ASSERT(!closure);
   autoCancel.Reset();
 }
 
@@ -264,6 +288,26 @@ FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv)
   aPromise.AppendNativeHandler(handler);
 }
 
+void
+FetchEvent::RespondWith(Response& aResponse, ErrorResult& aRv)
+{
+  if (mWaitToRespond) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+  nsRefPtr promise = Promise::Create(worker->GlobalScope(), aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+  promise->MaybeResolve(&aResponse);
+
+  RespondWith(*promise, aRv);
+}
+
 already_AddRefed
 FetchEvent::GetClient()
 {
diff --git a/dom/workers/ServiceWorkerEvents.h b/dom/workers/ServiceWorkerEvents.h
index a107dfd758..0fb76f07aa 100644
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -11,6 +11,7 @@
 #include "mozilla/dom/FetchEventBinding.h"
 #include "mozilla/dom/InstallEventBinding.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/Response.h"
 #include "nsProxyRelease.h"
 
 class nsIInterceptedChannel;
@@ -83,6 +84,9 @@ public:
   void
   RespondWith(Promise& aPromise, ErrorResult& aRv);
 
+  void
+  RespondWith(Response& aResponse, ErrorResult& aRv);
+
   already_AddRefed
   ForwardTo(const nsAString& aUrl);
 
diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
index d6c5574790..4cd3cfc16c 100644
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -422,7 +422,7 @@ public:
                                                        const nsMainThreadPtrHandle aJob)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
     , mJob(aJob)
-  { 
+  {
     AssertIsOnMainThread();
   }
 
@@ -442,6 +442,40 @@ public:
   }
 };
 
+namespace {
+nsresult
+GetRequiredScopeStringPrefix(const nsACString& aScriptSpec, nsACString& aPrefix)
+{
+  nsCOMPtr scriptURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptSpec,
+                 nullptr, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = scriptURI->GetPrePath(aPrefix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr scriptURL(do_QueryInterface(scriptURI));
+  if (NS_WARN_IF(!scriptURL)) {
+    return rv;
+  }
+
+  nsAutoCString dir;
+  rv = scriptURL->GetDirectory(dir);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aPrefix.Append(dir);
+  return NS_OK;
+}
+} // anonymous namespace
+
+
+
 class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
                                            public nsIStreamLoaderObserver
 {
@@ -563,14 +597,29 @@ public:
     NS_WARNING("Byte wise check is disabled, just using new one");
     nsRefPtr swm = ServiceWorkerManager::GetInstance();
 
+    // FIXME: Bug 1130101 - Read max scope from Service-Worker-Allowed header.
+    nsAutoCString allowedPrefix;
+    rv = GetRequiredScopeStringPrefix(mRegistration->mScriptSpec, allowedPrefix);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      Fail(NS_ERROR_DOM_SECURITY_ERR);
+      return rv;
+    }
+
+    if (!StringBeginsWith(mRegistration->mScope, allowedPrefix)) {
+      NS_WARNING("By default a service worker's scope is restricted to at or below it's script's location.");
+      Fail(NS_ERROR_DOM_SECURITY_ERR);
+      return NS_ERROR_DOM_SECURITY_ERR;
+    }
+
     // We have to create a ServiceWorker here simply to ensure there are no
     // errors. Ideally we should just pass this worker on to ContinueInstall.
     MOZ_ASSERT(!swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
     swm->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true);
+    nsRefPtr dummyInfo =
+      new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec);
     nsRefPtr serviceWorker;
     rv = swm->CreateServiceWorker(mRegistration->mPrincipal,
-                                  mRegistration->mScriptSpec,
-                                  mRegistration->mScope,
+                                  dummyInfo,
                                   getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -591,7 +640,7 @@ public:
     if (NS_WARN_IF(!ok)) {
       swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
       Fail(NS_ERROR_DOM_ABORT_ERR);
-      return rv;
+      return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
@@ -639,8 +688,7 @@ public:
     nsRefPtr serviceWorker;
     nsresult rv =
       swm->CreateServiceWorker(mRegistration->mPrincipal,
-                               mRegistration->mInstallingWorker->ScriptSpec(),
-                               mRegistration->mScope,
+                               mRegistration->mInstallingWorker,
                                getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -776,13 +824,8 @@ private:
       mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     }
 
-    // Although the spec first sets waiting worker and then updates its state,
-    // our ServiceWorkerInfo does not hold a list of associated ServiceWorker
-    // objects in content JS. This means if we want to fire an event on
-    // ServiceWorkerRegistration.installing, we need to do it first, before
-    // swapping it with waiting worker.
-    mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installed);
     mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
+    mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
 
@@ -818,8 +861,8 @@ ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmed
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
-                               const nsAString& aScope,
-                               const nsAString& aScriptURL,
+                               nsIURI* aScopeURI,
+                               nsIURI* aScriptURI,
                                nsISupports** aPromise)
 {
   AssertIsOnMainThread();
@@ -889,41 +932,29 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
     }
   }
 
-  nsCOMPtr scriptURI;
-  rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, documentURI);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
   // Data URLs are not allowed.
   nsCOMPtr documentPrincipal = doc->NodePrincipal();
 
-  rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
+  rv = documentPrincipal->CheckMayLoad(aScriptURI, true /* report */,
                                        false /* allowIfInheritsPrincipal */);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsCOMPtr scopeURI;
-  rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, documentURI);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
+  rv = documentPrincipal->CheckMayLoad(aScopeURI, true /* report */,
                                        false /* allowIfInheritsPrinciple */);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCString cleanedScope;
-  rv = scopeURI->GetSpecIgnoringRef(cleanedScope);
+  rv = aScopeURI->GetSpecIgnoringRef(cleanedScope);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoCString spec;
-  rv = scriptURI->GetSpec(spec);
+  rv = aScriptURI->GetSpec(spec);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -1129,7 +1160,6 @@ ServiceWorkerRegistrationInfo::Activate()
   // FIXME(nsm): Unlink appcache if there is one.
 
   swm->CheckPendingReadyPromises();
-  swm->StoreRegistration(mPrincipal, this);
 
   // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr controllerChangeRunnable =
@@ -1142,8 +1172,7 @@ ServiceWorkerRegistrationInfo::Activate()
   nsRefPtr serviceWorker;
   nsresult rv =
     swm->CreateServiceWorker(mPrincipal,
-                             mActiveWorker->ScriptSpec(),
-                             mScope,
+                             mActiveWorker,
                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     nsCOMPtr r =
@@ -1265,7 +1294,7 @@ ServiceWorkerManager::GetRegistrations(nsIDOMWindow* aWindow,
     return result.ErrorCode();
   }
 
-  nsRefPtr runnable =
+  nsCOMPtr runnable =
     new GetRegistrationsRunnable(window, promise);
   promise.forget(aPromise);
   return NS_DispatchToCurrentThread(runnable);
@@ -1366,7 +1395,7 @@ ServiceWorkerManager::GetRegistration(nsIDOMWindow* aWindow,
     return result.ErrorCode();
   }
 
-  nsRefPtr runnable =
+  nsCOMPtr runnable =
     new GetRegistrationRunnable(window, promise, aDocumentURL);
   promise.forget(aPromise);
   return NS_DispatchToCurrentThread(runnable);
@@ -1432,7 +1461,7 @@ ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
     return result.ErrorCode();
   }
 
-  nsRefPtr runnable =
+  nsCOMPtr runnable =
     new GetReadyPromiseRunnable(window, promise);
   promise.forget(aPromise);
   return NS_DispatchToCurrentThread(runnable);
@@ -1692,60 +1721,24 @@ ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
 
   if (aSuccess) {
     mActiveWorker->UpdateState(ServiceWorkerState::Activated);
+    nsRefPtr swm = ServiceWorkerManager::GetInstance();
+    swm->StoreRegistration(mPrincipal, this);
   } else {
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker = nullptr;
   }
 }
 
-void
-ServiceWorkerRegistrationInfo::QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
-                                                     ServiceWorkerState aState) const
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aInfo);
-  MOZ_ASSERT(aInfo == mInstallingWorker ||
-             aInfo == mWaitingWorker ||
-             aInfo == mActiveWorker);
-
-  nsRefPtr swm = ServiceWorkerManager::GetInstance();
-  WhichServiceWorker whichOne;
-  if (aInfo == mInstallingWorker) {
-    whichOne = WhichServiceWorker::INSTALLING_WORKER;
-  } else if (aInfo == mWaitingWorker) {
-    whichOne = WhichServiceWorker::WAITING_WORKER;
-  } else if (aInfo == mActiveWorker) {
-    whichOne = WhichServiceWorker::ACTIVE_WORKER;
-  } else {
-    MOZ_CRASH("Hit unexpected case");
-  }
-
-  // Refactor this iteration pattern across this and 2 other call-sites.
-  nsTObserverArray::ForwardIterator it(swm->mServiceWorkerRegistrations);
-  while (it.HasMore()) {
-    nsRefPtr target = it.GetNext();
-    nsAutoString regScope;
-    target->GetScope(regScope);
-    MOZ_ASSERT(!regScope.IsEmpty());
-
-    NS_ConvertUTF16toUTF8 utf8Scope(regScope);
-    if (utf8Scope.Equals(mScope)) {
-      target->QueueStateChangeEvent(whichOne, aState);
-    }
-  }
-}
-
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
-                                                   const nsACString& aScriptSpec,
-                                                   const nsACString& aScope,
+                                                   ServiceWorkerInfo* aInfo,
                                                    ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
 
   RuntimeService* rs = RuntimeService::GetOrCreateService();
-  nsRefPtr serviceWorker;
+  nsRefPtr sharedWorker;
 
   AutoJSAPI jsapi;
   jsapi.Init(aWindow);
@@ -1754,15 +1747,18 @@ ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
   nsCOMPtr sgo = do_QueryInterface(aWindow);
   JS::Rooted jsGlobal(cx, sgo->GetGlobalJSObject());
   GlobalObject global(cx, jsGlobal);
-  nsresult rv = rs->CreateServiceWorker(global,
-                                        NS_ConvertUTF8toUTF16(aScriptSpec),
-                                        aScope,
-                                        getter_AddRefs(serviceWorker));
+  nsresult rv = rs->CreateSharedWorkerForServiceWorker(global,
+                                                       NS_ConvertUTF8toUTF16(aInfo->ScriptSpec()),
+                                                       aInfo->Scope(),
+                                                       getter_AddRefs(sharedWorker));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  nsRefPtr serviceWorker =
+    new ServiceWorker(aWindow, aInfo, sharedWorker);
+
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
@@ -1785,8 +1781,12 @@ ServiceWorkerManager::LoadRegistrations(
 
     registration->mScriptSpec = aRegistrations[i].scriptSpec();
 
-    registration->mActiveWorker =
-      new ServiceWorkerInfo(registration, aRegistrations[i].currentWorkerURL());
+    const nsCString& currentWorkerURL = aRegistrations[i].currentWorkerURL();
+    if (!currentWorkerURL.IsEmpty()) {
+      registration->mActiveWorker =
+        new ServiceWorkerInfo(registration, currentWorkerURL);
+      registration->mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
+    }
   }
 }
 
@@ -2059,7 +2059,7 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow,
   AssertIsOnMainThread();
 
   nsCOMPtr window = do_QueryInterface(aWindow);
-  if (!window) {
+  if (NS_WARN_IF(!window)) {
     return NS_ERROR_FAILURE;
   }
 
@@ -2086,7 +2086,7 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow,
   ////////////////////////////////////////////
 
   nsRefPtr registration = GetRegistration(scope);
-  if (!registration) {
+  if (NS_WARN_IF(!registration)) {
     return NS_ERROR_FAILURE;
   }
 
@@ -2101,14 +2101,13 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow,
     MOZ_CRASH("Invalid worker type");
   }
 
-  if (!info) {
+  if (NS_WARN_IF(!info)) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
   nsRefPtr serviceWorker;
   rv = CreateServiceWorkerForWindow(window,
-                                    info->ScriptSpec(),
-                                    registration->mScope,
+                                    info,
                                     getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -2133,11 +2132,13 @@ public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      nsMainThreadPtrHandle& aChannel,
                      nsMainThreadPtrHandle& aServiceWorker,
-                     nsAutoPtr& aClientInfo)
+                     nsAutoPtr& aClientInfo,
+                     bool aIsReload)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
     , mInterceptedChannel(aChannel)
     , mServiceWorker(aServiceWorker)
     , mClientInfo(aClientInfo)
+    , mIsReload(aIsReload)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
@@ -2176,9 +2177,6 @@ public:
     rv = channel->GetLoadFlags(&loadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    //TODO(jdm): we should probably include reload-ness in the loadinfo or as a separate load flag
-    mIsReload = false;
-
     rv = httpChannel->VisitRequestHeaders(this);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2278,7 +2276,8 @@ private:
 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
 
 NS_IMETHODIMP
-ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChannel* aChannel)
+ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChannel* aChannel,
+                                         bool aIsReload)
 {
   MOZ_ASSERT(aChannel);
   nsCOMPtr serviceWorker;
@@ -2310,8 +2309,7 @@ ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChanne
 
     nsRefPtr sw;
     rv = CreateServiceWorker(registration->mPrincipal,
-                             registration->mActiveWorker->ScriptSpec(),
-                             registration->mScope,
+                             registration->mActiveWorker,
                              getter_AddRefs(sw));
     serviceWorker = sw.forget();
   }
@@ -2329,7 +2327,7 @@ ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChanne
 
   // clientInfo is null if we don't have a controlled document
   nsRefPtr event =
-    new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, clientInfo);
+    new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, clientInfo, aIsReload);
   rv = event->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2406,8 +2404,7 @@ ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports**
 
   nsRefPtr serviceWorker;
   rv = CreateServiceWorkerForWindow(window,
-                                    registration->mActiveWorker->ScriptSpec(),
-                                    registration->mScope,
+                                    registration->mActiveWorker,
                                     getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -2449,15 +2446,15 @@ ServiceWorkerManager::GetActive(nsIDOMWindow* aWindow,
 
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
-                                          const nsACString& aScriptSpec,
-                                          const nsACString& aScope,
+                                          ServiceWorkerInfo* aInfo,
                                           ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
 
-  WorkerPrivate::LoadInfo info;
-  nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aScriptSpec, nullptr, nullptr);
+  WorkerLoadInfo info;
+  nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aInfo->ScriptSpec(),
+                          nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -2479,7 +2476,6 @@ ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
   // them here.
   WorkerPrivate::OverrideLoadInfoLoadGroup(info);
 
-  nsRefPtr serviceWorker;
   RuntimeService* rs = RuntimeService::GetOrCreateService();
   if (!rs) {
     return NS_ERROR_FAILURE;
@@ -2487,15 +2483,19 @@ ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
 
   AutoJSAPI jsapi;
   jsapi.Init();
-  rv = rs->CreateServiceWorkerFromLoadInfo(jsapi.cx(), &info,
-                                           NS_ConvertUTF8toUTF16(aScriptSpec),
-                                           aScope,
-                                           getter_AddRefs(serviceWorker));
+  nsRefPtr 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 =
+    new ServiceWorker(nullptr, aInfo, sharedWorker);
+
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
@@ -2681,4 +2681,86 @@ ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRe
   }
 }
 
+void
+ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
+{
+  MOZ_ASSERT(aRegistration);
+  MOZ_ASSERT(!aRegistration->IsControllingDocuments());
+  MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
+  ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
+
+  // Hold a ref since the hashtable may be the last ref.
+  nsRefPtr reg;
+  mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope,
+                                         getter_AddRefs(reg));
+  MOZ_ASSERT(reg);
+
+  // All callers should be either from a job in which case the actor is
+  // available, or from MaybeStopControlling(), in which case, this will only be
+  // called if a valid registration is found. If a valid registration exists,
+  // it means the actor is available since the original map of registrations is
+  // populated by it, and any new registrations wait until the actor is
+  // available before proceeding (See ServiceWorkerRegisterJob::Start).
+  MOZ_ASSERT(mActor);
+
+  PrincipalInfo principalInfo;
+  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(reg->mPrincipal,
+                                                    &principalInfo)))) {
+    //XXXnsm I can't think of any other reason a stored principal would fail to
+    //convert.
+    NS_WARNING("Unable to unregister serviceworker due to possible OOM");
+    return;
+  }
+  mActor->SendUnregisterServiceWorker(principalInfo, NS_ConvertUTF8toUTF16(reg->mScope));
+}
+
+void
+ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
+{
+  MOZ_ASSERT(aWorker);
+#ifdef DEBUG
+  nsAutoString workerURL;
+  aWorker->GetScriptURL(workerURL);
+  MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
+#endif
+  MOZ_ASSERT(!mInstances.Contains(aWorker));
+
+  mInstances.AppendElement(aWorker);
+  aWorker->SetState(State());
+}
+
+void
+ServiceWorkerInfo::RemoveWorker(ServiceWorker* aWorker)
+{
+  MOZ_ASSERT(aWorker);
+#ifdef DEBUG
+  nsAutoString workerURL;
+  aWorker->GetScriptURL(workerURL);
+  MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
+#endif
+  MOZ_ASSERT(mInstances.Contains(aWorker));
+
+  mInstances.RemoveElement(aWorker);
+}
+
+void
+ServiceWorkerInfo::UpdateState(ServiceWorkerState aState)
+{
+#ifdef DEBUG
+  // Any state can directly transition to redundant, but everything else is
+  // ordered.
+  if (aState != ServiceWorkerState::Redundant) {
+    MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
+    MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
+    MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
+    MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
+  }
+  // Activated can only go to redundant.
+  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);
+  }
+}
 END_WORKERS_NAMESPACE
diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
index 88917bdf9f..701dc2db18 100644
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -202,9 +202,6 @@ public:
   void
   FinishActivate(bool aSuccess);
 
-  void
-  QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
-                        ServiceWorkerState aState) const;
 };
 
 /*
@@ -219,6 +216,11 @@ private:
   const ServiceWorkerRegistrationInfo* mRegistration;
   nsCString mScriptSpec;
   ServiceWorkerState mState;
+  // 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 mInstances;
 
   ~ServiceWorkerInfo()
   { }
@@ -232,6 +234,12 @@ public:
     return mScriptSpec;
   }
 
+  const nsCString&
+  Scope() const
+  {
+    return mRegistration->mScope;
+  }
+
   void SetScriptSpec(const nsCString& aSpec)
   {
     MOZ_ASSERT(!aSpec.IsEmpty());
@@ -254,23 +262,20 @@ public:
   }
 
   void
-  UpdateState(ServiceWorkerState aState)
+  UpdateState(ServiceWorkerState aState);
+
+  // Only used to set initial state when loading from disk!
+  void
+  SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
   {
-#ifdef DEBUG
-    // Any state can directly transition to redundant, but everything else is
-    // ordered.
-    if (aState != ServiceWorkerState::Redundant) {
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
-    }
-    // Activated can only go to redundant.
-    MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
-#endif
     mState = aState;
-    mRegistration->QueueStateChangeEvent(this, mState);
   }
+
+  void
+  AppendWorker(ServiceWorker* aWorker);
+
+  void
+  RemoveWorker(ServiceWorker* aWorker);
 };
 
 #define NS_SERVICEWORKERMANAGER_IMPL_IID                 \
@@ -346,14 +351,7 @@ public:
   CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal);
 
   void
-  RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
-  {
-    MOZ_ASSERT(aRegistration);
-    MOZ_ASSERT(!aRegistration->IsControllingDocuments());
-    MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
-    ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
-    mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope);
-  }
+  RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
 
   ServiceWorkerJobQueue*
   GetOrCreateJobQueue(const nsCString& aScope)
@@ -405,14 +403,12 @@ private:
 
   NS_IMETHOD
   CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
-                               const nsACString& aScriptSpec,
-                               const nsACString& aScope,
+                               ServiceWorkerInfo* aInfo,
                                ServiceWorker** aServiceWorker);
 
   NS_IMETHOD
   CreateServiceWorker(nsIPrincipal* aPrincipal,
-                      const nsACString& aScriptSpec,
-                      const nsACString& aScope,
+                      ServiceWorkerInfo* aInfo,
                       ServiceWorker** aServiceWorker);
 
   NS_IMETHODIMP
diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp
index 513a5dc8f7..063dc86144 100644
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -265,28 +265,6 @@ ServiceWorkerRegistration::InvalidateWorkerReference(WhichServiceWorker aWhichOn
   }
 }
 
-void
-ServiceWorkerRegistration::QueueStateChangeEvent(WhichServiceWorker aWhichOne,
-                                                 ServiceWorkerState aState) const
-{
-  nsRefPtr worker;
-  if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) {
-    worker = mInstallingWorker;
-  } else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) {
-    worker = mWaitingWorker;
-  } else if (aWhichOne == WhichServiceWorker::ACTIVE_WORKER) {
-    worker = mActiveWorker;
-  } else {
-    MOZ_CRASH("Invalid case");
-  }
-
-  if (worker) {
-    worker->SetState(aState);
-    nsCOMPtr r = NS_NewRunnableMethod(worker, &ServiceWorker::DispatchStateChange);
-    NS_DispatchToMainThread(r);
-  }
-}
-
 // XXXnsm, maybe this can be optimized to only add when a event handler is
 // registered.
 void
diff --git a/dom/workers/ServiceWorkerRegistration.h b/dom/workers/ServiceWorkerRegistration.h
index 4644b449fe..ea95784d62 100644
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -59,9 +59,6 @@ public:
   void
   InvalidateWorkerReference(WhichServiceWorker aWhichOnes);
 
-  void
-  QueueStateChangeEvent(WhichServiceWorker aWhichOne, ServiceWorkerState aState) const;
-
   // DOMEventTargethelper
   virtual void DisconnectFromOwner() override;
 
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index 160955faaa..8827e87740 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1143,6 +1143,9 @@ class MessageEventRunnable final : public WorkerRunnable
   uint64_t mMessagePortSerial;
   bool mToMessagePort;
 
+  // This is only used for messages dispatched to a service worker.
+  nsAutoPtr mEventSource;
+
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior,
@@ -1157,6 +1160,12 @@ public:
     mClonedObjects.SwapElements(aClonedObjects);
   }
 
+  void
+  SetMessageSource(ServiceWorkerClientInfo* aSource)
+  {
+    mEventSource = aSource;
+  }
+
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
@@ -1182,6 +1191,12 @@ public:
                               EmptyString(),
                               EmptyString(),
                               nullptr);
+    if (mEventSource) {
+      nsRefPtr client =
+        new ServiceWorkerWindowClient(aTarget, *mEventSource);
+      event->SetSource(client);
+    }
+
     if (NS_FAILED(rv)) {
       xpc::Throw(aCx, rv);
       return false;
@@ -2091,9 +2106,7 @@ NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
 
-template 
-WorkerPrivateParent::
-LoadInfo::LoadInfo()
+WorkerLoadInfo::WorkerLoadInfo()
   : mWindowID(UINT64_MAX)
   , mFromWindow(false)
   , mEvalAllowed(false)
@@ -2104,15 +2117,114 @@ LoadInfo::LoadInfo()
   , mIsInCertifiedApp(false)
   , mIndexedDBAllowed(false)
 {
-  MOZ_COUNT_CTOR(WorkerPrivateParent::LoadInfo);
+  MOZ_COUNT_CTOR(WorkerLoadInfo);
+}
+
+WorkerLoadInfo::~WorkerLoadInfo()
+{
+  MOZ_COUNT_DTOR(WorkerLoadInfo);
+}
+
+void
+WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
+{
+  MOZ_ASSERT(!mBaseURI);
+  aOther.mBaseURI.swap(mBaseURI);
+
+  MOZ_ASSERT(!mResolvedScriptURI);
+  aOther.mResolvedScriptURI.swap(mResolvedScriptURI);
+
+  MOZ_ASSERT(!mPrincipal);
+  aOther.mPrincipal.swap(mPrincipal);
+
+  MOZ_ASSERT(!mScriptContext);
+  aOther.mScriptContext.swap(mScriptContext);
+
+  MOZ_ASSERT(!mWindow);
+  aOther.mWindow.swap(mWindow);
+
+  MOZ_ASSERT(!mCSP);
+  aOther.mCSP.swap(mCSP);
+
+  MOZ_ASSERT(!mChannel);
+  aOther.mChannel.swap(mChannel);
+
+  MOZ_ASSERT(!mLoadGroup);
+  aOther.mLoadGroup.swap(mLoadGroup);
+
+  MOZ_ASSERT(!mInterfaceRequestor);
+  aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
+
+  MOZ_ASSERT(!mPrincipalInfo);
+  mPrincipalInfo = aOther.mPrincipalInfo.forget();
+
+  mDomain = aOther.mDomain;
+  mWindowID = aOther.mWindowID;
+  mFromWindow = aOther.mFromWindow;
+  mEvalAllowed = aOther.mEvalAllowed;
+  mReportCSPViolations = aOther.mReportCSPViolations;
+  mXHRParamsAllowed = aOther.mXHRParamsAllowed;
+  mPrincipalIsSystem = aOther.mPrincipalIsSystem;
+  mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
+  mIsInCertifiedApp = aOther.mIsInCertifiedApp;
+  mIndexedDBAllowed = aOther.mIndexedDBAllowed;
 }
 
 template 
-WorkerPrivateParent::
-LoadInfo::~LoadInfo()
+class WorkerPrivateParent::EventTarget MOZ_FINAL
+  : public nsIEventTarget
 {
-  MOZ_COUNT_DTOR(WorkerPrivateParent::LoadInfo);
-}
+  // This mutex protects mWorkerPrivate and must be acquired *before* the
+  // WorkerPrivate's mutex whenever they must both be held.
+  mozilla::Mutex mMutex;
+  WorkerPrivate* mWorkerPrivate;
+  nsIEventTarget* mWeakNestedEventTarget;
+  nsCOMPtr mNestedEventTarget;
+
+public:
+  explicit EventTarget(WorkerPrivate* aWorkerPrivate)
+  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
+    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+  }
+
+  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
+  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
+    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
+    mNestedEventTarget(aNestedEventTarget)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aNestedEventTarget);
+  }
+
+  void
+  Disable()
+  {
+    nsCOMPtr nestedEventTarget;
+    {
+      MutexAutoLock lock(mMutex);
+
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate = nullptr;
+      mNestedEventTarget.swap(nestedEventTarget);
+    }
+  }
+
+  nsIEventTarget*
+  GetWeakNestedEventTarget() const
+  {
+    MOZ_ASSERT(mWeakNestedEventTarget);
+    return mWeakNestedEventTarget;
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIEVENTTARGET
+
+private:
+  ~EventTarget()
+  { }
+};
 
 template 
 class WorkerPrivateParent::SynchronizeAndResumeRunnable final
@@ -2170,184 +2282,110 @@ private:
   }
 };
 
-template 
-class WorkerPrivateParent::InterfaceRequestor final
-  : public nsIInterfaceRequestor
+WorkerLoadInfo::
+InterfaceRequestor::InterfaceRequestor(nsIPrincipal* aPrincipal,
+                                       nsILoadGroup* aLoadGroup)
 {
-  NS_DECL_ISUPPORTS
-
-public:
-  InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(aPrincipal);
-
-    // Look for an existing LoadContext.  This is optional and it's ok if
-    // we don't find one.
-    nsCOMPtr baseContext;
-    if (aLoadGroup) {
-      nsCOMPtr callbacks;
-      aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
-      if (callbacks) {
-        callbacks->GetInterface(NS_GET_IID(nsILoadContext),
-                                getter_AddRefs(baseContext));
-      }
-    }
-
-    mLoadContext = new LoadContext(aPrincipal, baseContext);
-  }
-
-  void
-  MaybeAddTabChild(nsILoadGroup* aLoadGroup)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (!aLoadGroup) {
-      return;
-    }
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
 
+  // Look for an existing LoadContext.  This is optional and it's ok if
+  // we don't find one.
+  nsCOMPtr baseContext;
+  if (aLoadGroup) {
     nsCOMPtr callbacks;
     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
-    if (!callbacks) {
-      return;
+    if (callbacks) {
+      callbacks->GetInterface(NS_GET_IID(nsILoadContext),
+                              getter_AddRefs(baseContext));
     }
-
-    nsCOMPtr tabChild;
-    callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild));
-    if (!tabChild) {
-      return;
-    }
-
-    // Use weak references to the tab child.  Holding a strong reference will
-    // not prevent an ActorDestroy() from being called on the TabChild.
-    // Therefore, we should let the TabChild destroy itself as soon as possible.
-    mTabChildList.AppendElement(do_GetWeakReference(tabChild));
   }
 
-  NS_IMETHOD
-  GetInterface(const nsIID& aIID, void** aSink) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(mLoadContext);
+  mLoadContext = new LoadContext(aPrincipal, baseContext);
+}
 
-    if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
-      nsCOMPtr ref = mLoadContext;
-      ref.forget(aSink);
-      return NS_OK;
-    }
-
-    // If we still have an active nsITabChild, then return it.  Its possible,
-    // though, that all of the TabChild objects have been destroyed.  In that
-    // case we return NS_NOINTERFACE.
-    if(aIID.Equals(NS_GET_IID(nsITabChild))) {
-      nsCOMPtr tabChild = GetAnyLiveTabChild();
-      if (!tabChild) {
-        return NS_NOINTERFACE;
-      }
-      tabChild.forget(aSink);
-      return NS_OK;
-    }
-
-    return NS_NOINTERFACE;
-  }
-
-private:
-  ~InterfaceRequestor() { }
-
-  already_AddRefed
-  GetAnyLiveTabChild()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // Search our list of known TabChild objects for one that still exists.
-    while (!mTabChildList.IsEmpty()) {
-      nsCOMPtr tabChild =
-        do_QueryReferent(mTabChildList.LastElement());
-
-      // Does this tab child still exist?  If so, return it.  We are done.
-      if (tabChild) {
-        return tabChild.forget();
-      }
-
-      // Otherwise remove the stale weak reference and check the next one
-      mTabChildList.RemoveElementAt(mTabChildList.Length() - 1);
-    }
-
-    return nullptr;
-  }
-
-  nsCOMPtr mLoadContext;
-
-  // Array of weak references to nsITabChild.  We do not want to keep TabChild
-  // actors alive for long after their ActorDestroy() methods are called.
-  nsTArray mTabChildList;
-};
-
-template 
-NS_IMPL_ADDREF(WorkerPrivateParent::InterfaceRequestor)
-
-template 
-NS_IMPL_RELEASE(WorkerPrivateParent::InterfaceRequestor)
-
-template 
-NS_IMPL_QUERY_INTERFACE(WorkerPrivateParent::InterfaceRequestor,
-                        nsIInterfaceRequestor)
-
-template 
-class WorkerPrivateParent::EventTarget final
-  : public nsIEventTarget
+void
+WorkerLoadInfo::
+InterfaceRequestor::MaybeAddTabChild(nsILoadGroup* aLoadGroup)
 {
-  // This mutex protects mWorkerPrivate and must be acquired *before* the
-  // WorkerPrivate's mutex whenever they must both be held.
-  mozilla::Mutex mMutex;
-  WorkerPrivate* mWorkerPrivate;
-  nsIEventTarget* mWeakNestedEventTarget;
-  nsCOMPtr mNestedEventTarget;
+  MOZ_ASSERT(NS_IsMainThread());
 
-public:
-  explicit EventTarget(WorkerPrivate* aWorkerPrivate)
-  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
-    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
+  if (!aLoadGroup) {
+    return;
   }
 
-  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
-  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
-    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
-    mNestedEventTarget(aNestedEventTarget)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aNestedEventTarget);
+  nsCOMPtr callbacks;
+  aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  if (!callbacks) {
+    return;
   }
 
-  void
-  Disable()
-  {
-    nsCOMPtr nestedEventTarget;
-    {
-      MutexAutoLock lock(mMutex);
+  nsCOMPtr tabChild;
+  callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild));
+  if (!tabChild) {
+    return;
+  }
 
-      MOZ_ASSERT(mWorkerPrivate);
-      mWorkerPrivate = nullptr;
-      mNestedEventTarget.swap(nestedEventTarget);
+  // Use weak references to the tab child.  Holding a strong reference will
+  // not prevent an ActorDestroy() from being called on the TabChild.
+  // Therefore, we should let the TabChild destroy itself as soon as possible.
+  mTabChildList.AppendElement(do_GetWeakReference(tabChild));
+}
+
+NS_IMETHODIMP
+WorkerLoadInfo::
+InterfaceRequestor::GetInterface(const nsIID& aIID, void** aSink)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mLoadContext);
+
+  if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
+    nsCOMPtr ref = mLoadContext;
+    ref.forget(aSink);
+    return NS_OK;
+  }
+
+  // If we still have an active nsITabChild, then return it.  Its possible,
+  // though, that all of the TabChild objects have been destroyed.  In that
+  // case we return NS_NOINTERFACE.
+  if (aIID.Equals(NS_GET_IID(nsITabChild))) {
+    nsCOMPtr tabChild = GetAnyLiveTabChild();
+    if (!tabChild) {
+      return NS_NOINTERFACE;
     }
+    tabChild.forget(aSink);
+    return NS_OK;
   }
 
-  nsIEventTarget*
-  GetWeakNestedEventTarget() const
-  {
-    MOZ_ASSERT(mWeakNestedEventTarget);
-    return mWeakNestedEventTarget;
+  return NS_NOINTERFACE;
+}
+
+already_AddRefed
+WorkerLoadInfo::
+InterfaceRequestor::GetAnyLiveTabChild()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Search our list of known TabChild objects for one that still exists.
+  while (!mTabChildList.IsEmpty()) {
+    nsCOMPtr tabChild =
+      do_QueryReferent(mTabChildList.LastElement());
+
+    // Does this tab child still exist?  If so, return it.  We are done.
+    if (tabChild) {
+      return tabChild.forget();
+    }
+
+    // Otherwise remove the stale weak reference and check the next one
+    mTabChildList.RemoveElementAt(mTabChildList.Length() - 1);
   }
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIEVENTTARGET
+  return nullptr;
+}
 
-private:
-  ~EventTarget()
-  { }
-};
+NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
+NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
+NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor, nsIInterfaceRequestor)
 
 struct WorkerPrivate::TimeoutInfo
 {
@@ -2551,7 +2589,7 @@ WorkerPrivateParent::WorkerPrivateParent(
                                            bool aIsChromeWorker,
                                            WorkerType aWorkerType,
                                            const nsACString& aSharedWorkerName,
-                                           LoadInfo& aLoadInfo)
+                                           WorkerLoadInfo& aLoadInfo)
 : mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
@@ -3193,9 +3231,10 @@ void
 WorkerPrivateParent::PostMessageInternal(
                                             JSContext* aCx,
                                             JS::Handle aMessage,
-                                            const Optional >& aTransferable,
+                                            const Optional>& aTransferable,
                                             bool aToMessagePort,
                                             uint64_t aMessagePortSerial,
+                                            ServiceWorkerClientInfo* aClientInfo,
                                             ErrorResult& aRv)
 {
   AssertIsOnParentThread();
@@ -3259,11 +3298,26 @@ WorkerPrivateParent::PostMessageInternal(
                              WorkerRunnable::WorkerThreadModifyBusyCount,
                              Move(buffer), clonedObjects, aToMessagePort,
                              aMessagePortSerial);
+  runnable->SetMessageSource(aClientInfo);
+
   if (!runnable->Dispatch(aCx)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
+template 
+void
+WorkerPrivateParent::PostMessageToServiceWorker(
+                             JSContext* aCx, JS::Handle aMessage,
+                             const Optional>& aTransferable,
+                             nsAutoPtr& aClientInfo,
+                             ErrorResult& aRv)
+{
+  AssertIsOnMainThread();
+  PostMessageInternal(aCx, aMessage, aTransferable, false, 0,
+                      aClientInfo.forget(), aRv);
+}
+
 template 
 void
 WorkerPrivateParent::PostMessageToMessagePort(
@@ -3276,7 +3330,7 @@ WorkerPrivateParent::PostMessageToMessagePort(
   AssertIsOnMainThread();
 
   PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
-                      aRv);
+                      nullptr, aRv);
 }
 
 template 
@@ -4264,7 +4318,7 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
                              const nsACString& aSharedWorkerName,
-                             LoadInfo& aLoadInfo)
+                             WorkerLoadInfo& aLoadInfo)
   : WorkerPrivateParent(aCx, aParent, aScriptURL,
                                        aIsChromeWorker, aWorkerType,
                                        aSharedWorkerName, aLoadInfo)
@@ -4366,7 +4420,7 @@ WorkerPrivate::Constructor(const GlobalObject& aGlobal,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
                            const nsACString& aSharedWorkerName,
-                           LoadInfo* aLoadInfo, ErrorResult& aRv)
+                           WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
   return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
@@ -4379,7 +4433,7 @@ WorkerPrivate::Constructor(JSContext* aCx,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
                            const nsACString& aSharedWorkerName,
-                           LoadInfo* aLoadInfo, ErrorResult& aRv)
+                           WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
   WorkerPrivate* parent = NS_IsMainThread() ?
                           nullptr :
@@ -4395,7 +4449,7 @@ WorkerPrivate::Constructor(JSContext* aCx,
   MOZ_ASSERT_IF(aWorkerType == WorkerTypeDedicated,
                 aSharedWorkerName.IsEmpty());
 
-  Maybe stackLoadInfo;
+  Maybe stackLoadInfo;
   if (!aLoadInfo) {
     stackLoadInfo.emplace();
 
@@ -4458,7 +4512,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
                            WorkerPrivate* aParent, const nsAString& aScriptURL,
                            bool aIsChromeWorker,
                            LoadGroupBehavior aLoadGroupBehavior,
-                           LoadInfo* aLoadInfo)
+                           WorkerLoadInfo* aLoadInfo)
 {
   using namespace mozilla::dom::workers::scriptloader;
   using mozilla::dom::indexedDB::IDBFactory;
@@ -4470,7 +4524,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
     AssertIsOnMainThread();
   }
 
-  LoadInfo loadInfo;
+  WorkerLoadInfo loadInfo;
   nsresult rv;
 
   if (aParent) {
@@ -4714,12 +4768,13 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
 
 // static
 void
-WorkerPrivate::OverrideLoadInfoLoadGroup(LoadInfo& aLoadInfo)
+WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo)
 {
   MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
 
-  aLoadInfo.mInterfaceRequestor = new InterfaceRequestor(aLoadInfo.mPrincipal,
-                                                         aLoadInfo.mLoadGroup);
+  aLoadInfo.mInterfaceRequestor =
+    new WorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,
+                                           aLoadInfo.mLoadGroup);
   aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
 
   nsCOMPtr loadGroup =
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
index 77b17e11ed..8cccca6c9b 100644
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -61,6 +61,7 @@ BEGIN_WORKERS_NAMESPACE
 class AutoSyncLoopHolder;
 class MessagePort;
 class SharedWorker;
+class ServiceWorkerClientInfo;
 class WorkerControlRunnable;
 class WorkerDebugger;
 class WorkerDebuggerGlobalScope;
@@ -69,15 +70,6 @@ class WorkerPrivate;
 class WorkerRunnable;
 class WorkerThread;
 
-// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to
-// be updated too.
-enum WorkerType
-{
-  WorkerTypeDedicated,
-  WorkerTypeShared,
-  WorkerTypeService
-};
-
 // SharedMutex is a small wrapper around an (internal) reference-counted Mutex
 // object. It exists to avoid changing a lot of code to use Mutex* instead of
 // Mutex&.
@@ -133,7 +125,6 @@ class WorkerPrivateParent : public DOMEventTargetHelper
   class SynchronizeAndResumeRunnable;
 
 protected:
-  class InterfaceRequestor;
   class EventTarget;
   friend class EventTarget;
 
@@ -153,84 +144,6 @@ public:
     nsString mOrigin;
   };
 
-  struct LoadInfo
-  {
-    // All of these should be released in ForgetMainThreadObjects.
-    nsCOMPtr mBaseURI;
-    nsCOMPtr mResolvedScriptURI;
-    nsCOMPtr mPrincipal;
-    nsCOMPtr mScriptContext;
-    nsCOMPtr mWindow;
-    nsCOMPtr mCSP;
-    nsCOMPtr mChannel;
-    nsCOMPtr mLoadGroup;
-
-    // Only set if we have a custom overriden load group
-    nsRefPtr mInterfaceRequestor;
-
-    nsAutoPtr mPrincipalInfo;
-    nsCString mDomain;
-
-    uint64_t mWindowID;
-
-    bool mFromWindow;
-    bool mEvalAllowed;
-    bool mReportCSPViolations;
-    bool mXHRParamsAllowed;
-    bool mPrincipalIsSystem;
-    bool mIsInPrivilegedApp;
-    bool mIsInCertifiedApp;
-    bool mIndexedDBAllowed;
-
-    LoadInfo();
-    ~LoadInfo();
-
-    void
-    StealFrom(LoadInfo& aOther)
-    {
-      MOZ_ASSERT(!mBaseURI);
-      aOther.mBaseURI.swap(mBaseURI);
-
-      MOZ_ASSERT(!mResolvedScriptURI);
-      aOther.mResolvedScriptURI.swap(mResolvedScriptURI);
-
-      MOZ_ASSERT(!mPrincipal);
-      aOther.mPrincipal.swap(mPrincipal);
-
-      MOZ_ASSERT(!mScriptContext);
-      aOther.mScriptContext.swap(mScriptContext);
-
-      MOZ_ASSERT(!mWindow);
-      aOther.mWindow.swap(mWindow);
-
-      MOZ_ASSERT(!mCSP);
-      aOther.mCSP.swap(mCSP);
-
-      MOZ_ASSERT(!mChannel);
-      aOther.mChannel.swap(mChannel);
-
-      MOZ_ASSERT(!mLoadGroup);
-      aOther.mLoadGroup.swap(mLoadGroup);
-
-      MOZ_ASSERT(!mInterfaceRequestor);
-      aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
-
-      MOZ_ASSERT(!mPrincipalInfo);
-      mPrincipalInfo = aOther.mPrincipalInfo.forget();
-
-      mDomain = aOther.mDomain;
-      mWindowID = aOther.mWindowID;
-      mFromWindow = aOther.mFromWindow;
-      mEvalAllowed = aOther.mEvalAllowed;
-      mReportCSPViolations = aOther.mReportCSPViolations;
-      mXHRParamsAllowed = aOther.mXHRParamsAllowed;
-      mPrincipalIsSystem = aOther.mPrincipalIsSystem;
-      mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
-      mIsInCertifiedApp = aOther.mIsInCertifiedApp;
-      mIndexedDBAllowed = aOther.mIndexedDBAllowed;
-    }
-  };
-
 protected:
   typedef mozilla::ErrorResult ErrorResult;
 
@@ -249,7 +162,7 @@ private:
   LocationInfo mLocationInfo;
   // The lifetime of these objects within LoadInfo is managed explicitly;
   // they do not need to be cycle collected.
-  LoadInfo mLoadInfo;
+  WorkerLoadInfo mLoadInfo;
 
   // Only used for top level workers.
   nsTArray> mQueuedRunnables;
@@ -285,7 +198,7 @@ protected:
                       const nsAString& aScriptURL, bool aIsChromeWorker,
                       WorkerType aWorkerType,
                       const nsACString& aSharedWorkerName,
-                      LoadInfo& aLoadInfo);
+                      WorkerLoadInfo& aLoadInfo);
 
   ~WorkerPrivateParent();
 
@@ -309,8 +222,9 @@ private:
 
   void
   PostMessageInternal(JSContext* aCx, JS::Handle aMessage,
-                      const Optional >& aTransferable,
+                      const Optional>& aTransferable,
                       bool aToMessagePort, uint64_t aMessagePortSerial,
+                      ServiceWorkerClientInfo* aClientInfo,
                       ErrorResult& aRv);
 
   nsresult
@@ -412,9 +326,15 @@ public:
               const Optional >& aTransferable,
               ErrorResult& aRv)
   {
-    PostMessageInternal(aCx, aMessage, aTransferable, false, 0, aRv);
+    PostMessageInternal(aCx, aMessage, aTransferable, false, 0, nullptr, aRv);
   }
 
+  void
+  PostMessageToServiceWorker(JSContext* aCx, JS::Handle aMessage,
+                             const Optional>& aTransferable,
+                             nsAutoPtr& aClientInfo,
+                             ErrorResult& aRv);
+
   void
   PostMessageToMessagePort(JSContext* aCx,
                            uint64_t aMessagePortSerial,
@@ -926,12 +846,12 @@ public:
   Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
               bool aIsChromeWorker, WorkerType aWorkerType,
               const nsACString& aSharedWorkerName,
-              LoadInfo* aLoadInfo, ErrorResult& aRv);
+              WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
 
   static already_AddRefed
   Constructor(JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
               WorkerType aWorkerType, const nsACString& aSharedWorkerName,
-              LoadInfo* aLoadInfo, ErrorResult& aRv);
+              WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
 
   static bool
   WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
@@ -945,10 +865,10 @@ public:
   static nsresult
   GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
               const nsAString& aScriptURL, bool aIsChromeWorker,
-              LoadGroupBehavior aLoadGroupBehavior, LoadInfo* aLoadInfo);
+              LoadGroupBehavior aLoadGroupBehavior, WorkerLoadInfo* aLoadInfo);
 
   static void
-  OverrideLoadInfoLoadGroup(LoadInfo& aLoadInfo);
+  OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo);
 
   WorkerDebugger*
   Debugger() const
@@ -1185,13 +1105,6 @@ public:
     return mPreferences[WORKERPREF_DUMP];
   }
 
-  bool
-  DOMFetchEnabled() const
-  {
-    AssertIsOnWorkerThread();
-    return mPreferences[WORKERPREF_DOM_FETCH];
-  }
-
   bool
   DOMCachesEnabled() const
   {
@@ -1255,7 +1168,7 @@ private:
   WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsACString& aSharedWorkerName,
-                LoadInfo& aLoadInfo);
+                WorkerLoadInfo& aLoadInfo);
 
   void
   ClearMainEventQueue(WorkerRanOrNot aRanOrNot);
diff --git a/dom/workers/Workers.h b/dom/workers/Workers.h
index c798f99cc7..abdb6ae690 100644
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -14,6 +14,11 @@
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsString.h"
+#include "nsTArray.h"
+
+#include "nsILoadContext.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIInterfaceRequestor.h"
 
 #define BEGIN_WORKERS_NAMESPACE \
   namespace mozilla { namespace dom { namespace workers {
@@ -24,9 +29,32 @@
 
 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
 
-class nsIGlobalObject;
+class nsIContentSecurityPolicy;
 class nsIScriptContext;
+class nsIGlobalObject;
 class nsPIDOMWindow;
+class nsIPrincipal;
+class nsILoadGroup;
+class nsITabChild;
+class nsIChannel;
+class nsIURI;
+
+namespace mozilla {
+namespace ipc {
+class PrincipalInfo;
+}
+
+namespace dom {
+// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to
+// be updated too.
+enum WorkerType
+{
+  WorkerTypeDedicated,
+  WorkerTypeShared,
+  WorkerTypeService
+};
+}
+}
 
 BEGIN_WORKERS_NAMESPACE
 
@@ -166,11 +194,68 @@ struct JSSettings
 enum WorkerPreference
 {
   WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
-  WORKERPREF_DOM_FETCH,// dom.fetch.enabled
   WORKERPREF_DOM_CACHES, // dom.caches.enabled
   WORKERPREF_COUNT
 };
 
+// Implemented in WorkerPrivate.cpp
+
+struct WorkerLoadInfo
+{
+  // All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
+  nsCOMPtr mBaseURI;
+  nsCOMPtr mResolvedScriptURI;
+  nsCOMPtr mPrincipal;
+  nsCOMPtr mScriptContext;
+  nsCOMPtr mWindow;
+  nsCOMPtr mCSP;
+  nsCOMPtr mChannel;
+  nsCOMPtr mLoadGroup;
+
+  class InterfaceRequestor MOZ_FINAL : public nsIInterfaceRequestor
+  {
+    NS_DECL_ISUPPORTS
+
+  public:
+    InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
+    void MaybeAddTabChild(nsILoadGroup* aLoadGroup);
+    NS_IMETHOD GetInterface(const nsIID& aIID, void** aSink) MOZ_OVERRIDE;
+
+  private:
+    ~InterfaceRequestor() { }
+
+    already_AddRefed GetAnyLiveTabChild();
+
+    nsCOMPtr mLoadContext;
+
+    // Array of weak references to nsITabChild.  We do not want to keep TabChild
+    // actors alive for long after their ActorDestroy() methods are called.
+    nsTArray mTabChildList;
+  };
+
+  // Only set if we have a custom overriden load group
+  nsRefPtr mInterfaceRequestor;
+
+  nsAutoPtr mPrincipalInfo;
+  nsCString mDomain;
+
+  uint64_t mWindowID;
+
+  bool mFromWindow;
+  bool mEvalAllowed;
+  bool mReportCSPViolations;
+  bool mXHRParamsAllowed;
+  bool mPrincipalIsSystem;
+  bool mIsInPrivilegedApp;
+  bool mIsInCertifiedApp;
+  bool mIndexedDBAllowed;
+
+  WorkerLoadInfo();
+  ~WorkerLoadInfo();
+
+  void StealFrom(WorkerLoadInfo& aOther);
+};
+
 // All of these are implemented in RuntimeService.cpp
 
 void
diff --git a/dom/workers/test/fetch/test_interfaces.html b/dom/workers/test/fetch/test_interfaces.html
index dbbf21f6d1..207674486d 100644
--- a/dom/workers/test/fetch/test_interfaces.html
+++ b/dom/workers/test/fetch/test_interfaces.html
@@ -36,11 +36,7 @@
 
   SimpleTest.waitForExplicitFinish();
 
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.fetch.enabled", true]
-  ]}, function() {
-    checkEnabled();
-  });
+  checkEnabled();
 
 
 
diff --git a/dom/workers/test/fetch/test_request.html b/dom/workers/test/fetch/test_request.html
index 73d7b5b989..6473636a93 100644
--- a/dom/workers/test/fetch/test_request.html
+++ b/dom/workers/test/fetch/test_request.html
@@ -36,11 +36,7 @@
 
   SimpleTest.waitForExplicitFinish();
 
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.fetch.enabled", true]
-  ]}, function() {
-    checkEnabled();
-  });
+  checkEnabled();
 
 
 
diff --git a/dom/workers/test/fetch/test_response.html b/dom/workers/test/fetch/test_response.html
index e1bbd399ed..9e38ec31af 100644
--- a/dom/workers/test/fetch/test_response.html
+++ b/dom/workers/test/fetch/test_response.html
@@ -36,11 +36,7 @@
 
   SimpleTest.waitForExplicitFinish();
 
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.fetch.enabled", true]
-  ]}, function() {
-    runTest();
-  });
+  runTest();
 
 
 
diff --git a/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs
new file mode 100644
index 0000000000..abacdd2adb
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs
@@ -0,0 +1,17 @@
+function handleRequest(request, response) {
+  // The string "hello" repeated 10 times followed by newline. Compressed using gzip.
+  var bytes = [0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xe2, 0xf9, 0x54, 0x00, 0x03, 0x68,
+               0x65, 0x6c, 0x6c, 0x6f, 0x00, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0xcf,
+               0x20, 0x85, 0xe0, 0x02, 0x00, 0xf5, 0x4b, 0x38, 0xcf, 0x33, 0x00,
+               0x00, 0x00];
+
+  response.setHeader("Content-Encoding", "gzip", false);
+  response.setHeader("Content-Length", "" + bytes.length, false);
+  response.setHeader("Content-Type", "text/plain", false);
+
+  var bos = Components.classes["@mozilla.org/binaryoutputstream;1"]
+      .createInstance(Components.interfaces.nsIBinaryOutputStream);
+  bos.setOutputStream(response.bodyOutputStream);
+
+  bos.writeByteArray(bytes, bytes.length);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/fetch_tests.js b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
index e6900b6860..4db53ee569 100644
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -27,6 +27,25 @@ fetch('synthesized.txt', function(xhr) {
   finish();
 });
 
+fetch('test-respondwith-response.txt', function(xhr) {
+  my_ok(xhr.status == 200, "test-respondwith-response load should be successful");
+  my_ok(xhr.responseText == "test-respondwith-response response body", "load should have response");
+  finish();
+});
+
+fetch('synthesized-404.txt', function(xhr) {
+  my_ok(xhr.status == 404, "load should 404");
+  my_ok(xhr.responseText == "synthesized response body", "404 load should have synthesized response");
+  finish();
+});
+
+fetch('synthesized-headers.txt', function(xhr) {
+  my_ok(xhr.status == 200, "load should be successful");
+  my_ok(xhr.getResponseHeader("X-Custom-Greeting") === "Hello", "custom header should be set");
+  my_ok(xhr.responseText == "synthesized response body", "custom header load should have synthesized response");
+  finish();
+});
+
 fetch('ignored.txt', function(xhr) {
   my_ok(xhr.status == 404, "load should be uninterrupted");
   finish();
@@ -52,3 +71,35 @@ fetch('headers.txt', function(xhr) {
   my_ok(xhr.responseText == "1", "request header checks should have passed");
   finish();
 }, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]);
+
+var expectedUncompressedResponse = "";
+for (var i = 0; i < 10; ++i) {
+  expectedUncompressedResponse += "hello";
+}
+expectedUncompressedResponse += "\n";
+
+// ServiceWorker does not intercept, at which point the network request should
+// be correctly decoded.
+fetch('deliver-gzip.sjs', function(xhr) {
+  my_ok(xhr.status == 200, "network gzip load should be successful");
+  my_ok(xhr.responseText == expectedUncompressedResponse, "network gzip load should have synthesized response.");
+  my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "network Content-Encoding should be gzip.");
+  my_ok(xhr.getResponseHeader("Content-Length") == "35", "network Content-Length should be of original gzipped file.");
+  finish();
+});
+
+fetch('hello.gz', function(xhr) {
+  my_ok(xhr.status == 200, "gzip load should be successful");
+  my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
+  my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
+  my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
+  finish();
+});
+
+fetch('hello-after-extracting.gz', function(xhr) {
+  my_ok(xhr.status == 200, "gzip load should be successful");
+  my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
+  my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
+  my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
+  finish();
+});
diff --git a/dom/workers/test/serviceworkers/fetch_event_worker.js b/dom/workers/test/serviceworkers/fetch_event_worker.js
index ec956e7879..de46d39489 100644
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -1,86 +1,135 @@
+var seenIndex = false;
+
 onfetch = function(ev) {
   if (ev.request.url.includes("synthesized.txt")) {
-    var p = new Promise(function(resolve) {
-      var r = new Response("synthesized response body", {});
-      resolve(r);
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(
+      new Response("synthesized response body", {})
+    ));
+  }
+
+  else if (ev.request.url.contains("synthesized-404.txt")) {
+    ev.respondWith(Promise.resolve(
+      new Response("synthesized response body", { status: 404 })
+    ));
+  }
+
+  else if (ev.request.url.contains("synthesized-headers.txt")) {
+    ev.respondWith(Promise.resolve(
+      new Response("synthesized response body", {
+        headers: {
+          "X-Custom-Greeting": "Hello"
+        }
+      })
+    ));
+  }
+
+  else if (ev.request.url.contains("test-respondwith-response.txt")) {
+    ev.respondWith(new Response("test-respondwith-response response body", {}));
   }
 
   else if (ev.request.url.includes("ignored.txt")) {
   }
 
   else if (ev.request.url.includes("rejected.txt")) {
-    var p = new Promise(function(resolve, reject) {
-      reject();
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.reject());
   }
 
   else if (ev.request.url.includes("nonresponse.txt")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(5);
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(5));
   }
 
   else if (ev.request.url.includes("nonresponse2.txt")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve({});
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve({}));
   }
 
   else if (ev.request.url.includes("headers.txt")) {
-    var p = new Promise(function(resolve, reject) {
-      var ok = true;
-      ok &= ev.request.headers.get("X-Test1") == "header1";
-      ok &= ev.request.headers.get("X-Test2") == "header2";
-      var r = new Response(ok.toString(), {});
-      resolve(r);
-    });
-    ev.respondWith(p);    
+    var ok = true;
+    ok &= ev.request.headers.get("X-Test1") == "header1";
+    ok &= ev.request.headers.get("X-Test2") == "header2";
+    ev.respondWith(Promise.resolve(
+      new Response(ok.toString(), {})
+    ));
   }
 
   else if (ev.request.url.includes("nonexistent_image.gif")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(new Response(atob("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs"), {}));
-    });
-    ev.respondWith(p);
+      resolve(new Response(atob("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs"), {
+    ev.respondWith(Promise.resolve(
+      new Response(atob("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs"), {
+        headers: {
+          "Content-Type": "image/gif"
+        }
+      })
+    ));
   }
 
   else if (ev.request.url.includes("nonexistent_script.js")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(new Response("check_intercepted_script();", {}));
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(
+      new Response("check_intercepted_script();", {})
+    ));
   }
 
   else if (ev.request.url.includes("nonexistent_stylesheet.css")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(new Response("#style-test { background-color: black !important; }", {}));
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(
+      new Response("#style-test { background-color: black !important; }", {
+        headers : {
+          "Content-Type": "text/css"
+        }
+      })
+    ));
   }
 
   else if (ev.request.url.includes("nonexistent_page.html")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(new Response("", {}));
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(
+      new Response("", {
+        headers : {
+          "Content-Type": "text/html"
+        }
+      })
+    ));
   }
 
   else if (ev.request.url.includes("nonexistent_worker_script.js")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(new Response("postMessage('worker-intercept-success')", {}));
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(
+      new Response("postMessage('worker-intercept-success')", {})
+    ));
   }
 
   else if (ev.request.url.includes("nonexistent_imported_script.js")) {
-    var p = new Promise(function(resolve, reject) {
-      resolve(new Response("check_intercepted_script();", {}));
-    });
-    ev.respondWith(p);
+    ev.respondWith(Promise.resolve(
+      new Response("check_intercepted_script();", {})
+    ));
+  }
+
+  else if (ev.request.url.contains("deliver-gzip")) {
+    // Don't handle the request, this will make Necko perform a network request, at
+    // which point SetApplyConversion must be re-enabled, otherwise the request
+    // will fail.
+    return;
+  }
+
+  else if (ev.request.url.contains("hello.gz")) {
+    ev.respondWith(fetch("fetch/deliver-gzip.sjs"));
+  }
+
+  else if (ev.request.url.contains("hello-after-extracting.gz")) {
+    ev.respondWith(fetch("fetch/deliver-gzip.sjs").then(function(res) {
+      return res.text().then(function(body) {
+        return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
+      });
+    }));
+  }
+
+  else if (ev.request.url.contains("index.html")) {
+    if (seenIndex) {
+        var body = "";
+        ev.respondWith(new Response(body, {headers: {'Content-Type': 'text/html'}}));
+    } else {
+      seenIndex = true;
+      ev.respondWith(fetch(ev.request.url));
+    }
   }
 }
diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini
index cb92dd14ae..78c6efa7fa 100644
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -24,6 +24,7 @@ support-files =
   fetch/index.html
   fetch/fetch_worker_script.js
   fetch/fetch_tests.js
+  fetch/deliver-gzip.sjs
   fetch/https/index.html
   fetch/https/register.html
   fetch/https/unregister.html
@@ -41,6 +42,8 @@ support-files =
   serviceworker_not_sharedworker.js
   match_all_client/match_all_client_id.html
   match_all_client_id_worker.js
+  source_message_posting_worker.js
+  scope/scope_worker.js
 
 [test_unregister.html]
 skip-if = true # Bug 1133805
@@ -59,6 +62,7 @@ skip-if = true # Bug 1133805
 skip-if = true # Bug 1133805
 [test_post_message.html]
 [test_post_message_advanced.html]
+[test_post_message_source.html]
 [test_match_all_client_properties.html]
 [test_close.html]
 [test_serviceworker_interfaces.html]
diff --git a/dom/workers/test/serviceworkers/scope/scope_worker.js b/dom/workers/test/serviceworkers/scope/scope_worker.js
new file mode 100644
index 0000000000..4164e7a244
--- /dev/null
+++ b/dom/workers/test/serviceworkers/scope/scope_worker.js
@@ -0,0 +1,2 @@
+// This worker is used to test if calling register() without a scope argument
+// leads to scope being relative to service worker script.
diff --git a/dom/workers/test/serviceworkers/simpleregister/index.html b/dom/workers/test/serviceworkers/simpleregister/index.html
index 8f88c1b39a..2c0eb5345e 100644
--- a/dom/workers/test/serviceworkers/simpleregister/index.html
+++ b/dom/workers/test/serviceworkers/simpleregister/index.html
@@ -36,15 +36,6 @@
             }
           });
 
-          navigator.serviceWorker.getRegistration('http://mochi.test:8888/with_star/*')
-            .then(function(a) {
-            window.parent.postMessage({ type: "check", status: true,
-                                        msg: "getRegistration returns a ServiceWorkerRegistration" }, "*");
-            a.onupdatefound = function(e) {
-              eventReceived();
-            }
-          });
-
           navigator.serviceWorker.getRegistration('http://www.something_else.net/')
             .then(function(a) {
             window.parent.postMessage({ type: "check", status: false,
diff --git a/dom/workers/test/serviceworkers/source_message_posting_worker.js b/dom/workers/test/serviceworkers/source_message_posting_worker.js
new file mode 100644
index 0000000000..ab8c085daf
--- /dev/null
+++ b/dom/workers/test/serviceworkers/source_message_posting_worker.js
@@ -0,0 +1,12 @@
+onmessage = function(e) {
+  if (!e.source) {
+    dump("ERROR: message doesn't have a source.");
+  }
+
+  // The client should be a window client
+  if (e.source instanceof  WindowClient) {
+    e.source.postMessage(e.data);
+  } else {
+    e.source.postMessage("ERROR. source is not a window client.");
+  }
+};
diff --git a/dom/workers/test/serviceworkers/test_controller.html b/dom/workers/test/serviceworkers/test_controller.html
index 07ecc16b92..483f488397 100644
--- a/dom/workers/test/serviceworkers/test_controller.html
+++ b/dom/workers/test/serviceworkers/test_controller.html
@@ -60,6 +60,7 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_fetch_event.html b/dom/workers/test/serviceworkers/test_fetch_event.html
index e2a2166026..354d5677c7 100644
--- a/dom/workers/test/serviceworkers/test_fetch_event.html
+++ b/dom/workers/test/serviceworkers/test_fetch_event.html
@@ -18,18 +18,28 @@
 
   function simpleRegister() {
     var p = navigator.serviceWorker.register("fetch_event_worker.js", { scope: "./fetch" });
-    return p;
+    return p.then(function(swr) {
+      return new Promise(function(resolve) {
+        swr.installing.onstatechange = resolve;
+      });
+    });
   }
 
   function testController() {
     var p = new Promise(function(resolve, reject) {
+      var reloaded = false;
       window.onmessage = function(e) {
         if (e.data.status == "ok") {
           ok(e.data.result, e.data.message);
         } else if (e.data.status == "done") {
-          window.onmessage = null;
-          w.close();
-          resolve();
+          if (reloaded) {
+            window.onmessage = null;
+            w.close();
+            resolve();
+          } else {
+            w.location.reload();
+            reloaded = true;
+          }
         }
       }
     });
@@ -51,9 +61,9 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.fetch.enabled", true]
   ]}, runTest);
 
 
diff --git a/dom/workers/test/serviceworkers/test_install_event.html b/dom/workers/test/serviceworkers/test_install_event.html
index dcfc2d2393..86532002d6 100644
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -102,6 +102,7 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_installation_simple.html b/dom/workers/test/serviceworkers/test_installation_simple.html
index 4619f39cb2..8f5c61898e 100644
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -177,6 +177,7 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.messageChannel.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
diff --git a/dom/workers/test/serviceworkers/test_match_all.html b/dom/workers/test/serviceworkers/test_match_all.html
index ce60647ab6..c6d3bdbee9 100644
--- a/dom/workers/test/serviceworkers/test_match_all.html
+++ b/dom/workers/test/serviceworkers/test_match_all.html
@@ -67,6 +67,7 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_navigator.html b/dom/workers/test/serviceworkers/test_navigator.html
index cb8936e22b..164f41bcd2 100644
--- a/dom/workers/test/serviceworkers/test_navigator.html
+++ b/dom/workers/test/serviceworkers/test_navigator.html
@@ -27,6 +27,7 @@
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true]
   ]}, function() {
     checkEnabled();
diff --git a/dom/workers/test/serviceworkers/test_post_message.html b/dom/workers/test/serviceworkers/test_post_message.html
index 0bf48f93a4..2a1bf99c33 100644
--- a/dom/workers/test/serviceworkers/test_post_message.html
+++ b/dom/workers/test/serviceworkers/test_post_message.html
@@ -66,6 +66,7 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_post_message_advanced.html b/dom/workers/test/serviceworkers/test_post_message_advanced.html
index a91fe7fef2..7d4c9ba277 100644
--- a/dom/workers/test/serviceworkers/test_post_message_advanced.html
+++ b/dom/workers/test/serviceworkers/test_post_message_advanced.html
@@ -95,6 +95,7 @@
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
diff --git a/dom/workers/test/serviceworkers/test_post_message_source.html b/dom/workers/test/serviceworkers/test_post_message_source.html
new file mode 100644
index 0000000000..3cf6c45abc
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_post_message_source.html
@@ -0,0 +1,65 @@
+
+
+
+
+  Bug 1142015 - Test service worker post message source 
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_scopes.html b/dom/workers/test/serviceworkers/test_scopes.html
index 464a9fd5e7..2d8116f837 100644
--- a/dom/workers/test/serviceworkers/test_scopes.html
+++ b/dom/workers/test/serviceworkers/test_scopes.html
@@ -16,7 +16,6 @@
 
 
 
diff --git a/dom/workers/test/serviceworkers/unregister/unregister.html b/dom/workers/test/serviceworkers/unregister/unregister.html
index 64e47b3ae8..6fda820268 100644
--- a/dom/workers/test/serviceworkers/unregister/unregister.html
+++ b/dom/workers/test/serviceworkers/unregister/unregister.html
@@ -12,7 +12,7 @@
 
 
diff --git a/dom/workers/test/serviceworkers/workerUpdate/update.html b/dom/workers/test/serviceworkers/workerUpdate/update.html
index 0aa0fb14d0..8f984ccc44 100644
--- a/dom/workers/test/serviceworkers/workerUpdate/update.html
+++ b/dom/workers/test/serviceworkers/workerUpdate/update.html
@@ -12,7 +12,7 @@