Bug 1155153 - about:serviceWorkers e10s take 2 draft
authorNikhil Marathe <nsm.nikhil@gmail.com>
Mon, 01 Jun 2015 10:33:45 -0700
changeset 269269 e171cc3729722b88fda5a5fb0c11e643ce8b2dc5
parent 269268 f09904276281685d3715f1a89ceb2a1b5cd7f85c
child 269270 3bb2972f07836a5c40045cb1d085baeca2e7919e
child 269276 090f782a47817bee2bc619ac32a8e9b2ce09daef
push id2479
push usernsm.nikhil@gmail.com
push dateTue, 02 Jun 2015 21:54:18 +0000
bugs1155153
milestone41.0a1
Bug 1155153 - about:serviceWorkers e10s take 2
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/BasePrincipalIPC.h
caps/moz.build
docshell/base/nsAboutRedirector.cpp
dom/interfaces/base/nsIServiceWorkerManager.idl
dom/workers/PServiceWorkerManager.ipdl
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerManagerChild.cpp
dom/workers/ServiceWorkerManagerChild.h
dom/workers/ServiceWorkerManagerParent.cpp
dom/workers/ServiceWorkerManagerParent.h
dom/workers/ServiceWorkerManagerService.cpp
dom/workers/ServiceWorkerManagerService.h
dom/workers/moz.build
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
ipc/glue/moz.build
toolkit/content/aboutServiceWorkers.js
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -12,55 +12,112 @@
 
 #include "nsPrincipal.h"
 #include "nsNetUtil.h"
 #include "nsNullPrincipal.h"
 #include "nsScriptSecurityManager.h"
 
 #include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 
+using dom::URLSearchParams;
+
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
-  aStr.Truncate();
   MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
-  int attrCount = 0;
+
+  nsRefPtr<URLSearchParams> usp = new URLSearchParams();
+  nsAutoString value;
 
   if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
-    aStr.Append(attrCount++ ? "&appId=" : "!appId=");
-    aStr.AppendInt(mAppId);
+    value.AppendInt(mAppId);
+    usp->Set(NS_LITERAL_STRING("appId"), value);
   }
 
   if (mInBrowser) {
-    aStr.Append(attrCount++ ? "&inBrowser=1" : "!inBrowser=1");
+    usp->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
   }
+
+  usp->Serialize(value);
+
+  aStr.Truncate();
+  aStr.AppendLiteral("!");
+  aStr.Append(NS_ConvertUTF16toUTF8(value));
+}
+
+bool
+OriginAttributes::PopulateFromSuffix(const nsACString& aStr)
+{
+  nsRefPtr<URLSearchParams> usp = new URLSearchParams();
+  usp->ParseInput(aStr, nullptr);
+
+  uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
+  bool inBrowser = false;
+
+  nsAutoString value;
+  if (usp->Has(NS_LITERAL_STRING("appId"))) {
+    usp->Get(NS_LITERAL_STRING("appId"), value);
+
+    nsresult rv;
+    appId = value.ToInteger(&rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    if (appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+      return false;
+    }
+  }
+
+  if (usp->Has(NS_LITERAL_STRING("inBrowser"))) {
+    usp->Get(NS_LITERAL_STRING("inBrowser"), value);
+    if (!value.EqualsLiteral("1")) {
+      return false;
+    }
+
+    inBrowser = true;
+  }
+
+  mAppId = appId;
+  mInBrowser = inBrowser;
+  return true;
 }
 
 void
 OriginAttributes::Serialize(nsIObjectOutputStream* aStream) const
 {
-  aStream->Write32(mAppId);
-  aStream->WriteBoolean(mInBrowser);
+  nsAutoCString suffix;
+  CreateSuffix(suffix);
+  aStream->WriteStringZ(suffix.get());
 }
 
 nsresult
 OriginAttributes::Deserialize(nsIObjectInputStream* aStream)
 {
-  nsresult rv = aStream->Read32(&mAppId);
+  nsAutoCString suffix;
+  nsresult rv = aStream->ReadCString(suffix);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aStream->ReadBoolean(&mInBrowser);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (!PopulateFromSuffix(suffix)) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
+BasePrincipal::BasePrincipal()
+{}
+
+BasePrincipal::~BasePrincipal()
+{}
+
 NS_IMETHODIMP
 BasePrincipal::GetOrigin(nsACString& aOrigin)
 {
   nsresult rv = GetOriginInternal(aOrigin);
   NS_ENSURE_SUCCESS(rv, rv);
   nsAutoCString suffix;
   mOriginAttributes.CreateSuffix(suffix);
   aOrigin.Append(suffix);
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -8,16 +8,17 @@
 #define mozilla_BasePrincipal_h
 
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsJSPrincipals.h"
 
 #include "mozilla/dom/SystemDictionariesBinding.h"
 
+class nsIContentSecurityPolicy;
 class nsIObjectOutputStream;
 class nsIObjectInputStream;
 
 namespace mozilla {
 
 class OriginAttributes : public dom::OriginAttributesDictionary
 {
 public:
@@ -37,32 +38,33 @@ public:
   {
     return !(*this == aOther);
   }
 
   // Serializes non-default values into the suffix format, i.e.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
   // returns an empty string.
   void CreateSuffix(nsACString& aStr) const;
+  bool PopulateFromSuffix(const nsACString& aStr);
 
   void Serialize(nsIObjectOutputStream* aStream) const;
   nsresult Deserialize(nsIObjectInputStream* aStream);
 };
 
 /*
  * Base class from which all nsIPrincipal implementations inherit. Use this for
  * default implementations and other commonalities between principal
  * implementations.
  *
  * We should merge nsJSPrincipals into this class at some point.
  */
 class BasePrincipal : public nsJSPrincipals
 {
 public:
-  BasePrincipal() {}
+  BasePrincipal();
 
   enum DocumentDomainConsideration { DontConsiderDocumentDomain, ConsiderDocumentDomain};
   bool Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration);
 
   NS_IMETHOD GetOrigin(nsACString& aOrigin) final;
   NS_IMETHOD GetOriginNoSuffix(nsACString& aOrigin) final;
   NS_IMETHOD Equals(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
@@ -86,17 +88,17 @@ public:
   static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
   static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs);
 
   const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
   uint32_t AppId() const { return mOriginAttributes.mAppId; }
   bool IsInBrowserElement() const { return mOriginAttributes.mInBrowser; }
 
 protected:
-  virtual ~BasePrincipal() {}
+  virtual ~BasePrincipal();
 
   virtual nsresult GetOriginInternal(nsACString& aOrigin) = 0;
   virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
 
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   OriginAttributes mOriginAttributes;
 };
 
new file mode 100644
--- /dev/null
+++ b/caps/BasePrincipalIPC.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BasePrincipalIPC_h
+#define mozilla_BasePrincipalIPC_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/BasePrincipal.h"
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::OriginAttributes>
+{
+  typedef mozilla::OriginAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    nsAutoCString suffix;
+    aParam.CreateSuffix(suffix);
+    WriteParam(aMsg, suffix);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    nsAutoCString suffix;
+    return ReadParam(aMsg, aIter, &suffix) &&
+           aResult->PopulateFromSuffix(suffix);
+  }
+};
+
+} // IPC namespace
+
+#endif // mozilla_BasePrincipalIPC_h
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -17,16 +17,20 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'caps'
 
 EXPORTS += [
     'nsJSPrincipals.h',
     'nsNullPrincipal.h',
     'nsNullPrincipalURI.h',
 ]
 
+EXPORTS.ipc = [
+    'BasePrincipalIPC.h'
+]
+
 EXPORTS.mozilla = [
     'BasePrincipal.h'
 ]
 
 UNIFIED_SOURCES += [
     'BasePrincipal.cpp',
     'DomainPolicy.cpp',
     'nsJSPrincipals.cpp',
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -100,16 +100,17 @@ static RedirEntry kRedirMap[] = {
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
     "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.xhtml",
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
     "serviceworkers", "chrome://global/content/aboutServiceWorkers.xhtml",
+    nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT
   },
   // about:srcdoc is unresolvable by specification.  It is included here
   // because the security manager would disallow srcdoc iframes otherwise.
   {
     "srcdoc", "about:blank",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -28,17 +28,17 @@ interface nsIServiceWorkerInfo : nsISupp
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString currentWorkerURL;
 
   readonly attribute DOMString activeCacheName;
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(9830de01-b52f-4ff7-959d-23c9fd58706c)]
+[scriptable, builtinclass, uuid(5e112a42-df4c-4ae9-bc71-e6e681ab5f38)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
@@ -92,23 +92,16 @@ interface nsIServiceWorkerManager : nsIS
   [noscript] nsISupports GetActive(in nsIDOMWindow aWindow, in DOMString aScope);
 
   /*
    * Returns a ServiceWorker.
    */
   [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
 
   /*
-   * This implements the soft update algorithm.
-   * XXXbaku this can be removed when bug 1155153 lands.
-   */
-  [implicit_jscontext] void softUpdate(in jsval aOriginAttributes,
-                                       in DOMString aScope);
-
-  /*
    * Clears ServiceWorker registrations from memory and disk for the specified
    * host.
    * - All ServiceWorker instances change their state to redundant.
    * - Existing ServiceWorker instances handling fetches will keep running.
    * - All documents will immediately stop being controlled.
    * - Unregister jobs will be queued for all registrations.
    *   This eventually results in the registration being deleted from disk too.
    */
@@ -117,20 +110,32 @@ interface nsIServiceWorkerManager : nsIS
   /*
    * Clear all registrations for all hosts. See remove().
    */
   void removeAll();
 
   // Testing
   DOMString getScopeForUrl(in nsIPrincipal aPrincipal, in DOMString aPath);
 
-  // This is meant to be used only by about:serviceworkers. It returns an array
-  // of nsIServiceWorkerInfo.
+  // Note: This is meant to be used only by about:serviceworkers.
+  //It returns an array of nsIServiceWorkerInfo.
   nsIArray getAllRegistrations();
 
+  // Note: This is meant to be used only by about:serviceworkers.
+  // It calls softUpdate() for each child process.
+  [implicit_jscontext] void propagateSoftUpdate(in jsval aOriginAttributes,
+                                                in DOMString aScope);
+
+  // Note: This is meant to be used only by about:serviceworkers.
+  // It calls unregister() in each child process. The callback is used to
+  // inform when unregister() is completed on the current process.
+  void propagateUnregister(in nsIPrincipal aPrincipal,
+                           in nsIServiceWorkerUnregisterCallback aCallback,
+                           in DOMString aScope);
+
   [implicit_jscontext] void sendPushEvent(in jsval aOriginAttributes,
                                           in ACString aScope,
                                           in DOMString aData);
   [implicit_jscontext] void sendPushSubscriptionChangeEvent(in jsval aOriginAttributes,
                                                             in ACString scope);
 
   void sendNotificationClickEvent(in ACString scope,
                                   in AString aID,
new file mode 100644
--- /dev/null
+++ b/dom/workers/PServiceWorkerManager.ipdl
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackground;
+
+include PBackgroundSharedTypes;
+include ServiceWorkerRegistrarTypes;
+
+using mozilla::OriginAttributes from "ipc/BasePrincipalIPC.h";
+
+namespace mozilla {
+namespace dom {
+
+protocol PServiceWorkerManager
+{
+  manager PBackground;
+
+parent:
+  Register(ServiceWorkerRegistrationData data);
+
+  Unregister(PrincipalInfo principalInfo, nsString scope);
+
+  PropagateSoftUpdate(OriginAttributes originAttributes,
+                      nsString scope);
+  PropagateUnregister(PrincipalInfo principalInfo, nsString scope);
+
+  Shutdown();
+
+child:
+  NotifyRegister(ServiceWorkerRegistrationData data);
+  NotifySoftUpdate(OriginAttributes originAttributes, nsString scope);
+  NotifyUnregister(PrincipalInfo principalInfo, nsString scope);
+
+  __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -49,16 +49,17 @@
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsTArray.h"
 
 #include "RuntimeService.h"
 #include "ServiceWorker.h"
 #include "ServiceWorkerClient.h"
 #include "ServiceWorkerContainer.h"
+#include "ServiceWorkerManagerChild.h"
 #include "ServiceWorkerRegistrar.h"
 #include "ServiceWorkerRegistration.h"
 #include "ServiceWorkerScriptCache.h"
 #include "ServiceWorkerEvents.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
@@ -256,16 +257,38 @@ PopulateRegistrationData(nsIPrincipal* a
 
   if (aRegistration->mWaitingWorker) {
     aData.waitingCacheName() = aRegistration->mWaitingWorker->CacheName();
   }
 
   return NS_OK;
 }
 
+class TeardownRunnable final : public nsRunnable
+{
+public:
+  explicit TeardownRunnable(ServiceWorkerManagerChild* aActor)
+    : mActor(aActor)
+  {
+    MOZ_ASSERT(mActor);
+  }
+
+  NS_IMETHODIMP Run() override
+  {
+    MOZ_ASSERT(mActor);
+    mActor->SendShutdown();
+    return NS_OK;
+  }
+
+private:
+  ~TeardownRunnable() {}
+
+  nsRefPtr<ServiceWorkerManagerChild> mActor;
+};
+
 } // Anonymous namespace
 
 NS_IMPL_ISUPPORTS0(ServiceWorkerJob)
 NS_IMPL_ISUPPORTS0(ServiceWorkerRegistrationInfo)
 
 void
 ServiceWorkerJob::Done(nsresult aStatus)
 {
@@ -352,19 +375,46 @@ NS_INTERFACE_MAP_BEGIN(ServiceWorkerMana
   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   if (aIID.Equals(NS_GET_IID(ServiceWorkerManager)))
     foundInterface = static_cast<nsIServiceWorkerManager*>(this);
   else
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
 NS_INTERFACE_MAP_END
 
+namespace {
+
+ServiceWorkerManager* mSWMInstanceInitializing = nullptr;
+
+} // anonymous namespace
+
 ServiceWorkerManager::ServiceWorkerManager()
   : mActor(nullptr)
+{}
+
+ServiceWorkerManager::~ServiceWorkerManager()
 {
+  // The map will assert if it is not empty when destroyed.
+  mRegistrationInfos.Clear();
+
+  if (mActor) {
+    mActor->ManagerShuttingDown();
+
+    nsRefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
+    nsresult rv = NS_DispatchToMainThread(runnable);
+    unused << NS_WARN_IF(NS_FAILED(rv));
+  }
+}
+
+void
+ServiceWorkerManager::Initialize()
+{
+  AutoRestore<ServiceWorkerManager*> ar(mSWMInstanceInitializing);
+  mSWMInstanceInitializing = this;
+
   // Register this component to PBackground.
   MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this));
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsRefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
     MOZ_ASSERT(swr);
 
     nsTArray<ServiceWorkerRegistrationData> data;
@@ -379,22 +429,16 @@ ServiceWorkerManager::ServiceWorkerManag
       rv = obs->AddObserver(this, PURGE_SESSION_HISTORY, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 }
 
-ServiceWorkerManager::~ServiceWorkerManager()
-{
-  // The map will assert if it is not empty when destroyed.
-  mRegistrationInfos.Clear();
-}
-
 class ContinueLifecycleTask : public nsISupports
 {
   NS_DECL_ISUPPORTS
 
 protected:
   virtual ~ContinueLifecycleTask()
   { }
 
@@ -684,16 +728,82 @@ GetRequiredScopeStringPrefix(nsIURI* aSc
     }
 
     aPrefix.Append(path);
   } else {
     MOZ_ASSERT_UNREACHABLE("Invalid value for aPrefixMode");
   }
   return NS_OK;
 }
+
+class PropagateSoftUpdateRunnable final : public nsRunnable
+{
+public:
+  PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
+                              const nsAString& aScope)
+    : mOriginAttributes(aOriginAttributes)
+    , mScope(aScope)
+  {}
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    MOZ_ASSERT(swm);
+
+    swm->PropagateSoftUpdate(mOriginAttributes,mScope);
+    return NS_OK;
+  }
+
+private:
+  ~PropagateSoftUpdateRunnable()
+  {}
+
+  const OriginAttributes mOriginAttributes;
+  const nsString mScope;
+};
+
+class PropagateUnregisterRunnable final : public nsRunnable
+{
+public:
+  PropagateUnregisterRunnable(nsIPrincipal* aPrincipal,
+                              nsIServiceWorkerUnregisterCallback* aCallback,
+                              const nsAString& aScope)
+    : mPrincipal(aPrincipal)
+    , mCallback(aCallback)
+    , mScope(aScope)
+  {
+    MOZ_ASSERT(aPrincipal);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    MOZ_ASSERT(swm);
+
+    nsresult rv = swm->PropagateUnregister(mPrincipal, mCallback, mScope);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ~PropagateUnregisterRunnable()
+  {}
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
+  const nsString mScope;
+};
+
 } // anonymous namespace
 
 class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
                                        public serviceWorkerScriptCache::CompareCallback
 {
   friend class ContinueInstallTask;
 
   nsCString mScope;
@@ -2286,18 +2396,17 @@ private:
       }
 
       // "Invoke [[Clear Registration]]..."
       registration->Clear();
       swm->RemoveRegistration(registration);
     }
 
     MOZ_ASSERT(swm->mActor);
-    swm->mActor->SendUnregisterServiceWorker(principalInfo,
-                                             NS_ConvertUTF8toUTF16(mScope));
+    swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope));
 
     return NS_OK;
   }
 
   // The unregister job is done irrespective of success or failure of any sort.
   void
   UnregisterAndDone()
   {
@@ -2368,18 +2477,25 @@ ServiceWorkerManager::GetOrCreateJobQueu
 
   return queue;
 }
 
 /* static */
 already_AddRefed<ServiceWorkerManager>
 ServiceWorkerManager::GetInstance()
 {
-  nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
-  nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
+  nsRefPtr<ServiceWorkerManager> concrete;
+
+  if (mSWMInstanceInitializing) {
+    concrete = mSWMInstanceInitializing;
+  } else {
+    nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
+    concrete = do_QueryObject(swm);
+  }
+
   return concrete.forget();
 }
 
 void
 ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
 {
 }
 
@@ -2499,54 +2615,67 @@ ServiceWorkerManager::CreateServiceWorke
   nsRefPtr<ServiceWorker> serviceWorker =
     new ServiceWorker(aWindow, aInfo, sharedWorker);
 
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
 
 void
+ServiceWorkerManager::LoadRegistration(
+                             const ServiceWorkerRegistrationData& aRegistration)
+{
+  AssertIsOnMainThread();
+
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(aRegistration.principal());
+  if (!principal) {
+    return;
+  }
+
+  ServiceWorkerRegistrationInfo* registration =
+    CreateNewRegistration(aRegistration.scope(), principal);
+
+  registration->mScriptSpec = aRegistration.scriptSpec();
+
+  const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
+  if (!currentWorkerURL.IsEmpty()) {
+    registration->mActiveWorker =
+      new ServiceWorkerInfo(registration, currentWorkerURL, aRegistration.activeCacheName());
+    registration->mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
+  }
+}
+
+void
 ServiceWorkerManager::LoadRegistrations(
                   const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
 {
   AssertIsOnMainThread();
 
   for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
-    nsCOMPtr<nsIPrincipal> principal =
-      PrincipalInfoToPrincipal(aRegistrations[i].principal());
-    if (!principal) {
-      continue;
-    }
-
-    ServiceWorkerRegistrationInfo* registration =
-      CreateNewRegistration(aRegistrations[i].scope(), principal);
-
-    registration->mScriptSpec = aRegistrations[i].scriptSpec();
-
-    const nsCString& currentWorkerURL = aRegistrations[i].currentWorkerURL();
-    if (!currentWorkerURL.IsEmpty()) {
-      registration->mActiveWorker =
-        new ServiceWorkerInfo(registration, currentWorkerURL, aRegistrations[i].activeCacheName());
-      registration->mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
-    }
+    LoadRegistration(aRegistrations[i]);
   }
 }
 
 void
 ServiceWorkerManager::ActorFailed()
 {
   MOZ_CRASH("Failed to create a PBackgroundChild actor!");
 }
 
 void
 ServiceWorkerManager::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
 {
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(!mActor);
-  mActor = aActor;
+
+  PServiceWorkerManagerChild* actor =
+    aActor->SendPServiceWorkerManagerConstructor();
+
+  mActor = static_cast<ServiceWorkerManagerChild*>(actor);
 
   // Flush the pending requests.
   for (uint32_t i = 0, len = mPendingOperations.Length(); i < len; ++i) {
     MOZ_ASSERT(mPendingOperations[i].mRunnable ||
                (mPendingOperations[i].mJob && mPendingOperations[i].mQueue));
 
     if (mPendingOperations[i].mRunnable) {
       nsresult rv = NS_DispatchToCurrentThread(mPendingOperations[i].mRunnable);
@@ -2578,17 +2707,17 @@ ServiceWorkerManager::StoreRegistration(
   }
 
   PrincipalInfo principalInfo;
   if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
                                                     &principalInfo)))) {
     return;
   }
 
-  mActor->SendRegisterServiceWorker(data);
+  mActor->SendRegister(data);
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow)
 {
   MOZ_ASSERT(aWindow);
   nsCOMPtr<nsIDocument> document = aWindow->GetExtantDoc();
   return GetServiceWorkerRegistrationInfo(document);
@@ -3525,50 +3654,41 @@ ServiceWorkerManager::InvalidateServiceW
     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
 
     if (utf8Scope.Equals(aRegistration->mScope)) {
       target->InvalidateWorkers(aWhichOnes);
     }
   }
 }
 
-NS_IMETHODIMP
-ServiceWorkerManager::SoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
-                                 const nsAString& aScope, JSContext* aCx)
-{
-  AssertIsOnMainThread();
-
-  OriginAttributes attrs;
-  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  nsAutoCString suffix;
-  attrs.CreateSuffix(suffix);
-
-  SoftUpdate(suffix, NS_ConvertUTF16toUTF8(aScope));
-  return NS_OK;
-}
-
 void
 ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal,
                                  const nsACString& aScope)
 {
   MOZ_ASSERT(aPrincipal);
 
   nsAutoCString suffix;
   nsresult rv = PrincipalToScopeKey(aPrincipal, suffix);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   SoftUpdate(suffix, aScope);
 }
 
 void
+ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
+                                 const nsACString& aScope)
+{
+  nsAutoCString suffix;
+  aOriginAttributes.CreateSuffix(suffix);
+  SoftUpdate(suffix, aScope);
+}
+
+void
 ServiceWorkerManager::SoftUpdate(const nsACString& aOriginAttributesSuffix,
                                  const nsACString& aScope)
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> registration =
     GetRegistration(aOriginAttributesSuffix, aScope);
   if (NS_WARN_IF(!registration)) {
     return;
   }
@@ -3862,43 +3982,43 @@ ServiceWorkerManager::RemoveRegistration
   if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aRegistration->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(aRegistration->mScope));
+  mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aRegistration->mScope));
 }
 
 class ServiceWorkerDataInfo final : public nsIServiceWorkerInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERINFO
 
   static already_AddRefed<ServiceWorkerDataInfo>
-  Create(const ServiceWorkerRegistrationData& aData);
+  Create(const ServiceWorkerRegistrationInfo* aData);
 
 private:
   ServiceWorkerDataInfo()
   {}
 
   ~ServiceWorkerDataInfo()
   {}
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsString mScope;
   nsString mScriptSpec;
   nsString mCurrentWorkerURL;
   nsString mActiveCacheName;
   nsString mWaitingCacheName;
 };
+
 void
 ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
 {
   RemoveRegistrationInternal(aRegistration);
   MOZ_ASSERT(HasScope(aRegistration->mPrincipal, aRegistration->mScope));
   RemoveScopeAndRegistration(aRegistration);
 }
 
@@ -3996,36 +4116,71 @@ UnregisterIfMatchesHostPerPrincipal(cons
                                     ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
                                     void* aUserData)
 {
   UnregisterIfMatchesHostData data(aData, aUserData);
   aData->mInfos.EnumerateRead(UnregisterIfMatchesHost, &data);
   return PL_DHASH_NEXT;
 }
 
+PLDHashOperator
+GetAllRegistrationsEnumerator(const nsACString& aScope,
+                              ServiceWorkerRegistrationInfo* aReg,
+                              void* aData)
+{
+  nsIMutableArray* array = static_cast<nsIMutableArray*>(aData);
+  MOZ_ASSERT(aReg);
+
+  if (aReg->mPendingUninstall) {
+    return PL_DHASH_NEXT;
+  }
+
+  nsCOMPtr<nsIServiceWorkerInfo> info = ServiceWorkerDataInfo::Create(aReg);
+  if (NS_WARN_IF(!info)) {
+    return PL_DHASH_NEXT;
+  }
+
+  array->AppendElement(info, false);
+  return PL_DHASH_NEXT;
+}
+
+PLDHashOperator
+GetAllRegistrationsPerPrincipalEnumerator(const nsACString& aKey,
+                                          ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
+                                          void* aUserData)
+{
+  aData->mInfos.EnumerateRead(GetAllRegistrationsEnumerator, aUserData);
+  return PL_DHASH_NEXT;
+}
+
 } // anonymous namespace
+
 NS_IMPL_ISUPPORTS(ServiceWorkerDataInfo, nsIServiceWorkerInfo)
 
 /* static */ already_AddRefed<ServiceWorkerDataInfo>
-ServiceWorkerDataInfo::Create(const ServiceWorkerRegistrationData& aData)
+ServiceWorkerDataInfo::Create(const ServiceWorkerRegistrationInfo* aData)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aData);
 
   nsRefPtr<ServiceWorkerDataInfo> info = new ServiceWorkerDataInfo();
 
-  info->mPrincipal = PrincipalInfoToPrincipal(aData.principal());
-  if (!info->mPrincipal) {
-    return nullptr;
+  info->mPrincipal = aData->mPrincipal;
+  CopyUTF8toUTF16(aData->mScope, info->mScope);
+  CopyUTF8toUTF16(aData->mScriptSpec, info->mScriptSpec);
+
+  if (aData->mActiveWorker) {
+    CopyUTF8toUTF16(aData->mActiveWorker->ScriptSpec(),
+                    info->mCurrentWorkerURL);
+    info->mActiveCacheName = aData->mActiveWorker->CacheName();
   }
 
-  CopyUTF8toUTF16(aData.scope(), info->mScope);
-  CopyUTF8toUTF16(aData.scriptSpec(), info->mScriptSpec);
-  CopyUTF8toUTF16(aData.currentWorkerURL(), info->mCurrentWorkerURL);
-  info->mActiveCacheName = aData.activeCacheName();
-  info->mWaitingCacheName = aData.waitingCacheName();
+  if (aData->mWaitingWorker) {
+    info->mWaitingCacheName = aData->mWaitingWorker->CacheName();
+  }
 
   return info.forget();
 }
 
 NS_IMETHODIMP
 ServiceWorkerDataInfo::GetPrincipal(nsIPrincipal** aPrincipal)
 {
   AssertIsOnMainThread();
@@ -4073,36 +4228,22 @@ ServiceWorkerDataInfo::GetWaitingCacheNa
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
 {
   AssertIsOnMainThread();
 
-  nsRefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
-  MOZ_ASSERT(swr);
-
-  nsTArray<ServiceWorkerRegistrationData> data;
-  swr->GetRegistrations(data);
-
   nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
   if (!array) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
-    nsCOMPtr<nsIServiceWorkerInfo> info = ServiceWorkerDataInfo::Create(data[i]);
-    if (!info) {
-      return NS_ERROR_FAILURE;
-    }
-
-    array->AppendElement(info, false);
-  }
-
+  mRegistrationInfos.EnumerateRead(GetAllRegistrationsPerPrincipalEnumerator, array);
   array.forget(aResult);
   return NS_OK;
 }
 
 // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
 void
 ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
                                       ServiceWorkerRegistrationInfo* aRegistration)
@@ -4198,16 +4339,79 @@ ServiceWorkerManager::Observe(nsISupport
     }
   } else {
     MOZ_CRASH("Received message we aren't supposed to be registered for!");
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
+                                          const nsAString& aScope,
+                                          JSContext* aCx)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  OriginAttributes attrs;
+  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  PropagateSoftUpdate(attrs, aScope);
+  return NS_OK;
+}
+
+void
+ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
+                                          const nsAString& aScope)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mActor) {
+    nsRefPtr<nsIRunnable> runnable =
+      new PropagateSoftUpdateRunnable(aOriginAttributes, aScope);
+    AppendPendingOperation(runnable);
+    return;
+  }
+
+  mActor->SendPropagateSoftUpdate(aOriginAttributes, nsString(aScope));
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::PropagateUnregister(nsIPrincipal* aPrincipal,
+                                          nsIServiceWorkerUnregisterCallback* aCallback,
+                                          const nsAString& aScope)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+
+  if (!mActor) {
+    nsRefPtr<nsIRunnable> runnable =
+      new PropagateUnregisterRunnable(aPrincipal, aCallback, aScope);
+    AppendPendingOperation(runnable);
+    return NS_OK;
+  }
+
+  PrincipalInfo principalInfo;
+  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
+                                                    &principalInfo)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mActor->SendPropagateUnregister(principalInfo, nsString(aScope));
+
+  nsresult rv = Unregister(aPrincipal, aCallback, aScope);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 void
 ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
 {
   MOZ_ASSERT(aWorker);
 #ifdef DEBUG
   nsAutoString workerURL;
   aWorker->GetScriptURL(workerURL);
   MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -30,31 +30,28 @@
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsTObserverArray.h"
 
 namespace mozilla {
 
 class OriginAttributes;
 
-namespace ipc {
-class BackgroundChild;
-}
-
 namespace dom {
 
 class ServiceWorkerRegistrationListener;
 
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerClientInfo;
 class ServiceWorkerInfo;
 class ServiceWorkerJob;
 class ServiceWorkerJobQueue;
+class ServiceWorkerManagerChild;
 
 // Needs to inherit from nsISupports because NS_ProxyRelease() does not support
 // non-ISupports classes.
 class ServiceWorkerRegistrationInfo final : public nsISupports
 {
   uint32_t mControlledDocumentsCounter;
 
   virtual ~ServiceWorkerRegistrationInfo();
@@ -263,16 +260,17 @@ public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
 
   static ServiceWorkerManager* FactoryCreate()
   {
     AssertIsOnMainThread();
 
     ServiceWorkerManager* res = new ServiceWorkerManager;
     NS_ADDREF(res);
+    res->Initialize();
     return res;
   }
 
   struct RegistrationDataPerPrincipal;
   nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
 
   nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
 
@@ -292,16 +290,23 @@ public:
                      nsIDocument* aDoc,
                      nsIInterceptedChannel* aChannel,
                      bool aIsReload,
                      ErrorResult& aRv);
 
   void
   SoftUpdate(nsIPrincipal* aPrincipal, const nsACString& aScope);
 
+  void
+  SoftUpdate(const OriginAttributes& aOriginAttributes, const nsACString& aScope);
+
+  void
+  PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
+                      const nsAString& aScope);
+
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetRegistration(nsIPrincipal* aPrincipal, const nsACString& aScope) const;
 
   ServiceWorkerRegistrationInfo*
   CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal);
 
   void
   RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
@@ -336,16 +341,18 @@ public:
                    ServiceWorkerRegistrationInfo* aWorkerRegistration);
 
   nsresult
   ClaimClients(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aId);
 
   static already_AddRefed<ServiceWorkerManager>
   GetInstance();
 
+ void LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
+
  void LoadRegistrations(
                  const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
 
   // Used by remove() and removeAll() when clearing history.
   // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
   void
   ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
                   ServiceWorkerRegistrationInfo* aRegistration);
@@ -356,16 +363,19 @@ public:
 
   NS_IMETHOD
   RemoveRegistrationEventListener(const nsAString& aScope,
                                   ServiceWorkerRegistrationListener* aListener);
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
+  void
+  Initialize();
+
   ServiceWorkerJobQueue*
   GetOrCreateJobQueue(const nsACString& aOriginSuffix,
                       const nsACString& aScope);
 
   void
   MaybeRemoveRegistrationInfo(const nsACString& aOriginAttributesSuffix);
 
   void
@@ -506,17 +516,17 @@ private:
   // Does all cleanup except removing the registration from
   // mServiceWorkerRegistrationInfos. This is useful when we clear
   // registrations via remove()/removeAll() since we are iterating over the
   // hashtable and can cleanly remove within the hashtable enumeration
   // function.
   void
   RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration);
 
-  mozilla::ipc::PBackgroundChild* mActor;
+  nsRefPtr<ServiceWorkerManagerChild> mActor;
 
   struct PendingOperation;
   nsTArray<PendingOperation> mPendingOperations;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
                               NS_SERVICEWORKERMANAGER_IMPL_IID);
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerManagerChild.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ServiceWorkerManagerChild.h"
+#include "ServiceWorkerManager.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+namespace workers {
+
+bool
+ServiceWorkerManagerChild::RecvNotifyRegister(
+                                     const ServiceWorkerRegistrationData& aData)
+{
+  if (mShuttingDown) {
+    return true;
+  }
+
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  MOZ_ASSERT(swm);
+
+  swm->LoadRegistration(aData);
+  return true;
+}
+
+bool
+ServiceWorkerManagerChild::RecvNotifySoftUpdate(
+                                      const OriginAttributes& aOriginAttributes,
+                                      const nsString& aScope)
+{
+  if (mShuttingDown) {
+    return true;
+  }
+
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  MOZ_ASSERT(swm);
+
+  swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope));
+  return true;
+}
+
+bool
+ServiceWorkerManagerChild::RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo,
+                                                const nsString& aScope)
+{
+  if (mShuttingDown) {
+    return true;
+  }
+
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  MOZ_ASSERT(swm);
+
+  nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(aPrincipalInfo);
+  if (NS_WARN_IF(!principal)) {
+    return true;
+  }
+
+  nsresult rv = swm->Unregister(principal, nullptr, aScope);
+  unused << NS_WARN_IF(NS_FAILED(rv));
+  return true;
+}
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerManagerChild.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ServiceWorkerManagerChild_h
+#define mozilla_dom_ServiceWorkerManagerChild_h
+
+#include "mozilla/dom/PServiceWorkerManagerChild.h"
+#include "ipc/BasePrincipalIPC.h"
+
+namespace mozilla {
+
+class OriginAttributes;
+
+namespace ipc {
+class BackgroundChildImpl;
+}
+
+namespace dom {
+namespace workers {
+
+class ServiceWorkerManagerChild final : public PServiceWorkerManagerChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerManagerChild)
+
+  void ManagerShuttingDown()
+  {
+    mShuttingDown = true;
+  }
+
+  virtual bool RecvNotifyRegister(const ServiceWorkerRegistrationData& aData)
+                                                                       override;
+
+  virtual bool RecvNotifySoftUpdate(const OriginAttributes& aOriginAttributes,
+                                    const nsString& aScope) override;
+
+  virtual bool RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo,
+                                    const nsString& aScope) override;
+
+private:
+  ServiceWorkerManagerChild()
+    : mShuttingDown(false)
+  {}
+
+  ~ServiceWorkerManagerChild() {}
+
+  bool mShuttingDown;
+};
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_ServiceWorkerManagerChild_h
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerManagerParent.cpp
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ServiceWorkerManagerParent.h"
+#include "ServiceWorkerManagerService.h"
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+namespace workers {
+
+namespace {
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+}
+
+class RegisterServiceWorkerCallback final : public nsRunnable
+{
+public:
+  explicit RegisterServiceWorkerCallback(
+                                     const ServiceWorkerRegistrationData& aData)
+    : mData(aData)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    nsRefPtr<dom::ServiceWorkerRegistrar> service =
+      dom::ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->RegisterServiceWorker(mData);
+
+    nsRefPtr<ServiceWorkerManagerService> managerService =
+      ServiceWorkerManagerService::Get();
+    if (managerService) {
+      managerService->PropagateRegistration(mData);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ServiceWorkerRegistrationData mData;
+};
+
+class UnregisterServiceWorkerCallback final : public nsRunnable
+{
+public:
+  UnregisterServiceWorkerCallback(const PrincipalInfo& aPrincipalInfo,
+                                  const nsString& aScope)
+    : mPrincipalInfo(aPrincipalInfo)
+    , mScope(aScope)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    nsRefPtr<dom::ServiceWorkerRegistrar> service =
+      dom::ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->UnregisterServiceWorker(mPrincipalInfo,
+                                     NS_ConvertUTF16toUTF8(mScope));
+    return NS_OK;
+  }
+
+private:
+  const PrincipalInfo mPrincipalInfo;
+  nsString mScope;
+};
+
+class CheckPrincipalWithCallbackRunnable final : public nsRunnable
+{
+public:
+  CheckPrincipalWithCallbackRunnable(already_AddRefed<ContentParent> aParent,
+                                     const PrincipalInfo& aPrincipalInfo,
+                                     nsRunnable* aCallback)
+    : mContentParent(aParent)
+    , mPrincipalInfo(aPrincipalInfo)
+    , mCallback(aCallback)
+    , mBackgroundThread(NS_GetCurrentThread())
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    MOZ_ASSERT(mContentParent);
+    MOZ_ASSERT(mCallback);
+    MOZ_ASSERT(mBackgroundThread);
+  }
+
+  NS_IMETHODIMP Run()
+  {
+    if (NS_IsMainThread()) {
+      nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
+      AssertAppPrincipal(mContentParent, principal);
+      mContentParent = nullptr;
+
+      mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      return NS_OK;
+    }
+
+    AssertIsOnBackgroundThread();
+    mCallback->Run();
+    mCallback = nullptr;
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<ContentParent> mContentParent;
+  PrincipalInfo mPrincipalInfo;
+  nsRefPtr<nsRunnable> mCallback;
+  nsCOMPtr<nsIThread> mBackgroundThread;
+};
+
+} // anonymous namespace
+
+ServiceWorkerManagerParent::ServiceWorkerManagerParent()
+  : mService(ServiceWorkerManagerService::GetOrCreate())
+{
+  AssertIsOnBackgroundThread();
+  mService->RegisterActor(this);
+}
+
+ServiceWorkerManagerParent::~ServiceWorkerManagerParent()
+{
+  AssertIsOnBackgroundThread();
+}
+
+bool
+ServiceWorkerManagerParent::RecvRegister(
+                                     const ServiceWorkerRegistrationData& aData)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  // Basic validation.
+  if (aData.scope().IsEmpty() ||
+      aData.scriptSpec().IsEmpty() ||
+      aData.principal().type() == PrincipalInfo::TNullPrincipalInfo ||
+      aData.principal().type() == PrincipalInfo::TSystemPrincipalInfo) {
+    return false;
+  }
+
+  nsRefPtr<RegisterServiceWorkerCallback> callback =
+    new RegisterServiceWorkerCallback(aData);
+
+  nsRefPtr<ContentParent> parent =
+    BackgroundParent::GetContentParent(Manager());
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    callback->Run();
+    return true;
+  }
+
+  nsRefPtr<CheckPrincipalWithCallbackRunnable> runnable =
+    new CheckPrincipalWithCallbackRunnable(parent.forget(), aData.principal(),
+                                           callback);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
+  return true;
+}
+
+bool
+ServiceWorkerManagerParent::RecvUnregister(const PrincipalInfo& aPrincipalInfo,
+                                           const nsString& aScope)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  // Basic validation.
+  if (aScope.IsEmpty() ||
+      aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo ||
+      aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+    return false;
+  }
+
+  nsRefPtr<UnregisterServiceWorkerCallback> callback =
+    new UnregisterServiceWorkerCallback(aPrincipalInfo, aScope);
+
+  nsRefPtr<ContentParent> parent =
+    BackgroundParent::GetContentParent(Manager());
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    callback->Run();
+    return true;
+  }
+
+  nsRefPtr<CheckPrincipalWithCallbackRunnable> runnable =
+    new CheckPrincipalWithCallbackRunnable(parent.forget(), aPrincipalInfo,
+                                           callback);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
+  return true;
+}
+
+bool
+ServiceWorkerManagerParent::RecvPropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
+                                                    const nsString& aScope)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!mService)) {
+    return false;
+  }
+
+  mService->PropagateSoftUpdate(this, aOriginAttributes, aScope);
+  return true;
+}
+
+bool
+ServiceWorkerManagerParent::RecvPropagateUnregister(const PrincipalInfo& aPrincipalInfo,
+                                                    const nsString& aScope)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!mService)) {
+    return false;
+  }
+
+  mService->PropagateUnregister(this, aPrincipalInfo, aScope);
+  return true;
+}
+
+bool
+ServiceWorkerManagerParent::RecvShutdown()
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!mService)) {
+    return false;
+  }
+
+  mService->UnregisterActor(this);
+  mService = nullptr;
+
+  unused << Send__delete__(this);
+  return true;
+}
+
+void
+ServiceWorkerManagerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+
+  if (mService) {
+    // This object is about to be released and with it, also mService will be
+    // released too.
+    mService->UnregisterActor(this);
+  }
+}
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerManagerParent.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ServiceWorkerManagerParent_h
+#define mozilla_dom_ServiceWorkerManagerParent_h
+
+#include "mozilla/dom/PServiceWorkerManagerParent.h"
+
+namespace mozilla {
+
+class OriginAttributes;
+
+namespace ipc {
+class BackgroundParentImpl;
+}
+
+namespace dom {
+namespace workers {
+
+class ServiceWorkerManagerService;
+
+class ServiceWorkerManagerParent final : public PServiceWorkerManagerParent
+{
+  friend class mozilla::ipc::BackgroundParentImpl;
+
+private:
+  ServiceWorkerManagerParent();
+  ~ServiceWorkerManagerParent();
+
+  virtual bool RecvRegister(
+                           const ServiceWorkerRegistrationData& aData) override;
+
+  virtual bool RecvUnregister(const PrincipalInfo& aPrincipalInfo,
+                              const nsString& aScope) override;
+
+  virtual bool RecvPropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
+                                       const nsString& aScope) override;
+
+  virtual bool RecvPropagateUnregister(const PrincipalInfo& aPrincipalInfo,
+                                       const nsString& aScope) override;
+
+  virtual bool RecvShutdown() override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  nsRefPtr<ServiceWorkerManagerService> mService;
+};
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_ServiceWorkerManagerParent_h
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerManagerService.cpp
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ServiceWorkerManagerService.h"
+#include "ServiceWorkerManagerParent.h"
+#include "ServiceWorkerRegistrar.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+namespace workers {
+
+namespace {
+
+ServiceWorkerManagerService* sInstance = nullptr;
+
+} // anonymous namespace
+
+ServiceWorkerManagerService::ServiceWorkerManagerService()
+{
+  AssertIsOnBackgroundThread();
+
+  // sInstance is a raw ServiceWorkerManagerService*.
+  MOZ_ASSERT(!sInstance);
+  sInstance = this;
+}
+
+ServiceWorkerManagerService::~ServiceWorkerManagerService()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(sInstance == this);
+  MOZ_ASSERT(mAgents.Count() == 0);
+
+  sInstance = nullptr;
+}
+
+/* static */ already_AddRefed<ServiceWorkerManagerService>
+ServiceWorkerManagerService::Get()
+{
+  AssertIsOnBackgroundThread();
+
+  nsRefPtr<ServiceWorkerManagerService> instance = sInstance;
+  return instance.forget();
+}
+
+/* static */ already_AddRefed<ServiceWorkerManagerService>
+ServiceWorkerManagerService::GetOrCreate()
+{
+  AssertIsOnBackgroundThread();
+
+  nsRefPtr<ServiceWorkerManagerService> instance = sInstance;
+  if (!instance) {
+    instance = new ServiceWorkerManagerService();
+  }
+  return instance.forget();
+}
+
+void
+ServiceWorkerManagerService::RegisterActor(ServiceWorkerManagerParent* aParent)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(!mAgents.Contains(aParent));
+
+  mAgents.PutEntry(aParent);
+}
+
+void
+ServiceWorkerManagerService::UnregisterActor(ServiceWorkerManagerParent* aParent)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mAgents.Contains(aParent));
+
+  mAgents.RemoveEntry(aParent);
+}
+
+namespace {
+
+PLDHashOperator
+RegistrationEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
+{
+  AssertIsOnBackgroundThread();
+
+  auto* data = static_cast<ServiceWorkerRegistrationData*>(aPtr);
+
+  ServiceWorkerManagerParent* parent = aKey->GetKey();
+  MOZ_ASSERT(parent);
+
+  unused << parent->SendNotifyRegister(*data);
+  return PL_DHASH_NEXT;
+}
+
+struct MOZ_STACK_CLASS SoftUpdateData final
+{
+  SoftUpdateData(ServiceWorkerManagerParent* aParent,
+                 const OriginAttributes& aOriginAttributes,
+                 const nsAString& aScope)
+    : mParent(aParent)
+    , mOriginAttributes(aOriginAttributes)
+    , mScope(aScope)
+  {
+    MOZ_ASSERT(aParent);
+    MOZ_COUNT_CTOR(SoftUpdateData);
+  }
+
+  ~SoftUpdateData()
+  {
+    MOZ_COUNT_DTOR(SoftUpdateData);
+  }
+
+  ServiceWorkerManagerParent* mParent;
+  const OriginAttributes& mOriginAttributes;
+  nsString mScope;
+};
+
+PLDHashOperator
+SoftUpdateEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
+{
+  AssertIsOnBackgroundThread();
+
+  auto* data = static_cast<SoftUpdateData*>(aPtr);
+  ServiceWorkerManagerParent* parent = aKey->GetKey();
+  MOZ_ASSERT(parent);
+
+  if (parent != data->mParent) {
+    unused <<parent->SendNotifySoftUpdate(data->mOriginAttributes,
+                                          data->mScope);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+struct MOZ_STACK_CLASS UnregisterData final
+{
+  UnregisterData(ServiceWorkerManagerParent* aParent,
+                 const PrincipalInfo& aPrincipalInfo,
+                 const nsAString& aScope)
+    : mParent(aParent)
+    , mPrincipalInfo(aPrincipalInfo)
+    , mScope(aScope)
+  {
+    MOZ_ASSERT(aParent);
+    MOZ_COUNT_CTOR(UnregisterData);
+  }
+
+  ~UnregisterData()
+  {
+    MOZ_COUNT_DTOR(UnregisterData);
+  }
+
+  ServiceWorkerManagerParent* mParent;
+  PrincipalInfo mPrincipalInfo;
+  nsString mScope;
+};
+
+PLDHashOperator
+UnregisterEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
+{
+  AssertIsOnBackgroundThread();
+
+  auto* data = static_cast<UnregisterData*>(aPtr);
+  ServiceWorkerManagerParent* parent = aKey->GetKey();
+  MOZ_ASSERT(parent);
+
+  if (parent != data->mParent) {
+    unused << parent->SendNotifyUnregister(data->mPrincipalInfo, data->mScope);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+} // anonymous namespce
+
+void
+ServiceWorkerManagerService::PropagateRegistration(
+                                           ServiceWorkerRegistrationData& aData)
+{
+  AssertIsOnBackgroundThread();
+  mAgents.EnumerateEntries(RegistrationEnumerator, &aData);
+}
+
+void
+ServiceWorkerManagerService::PropagateSoftUpdate(
+                                      ServiceWorkerManagerParent* aParent,
+                                      const OriginAttributes& aOriginAttributes,
+                                      const nsAString& aScope)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mAgents.Contains(aParent));
+
+  SoftUpdateData data(aParent, aOriginAttributes, aScope);
+  mAgents.EnumerateEntries(SoftUpdateEnumerator, &data);
+}
+
+void
+ServiceWorkerManagerService::PropagateUnregister(
+                                            ServiceWorkerManagerParent* aParent,
+                                            const PrincipalInfo& aPrincipalInfo,
+                                            const nsAString& aScope)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mAgents.Contains(aParent));
+
+  nsRefPtr<dom::ServiceWorkerRegistrar> service =
+    dom::ServiceWorkerRegistrar::Get();
+  MOZ_ASSERT(service);
+
+  // It's possible that we don't have any ServiceWorkerManager managing this
+  // scope but we still need to unregister it from the ServiceWorkerRegistrar.
+  service->UnregisterServiceWorker(aPrincipalInfo,
+                                   NS_ConvertUTF16toUTF8(aScope));
+
+  UnregisterData data(aParent, aPrincipalInfo, aScope);
+  mAgents.EnumerateEntries(UnregisterEnumerator, &data);
+}
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerManagerService.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ServiceWorkerManagerService_h
+#define mozilla_dom_ServiceWorkerManagerService_h
+
+#include "nsISupportsImpl.h"
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+
+class OriginAttributes;
+
+namespace ipc {
+class PrincipalInfo;
+}
+
+namespace dom {
+
+class ServiceWorkerRegistrationData;
+
+namespace workers {
+
+class ServiceWorkerManagerParent;
+
+class ServiceWorkerManagerService final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerManagerService)
+
+  static already_AddRefed<ServiceWorkerManagerService> Get();
+  static already_AddRefed<ServiceWorkerManagerService> GetOrCreate();
+
+  void RegisterActor(ServiceWorkerManagerParent* aParent);
+  void UnregisterActor(ServiceWorkerManagerParent* aParent);
+
+  void PropagateRegistration(ServiceWorkerRegistrationData& aData);
+
+  void PropagateSoftUpdate(ServiceWorkerManagerParent* aParent,
+                           const OriginAttributes& aOriginAttributes,
+                           const nsAString& aScope);
+
+  void PropagateUnregister(ServiceWorkerManagerParent* aParent,
+                           const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+                           const nsAString& aScope);
+
+private:
+  ServiceWorkerManagerService();
+  ~ServiceWorkerManagerService();
+
+  nsTHashtable<nsPtrHashKey<ServiceWorkerManagerParent>> mAgents;
+};
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_ServiceWorkerManagerService_h
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -64,16 +64,19 @@ UNIFIED_SOURCES += [
     'RuntimeService.cpp',
     'ScriptLoader.cpp',
     'ServiceWorker.cpp',
     'ServiceWorkerClient.cpp',
     'ServiceWorkerClients.cpp',
     'ServiceWorkerContainer.cpp',
     'ServiceWorkerEvents.cpp',
     'ServiceWorkerManager.cpp',
+    'ServiceWorkerManagerChild.cpp',
+    'ServiceWorkerManagerParent.cpp',
+    'ServiceWorkerManagerService.cpp',
     'ServiceWorkerPeriodicUpdater.cpp',
     'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'ServiceWorkerScriptCache.cpp',
     'ServiceWorkerWindowClient.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
     'WorkerDebuggerManager.cpp',
@@ -81,16 +84,17 @@ UNIFIED_SOURCES += [
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
     'XMLHttpRequest.cpp',
     'XMLHttpRequestUpload.cpp',
 ]
 
 IPDL_SOURCES += [
+    'PServiceWorkerManager.ipdl',
     'ServiceWorkerRegistrarTypes.ipdlh',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundChildImpl.h"
 
 #include "ActorsChild.h" // IndexedDB
 #include "BroadcastChannelChild.h"
+#include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
@@ -234,16 +235,38 @@ BackgroundChildImpl::DeallocPBroadcastCh
 {
   nsRefPtr<dom::BroadcastChannelChild> child =
     dont_AddRef(static_cast<dom::BroadcastChannelChild*>(aActor));
   MOZ_ASSERT(child);
   return true;
 }
 
 // -----------------------------------------------------------------------------
+// ServiceWorkerManager
+// -----------------------------------------------------------------------------
+
+dom::PServiceWorkerManagerChild*
+BackgroundChildImpl::AllocPServiceWorkerManagerChild()
+{
+  nsRefPtr<dom::workers::ServiceWorkerManagerChild> agent =
+    new dom::workers::ServiceWorkerManagerChild();
+  return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPServiceWorkerManagerChild(
+                                             PServiceWorkerManagerChild* aActor)
+{
+  nsRefPtr<dom::workers::ServiceWorkerManagerChild> child =
+    dont_AddRef(static_cast<dom::workers::ServiceWorkerManagerChild*>(aActor));
+  MOZ_ASSERT(child);
+  return true;
+}
+
+// -----------------------------------------------------------------------------
 // Cache API
 // -----------------------------------------------------------------------------
 
 PCacheStorageChild*
 BackgroundChildImpl::AllocPCacheStorageChild(const Namespace& aNamespace,
                                              const PrincipalInfo& aPrincipalInfo)
 {
   MOZ_CRASH("CacheStorageChild actor must be provided to PBackground manager");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -87,16 +87,22 @@ protected:
   AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
                               const nsString& aOrigin,
                               const nsString& aChannel,
                               const bool& aPrivateBrowsing) override;
 
   virtual bool
   DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) override;
 
+  virtual PServiceWorkerManagerChild*
+  AllocPServiceWorkerManagerChild() override;
+
+  virtual bool
+  DeallocPServiceWorkerManagerChild(PServiceWorkerManagerChild* aActor) override;
+
   virtual dom::cache::PCacheStorageChild*
   AllocPCacheStorageChild(const dom::cache::Namespace& aNamespace,
                           const PrincipalInfo& aPrincipalInfo) override;
 
   virtual bool
   DeallocPCacheStorageChild(dom::cache::PCacheStorageChild* aActor) override;
 
   virtual dom::cache::PCacheChild* AllocPCacheChild() override;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsRefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
+#include "ServiceWorkerManagerParent.h"
 
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
 #else
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false)
 #endif
 
 using mozilla::ipc::AssertIsOnBackgroundThread;
@@ -75,16 +76,17 @@ public:
 } // anonymous namespace
 
 namespace mozilla {
 namespace ipc {
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::BroadcastChannelParent;
 using mozilla::dom::ServiceWorkerRegistrationData;
+using mozilla::dom::workers::ServiceWorkerManagerParent;
 
 BackgroundParentImpl::BackgroundParentImpl()
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
 
   MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
 }
@@ -375,207 +377,49 @@ BackgroundParentImpl::DeallocPBroadcastC
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<BroadcastChannelParent*>(aActor);
   return true;
 }
 
+mozilla::dom::PServiceWorkerManagerParent*
+BackgroundParentImpl::AllocPServiceWorkerManagerParent()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return new ServiceWorkerManagerParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPServiceWorkerManagerParent(
+                                            PServiceWorkerManagerParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<ServiceWorkerManagerParent*>(aActor);
+  return true;
+}
+
 media::PMediaParent*
 BackgroundParentImpl::AllocPMediaParent()
 {
   return media::AllocPMediaParent();
 }
 
 bool
 BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
 {
   return media::DeallocPMediaParent(aActor);
 }
 
-namespace {
-
-class RegisterServiceWorkerCallback final : public nsRunnable
-{
-public:
-  explicit RegisterServiceWorkerCallback(
-                                     const ServiceWorkerRegistrationData& aData)
-    : mData(aData)
-  {
-    AssertIsInMainProcess();
-    AssertIsOnBackgroundThread();
-  }
-
-  NS_IMETHODIMP
-  Run()
-  {
-    AssertIsInMainProcess();
-    AssertIsOnBackgroundThread();
-
-    nsRefPtr<dom::ServiceWorkerRegistrar> service =
-      dom::ServiceWorkerRegistrar::Get();
-    MOZ_ASSERT(service);
-
-    service->RegisterServiceWorker(mData);
-    return NS_OK;
-  }
-
-private:
-  ServiceWorkerRegistrationData mData;
-};
-
-class UnregisterServiceWorkerCallback final : public nsRunnable
-{
-public:
-  UnregisterServiceWorkerCallback(const PrincipalInfo& aPrincipalInfo,
-                                  const nsString& aScope)
-    : mPrincipalInfo(aPrincipalInfo)
-    , mScope(aScope)
-  {
-    AssertIsInMainProcess();
-    AssertIsOnBackgroundThread();
-  }
-
-  NS_IMETHODIMP
-  Run()
-  {
-    AssertIsInMainProcess();
-    AssertIsOnBackgroundThread();
-
-    nsRefPtr<dom::ServiceWorkerRegistrar> service =
-      dom::ServiceWorkerRegistrar::Get();
-    MOZ_ASSERT(service);
-
-    service->UnregisterServiceWorker(mPrincipalInfo,
-                                     NS_ConvertUTF16toUTF8(mScope));
-    return NS_OK;
-  }
-
-private:
-  const PrincipalInfo mPrincipalInfo;
-  nsString mScope;
-};
-
-class CheckPrincipalWithCallbackRunnable final : public nsRunnable
-{
-public:
-  CheckPrincipalWithCallbackRunnable(already_AddRefed<ContentParent> aParent,
-                                     const PrincipalInfo& aPrincipalInfo,
-                                     nsRunnable* aCallback)
-    : mContentParent(aParent)
-    , mPrincipalInfo(aPrincipalInfo)
-    , mCallback(aCallback)
-    , mBackgroundThread(NS_GetCurrentThread())
-  {
-    AssertIsInMainProcess();
-    AssertIsOnBackgroundThread();
-
-    MOZ_ASSERT(mContentParent);
-    MOZ_ASSERT(mCallback);
-    MOZ_ASSERT(mBackgroundThread);
-  }
-
-  NS_IMETHODIMP Run()
-  {
-    if (NS_IsMainThread()) {
-      nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
-      AssertAppPrincipal(mContentParent, principal);
-      mContentParent = nullptr;
-
-      mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL);
-      return NS_OK;
-    }
-
-    AssertIsOnBackgroundThread();
-    mCallback->Run();
-    mCallback = nullptr;
-
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<ContentParent> mContentParent;
-  PrincipalInfo mPrincipalInfo;
-  nsRefPtr<nsRunnable> mCallback;
-  nsCOMPtr<nsIThread> mBackgroundThread;
-};
-
-} // anonymous namespace
-
-bool
-BackgroundParentImpl::RecvRegisterServiceWorker(
-                                     const ServiceWorkerRegistrationData& aData)
-{
-  AssertIsInMainProcess();
-  AssertIsOnBackgroundThread();
-
-  // Basic validation.
-  if (aData.scope().IsEmpty() ||
-      aData.scriptSpec().IsEmpty() ||
-      aData.principal().type() == PrincipalInfo::TNullPrincipalInfo ||
-      aData.principal().type() == PrincipalInfo::TSystemPrincipalInfo) {
-    return false;
-  }
-
-  nsRefPtr<RegisterServiceWorkerCallback> callback =
-    new RegisterServiceWorkerCallback(aData);
-
-  nsRefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
-
-  // If the ContentParent is null we are dealing with a same-process actor.
-  if (!parent) {
-    callback->Run();
-    return true;
-  }
-
-  nsRefPtr<CheckPrincipalWithCallbackRunnable> runnable =
-    new CheckPrincipalWithCallbackRunnable(parent.forget(), aData.principal(),
-                                           callback);
-  nsresult rv = NS_DispatchToMainThread(runnable);
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
-
-  return true;
-}
-
-bool
-BackgroundParentImpl::RecvUnregisterServiceWorker(
-                                            const PrincipalInfo& aPrincipalInfo,
-                                            const nsString& aScope)
-{
-  AssertIsInMainProcess();
-  AssertIsOnBackgroundThread();
-
-  // Basic validation.
-  if (aScope.IsEmpty() ||
-      aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo ||
-      aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
-    return false;
-  }
-
-  nsRefPtr<UnregisterServiceWorkerCallback> callback =
-    new UnregisterServiceWorkerCallback(aPrincipalInfo, aScope);
-
-  nsRefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
-
-  // If the ContentParent is null we are dealing with a same-process actor.
-  if (!parent) {
-    callback->Run();
-    return true;
-  }
-
-  nsRefPtr<CheckPrincipalWithCallbackRunnable> runnable =
-    new CheckPrincipalWithCallbackRunnable(parent.forget(), aPrincipalInfo,
-                                           callback);
-  nsresult rv = NS_DispatchToMainThread(runnable);
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
-
-  return true;
-}
-
 bool
 BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar()
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   if (BackgroundParent::IsOtherProcessActor(this)) {
     return false;
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -81,31 +81,29 @@ protected:
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsString& origin,
                                    const nsString& channel,
                                    const bool& aPrivateBrowsing) override;
 
   virtual bool
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
 
+  virtual PServiceWorkerManagerParent*
+  AllocPServiceWorkerManagerParent() override;
+
+  virtual bool
+  DeallocPServiceWorkerManagerParent(PServiceWorkerManagerParent* aActor) override;
+
   virtual PMediaParent*
   AllocPMediaParent() override;
 
   virtual bool
   DeallocPMediaParent(PMediaParent* aActor) override;
 
   virtual bool
-  RecvRegisterServiceWorker(const ServiceWorkerRegistrationData& aData)
-                            override;
-
-  virtual bool
-  RecvUnregisterServiceWorker(const PrincipalInfo& aPrincipalInfo,
-                              const nsString& aScope) override;
-
-  virtual bool
   RecvShutdownServiceWorkerRegistrar() override;
 
   virtual dom::cache::PCacheStorageParent*
   AllocPCacheStorageParent(const dom::cache::Namespace& aNamespace,
                            const PrincipalInfo& aPrincipalInfo) override;
 
   virtual bool
   DeallocPCacheStorageParent(dom::cache::PCacheStorageParent* aActor) override;
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -5,23 +5,23 @@
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PServiceWorkerManager;
 include protocol PVsync;
 include protocol PMedia;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
-include ServiceWorkerRegistrarTypes;
 
 using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h";
 include "mozilla/dom/cache/IPCUtils.h";
 
 namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
@@ -29,34 +29,34 @@ sync protocol PBackground
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
+  manages PServiceWorkerManager;
   manages PVsync;
   manages PMedia;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
   PMedia();
 
   PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel,
                     bool privateBrowsing);
 
-  RegisterServiceWorker(ServiceWorkerRegistrationData data);
-  UnregisterServiceWorker(PrincipalInfo principalInfo,
-                          nsString scope);
+  PServiceWorkerManager();
+
   ShutdownServiceWorkerRegistrar();
 
   PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
 
 child:
   PCache();
   PCacheStreamControl();
 
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -128,16 +128,17 @@ SOURCES += [
     'GeckoChildProcessHost.cpp',
     'URIUtils.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/caps',
     '/dom/broadcastchannel',
     '/dom/indexedDB',
+    '/dom/workers',
     '/xpcom/build',
 ]
 
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'PBackground.ipdl',
     'PBackgroundSharedTypes.ipdlh',
     'PBackgroundTest.ipdl',
--- a/toolkit/content/aboutServiceWorkers.js
+++ b/toolkit/content/aboutServiceWorkers.js
@@ -127,18 +127,17 @@ function display(info) {
     error => {
       dump("about:serviceworkers - push registration failed\n");
     }
   );
 
   let updateButton = document.createElement("button");
   updateButton.appendChild(document.createTextNode(bundle.GetStringFromName('update')));
   updateButton.onclick = function() {
-    gSWM.softUpdate(info.principal.appId, info.principal.isInBrowserElement,
-                    info.scope);
+    gSWM.propagateSoftUpdate(info.principal.originAttributes, info.scope);
   };
   div.appendChild(updateButton);
 
   let unregisterButton = document.createElement("button");
   unregisterButton.appendChild(document.createTextNode(bundle.GetStringFromName('unregister')));
   div.appendChild(unregisterButton);
 
   let loadingMessage = document.createElement('span');
@@ -160,17 +159,17 @@ function display(info) {
       unregisterFailed: function() {
         alert(bundle.GetStringFromName('unregisterError'));
       },
 
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIServiceWorkerUnregisterCallback])
     };
 
     loadingMessage.classList.remove('inactive');
-    gSWM.unregister(info.principal, cb, info.scope);
+    gSWM.propagateUnregister(info.principal, cb, info.scope);
   };
 
   let sep = document.createElement('hr');
   div.appendChild(sep);
 
   ++gSWCount;
 }