Bug 984050 - Persist ServiceWorkerRegistrations. r=bent, r=nsm
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 11 Feb 2015 06:53:00 -0500
changeset 228753 b682e16c46f22e173b253ce2bcaad4ce63876a8c
parent 228752 7abb1cb9739ecf8e781c093876785f8824892754
child 228754 d9df89f77ab16747a0f306d8888e432c91470152
push id28271
push usercbook@mozilla.com
push dateThu, 12 Feb 2015 14:33:39 +0000
treeherdermozilla-central@81f979b17fbd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent, nsm
bugs984050
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 984050 - Persist ServiceWorkerRegistrations. r=bent, r=nsm
dom/interfaces/base/nsIServiceWorkerManager.idl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/ipc/moz.build
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerRegistrar.cpp
dom/workers/ServiceWorkerRegistrar.h
dom/workers/ServiceWorkerRegistrarTypes.ipdlh
dom/workers/ServiceWorkerRegistration.cpp
dom/workers/WorkerScope.cpp
dom/workers/moz.build
dom/workers/test/gtest/TestReadWrite.cpp
dom/workers/test/gtest/moz.build
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
layout/build/nsLayoutStatics.cpp
toolkit/components/telemetry/Histograms.json
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -1,45 +1,47 @@
 /* -*- Mode: IDL; 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/. */
 
 #include "domstubs.idl"
 
 interface nsIDocument;
+interface nsIPrincipal;
 interface nsIURI;
 
 [builtinclass, uuid(d4367ffe-e435-4195-95f8-0a51b1bbfdfb)]
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   [noscript] void UnregisterSucceeded(in bool aState);
   [noscript] void UnregisterFailed();
 };
 
-[builtinclass, uuid(430f02bf-63d0-44ab-9a59-7bd49c608949)]
+[builtinclass, uuid(861b55e9-d6ac-47cf-a528-8590e9b44de6)]
 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.
    *
    * Returns a Promise.
    */
   nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI);
 
   /**
    * Unregister an existing ServiceWorker registration for `aScope`.
    * It keeps aCallback alive until the operation is concluded.
    */
-  void unregister(in nsIServiceWorkerUnregisterCallback aCallback,
+  void unregister(in nsIPrincipal aPrincipal,
+                  in nsIServiceWorkerUnregisterCallback aCallback,
                   in DOMString aScope);
 
   // Returns a Promise
   nsISupports getRegistrations(in nsIDOMWindow aWindow);
 
   // Returns a Promise
   nsISupports getRegistration(in nsIDOMWindow aWindow, in DOMString aScope);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -14,16 +14,17 @@ include protocol PContentPermissionReque
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PRenderFrame;
 include protocol PPluginWidget;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
 include PContentPermission;
+include ServiceWorkerRegistrarTypes;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
 using LayoutDeviceIntRect from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
@@ -60,16 +61,21 @@ namespace dom {
 
 struct NativeKeyBinding
 {
   CommandInt[] singleLineCommands;
   CommandInt[] multiLineCommands;
   CommandInt[] richTextCommands;
 };
 
+struct BrowserConfiguration
+{
+  ServiceWorkerRegistrationData[] serviceWorkerRegistrations;
+};
+
 union MaybeNativeKeyBinding
 {
   NativeKeyBinding;
   void_t;
 };
 
 struct ShowInfo
 {
@@ -466,17 +472,17 @@ child:
     Show(nsIntSize size,
          ShowInfo info,
          ScrollingBehavior scrolling,
          TextureFactoryIdentifier textureFactoryIdentifier,
          uint64_t layersId,
          nullable PRenderFrame renderFrame,
          bool parentIsActive);
 
-    LoadURL(nsCString uri);
+    LoadURL(nsCString uri, BrowserConfiguration config);
 
     CacheFileDescriptor(nsString path, FileDescriptor fd);
 
     UpdateDimensions(nsIntRect rect, nsIntSize size, ScreenOrientation orientation,
                      nsIntPoint chromeDisp) compress;
 
     UpdateFrame(FrameMetrics frame);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -10,16 +10,17 @@
 
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/IntentionalCrash.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/plugins/PluginWidgetChild.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/layers/ActiveElementManager.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorChild.h"
@@ -94,16 +95,17 @@
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
+using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
 
 NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
@@ -1561,17 +1563,17 @@ TabChild::ProvideWindowCommon(nsIDOMWind
   for (size_t i = 0; i < frameScripts.Length(); i++) {
     FrameScriptInfo& info = frameScripts[i];
     if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
       MOZ_CRASH();
     }
   }
 
   if (!urlToLoad.IsEmpty()) {
-    newChild->RecvLoadURL(urlToLoad);
+    newChild->RecvLoadURL(urlToLoad, BrowserConfiguration());
   }
 
   nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->WebNavigation());
   win.forget(aReturn);
   return NS_OK;
 }
 
 bool
@@ -1712,32 +1714,37 @@ TabChild::IsRootContentDocument()
     // We do this because we make a remote frame opaque iff
     // IsRootContentDocument(), and making vanilla remote frames transparent
     // breaks our remote reftests.
 
     return !HasAppOwnerApp();
 }
 
 bool
-TabChild::RecvLoadURL(const nsCString& uri)
+TabChild::RecvLoadURL(const nsCString& aURI,
+                      const BrowserConfiguration& aConfiguration)
 {
     SetProcessNameToAppName();
 
-    nsresult rv = WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(uri).get(),
+    nsresult rv = WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(),
                                            nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                                            nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER,
                                            nullptr, nullptr, nullptr);
     if (NS_FAILED(rv)) {
         NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?");
     }
 
 #ifdef MOZ_CRASHREPORTER
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), uri);
+    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), aURI);
 #endif
 
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    MOZ_ASSERT(swm);
+    swm->LoadRegistrations(aConfiguration.serviceWorkerRegistrations());
+
     return true;
 }
 
 bool
 TabChild::RecvCacheFileDescriptor(const nsString& aPath,
                                   const FileDescriptor& aFileDescriptor)
 {
     MOZ_ASSERT(NS_IsMainThread());
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -307,17 +307,18 @@ public:
                                     const nsAString& aMessage,
                                     const mozilla::dom::StructuredCloneData& aData,
                                     JS::Handle<JSObject *> aCpows,
                                     nsIPrincipal* aPrincipal) MOZ_OVERRIDE;
     virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
                                          const ViewID& aViewId,
                                          const bool& aIsRoot,
                                          const ZoomConstraints& aConstraints) MOZ_OVERRIDE;
-    virtual bool RecvLoadURL(const nsCString& uri) MOZ_OVERRIDE;
+    virtual bool RecvLoadURL(const nsCString& aURI,
+                             const BrowserConfiguration& aConfiguration) MOZ_OVERRIDE;
     virtual bool RecvCacheFileDescriptor(const nsString& aPath,
                                          const FileDescriptor& aFileDescriptor)
                                          MOZ_OVERRIDE;
     virtual bool RecvShow(const nsIntSize& aSize,
                           const ShowInfo& aInfo,
                           const ScrollingBehavior& aScrolling,
                           const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                           const uint64_t& aLayersId,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -8,16 +8,17 @@
 
 #include "TabParent.h"
 
 #include "AppProcessChecker.h"
 #include "mozIApplication.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/plugins/PluginWidgetParent.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/InputAPZContext.h"
@@ -40,16 +41,17 @@
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadInfo.h"
+#include "nsPrincipal.h"
 #include "nsIPromptFactory.h"
 #include "nsIURI.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIWindowCreator2.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
 #include "nsIRemoteBrowser.h"
 #include "nsViewManager.h"
@@ -688,16 +690,29 @@ TabParent::SendLoadRemoteScript(const ns
     mDelayedFrameScripts.AppendElement(FrameScriptInfo(aURL, aRunInGlobalScope));
     return true;
   }
 
   MOZ_ASSERT(mDelayedFrameScripts.IsEmpty());
   return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope);
 }
 
+bool
+TabParent::InitBrowserConfiguration(nsIURI* aURI,
+                                    BrowserConfiguration& aConfiguration)
+{
+  // Get the list of ServiceWorkerRegistation for this origin.
+  nsRefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+  MOZ_ASSERT(swr);
+
+  swr->GetRegistrations(aConfiguration.serviceWorkerRegistrations());
+
+  return true;
+}
+
 void
 TabParent::LoadURL(nsIURI* aURI)
 {
     MOZ_ASSERT(aURI);
 
     if (mIsDestroyed) {
         return;
     }
@@ -722,17 +737,23 @@ TabParent::LoadURL(nsIURI* aURI)
     uint32_t appId = OwnOrContainingAppId();
     if (mSendOfflineStatus && NS_IsAppOffline(appId)) {
       // If the app is offline in the parent process
       // pass that state to the child process as well
       unused << SendAppOfflineStatus(appId, true);
     }
     mSendOfflineStatus = false;
 
-    unused << SendLoadURL(spec);
+    // This object contains the configuration for this new app.
+    BrowserConfiguration configuration;
+    if (NS_WARN_IF(!InitBrowserConfiguration(aURI, configuration))) {
+      return;
+    }
+
+    unused << SendLoadURL(spec, configuration);
 
     // If this app is a packaged app then we can speed startup by sending over
     // the file descriptor for the "application.zip" file that it will
     // invariably request. Only do this once.
     if (!mAppPackageFileDescriptorSent) {
         mAppPackageFileDescriptorSent = true;
 
         nsCOMPtr<mozIApplication> app = GetOwnOrContainingApp();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -398,16 +398,19 @@ protected:
                                         uint64_t* aLayersId) MOZ_OVERRIDE;
 
     virtual bool RecvSetDimensions(const uint32_t& aFlags,
                                    const int32_t& aX, const int32_t& aY,
                                    const int32_t& aCx, const int32_t& aCy) MOZ_OVERRIDE;
 
     bool SendCompositionChangeEvent(mozilla::WidgetCompositionEvent& event);
 
+    bool InitBrowserConfiguration(nsIURI* aURI,
+                                  BrowserConfiguration& aConfiguration);
+
     // IME
     static TabParent *mIMETabParent;
     nsString mIMECacheText;
     uint32_t mIMESelectionAnchor;
     uint32_t mIMESelectionFocus;
     mozilla::WritingMode mWritingMode;
     bool mIMEComposing;
     bool mIMECompositionEnding;
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -107,16 +107,17 @@ IPDL_SOURCES += [
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
+    '/caps',
     '/chrome',
     '/docshell/base',
     '/dom/base',
     '/dom/bluetooth',
     '/dom/bluetooth/ipc',
     '/dom/devicestorage',
     '/dom/filesystem',
     '/dom/fmradio/ipc',
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1,14 +1,15 @@
 /* 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 "ServiceWorkerManager.h"
 
+#include "nsIAppsService.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsPIDOMWindow.h"
 
@@ -16,16 +17,19 @@
 
 #include "mozilla/LoadContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/InstallEventBinding.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 
 #include "RuntimeService.h"
@@ -40,19 +44,68 @@
 #include "WorkerScope.h"
 
 #ifdef PostMessage
 #undef PostMessage
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
 
 BEGIN_WORKERS_NAMESPACE
 
+struct ServiceWorkerManager::PendingOperation
+{
+  nsCOMPtr<nsIRunnable> mRunnable;
+
+  ServiceWorkerJobQueue* mQueue;
+  nsRefPtr<ServiceWorkerJob> mJob;
+
+  ServiceWorkerRegistrationData mRegistration;
+};
+
+namespace {
+
+nsresult
+PopulateRegistrationData(nsIPrincipal* aPrincipal,
+                         const ServiceWorkerRegistrationInfo* aRegistration,
+                         ServiceWorkerRegistrationData& aData)
+{
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aRegistration);
+
+  bool isNullPrincipal = true;
+  nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // No null principals.
+  if (NS_WARN_IF(isNullPrincipal)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aData.scope() = aRegistration->mScope;
+  aData.scriptSpec() = aRegistration->mScriptSpec;
+
+  if (aRegistration->mActiveWorker) {
+    aData.currentWorkerURL() = aRegistration->mActiveWorker->ScriptSpec();
+  }
+
+  return NS_OK;
+}
+
+} // Anonymous namespace
+
 NS_IMPL_ISUPPORTS0(ServiceWorkerJob)
 NS_IMPL_ISUPPORTS0(ServiceWorkerRegistrationInfo)
 
 void
 ServiceWorkerJob::Done(nsresult aStatus)
 {
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
     // Windows builds complain if the return value of NS_WARN_IF isn't used.
@@ -88,20 +141,22 @@ ServiceWorkerRegistrationInfo::Clear()
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->InvalidateServiceWorkerRegistrationWorker(this,
                                                  WhichServiceWorker::INSTALLING_WORKER |
                                                  WhichServiceWorker::WAITING_WORKER |
                                                  WhichServiceWorker::ACTIVE_WORKER);
 }
 
-ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope)
-  : mControlledDocumentsCounter(0),
-    mScope(aScope),
-    mPendingUninstall(false)
+ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
+                                                             nsIPrincipal* aPrincipal)
+  : mControlledDocumentsCounter(0)
+  , mScope(aScope)
+  , mPrincipal(aPrincipal)
+  , mPendingUninstall(false)
 { }
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
   }
 }
@@ -110,24 +165,37 @@ ServiceWorkerRegistrationInfo::~ServiceW
 // ServiceWorkerManager //
 //////////////////////////
 
 NS_IMPL_ADDREF(ServiceWorkerManager)
 NS_IMPL_RELEASE(ServiceWorkerManager)
 
 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
   NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
   if (aIID.Equals(NS_GET_IID(ServiceWorkerManager)))
     foundInterface = static_cast<nsIServiceWorkerManager*>(this);
   else
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
 NS_INTERFACE_MAP_END
 
 ServiceWorkerManager::ServiceWorkerManager()
+  : mActor(nullptr)
 {
+  // 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;
+    swr->GetRegistrations(data);
+    LoadRegistrations(data);
+  }
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mServiceWorkerRegistrationInfos.Clear();
 }
 
@@ -375,16 +443,17 @@ class ServiceWorkerRegisterJob MOZ_FINAL
                                            public nsIStreamLoaderObserver
 {
   friend class ContinueInstallTask;
 
   nsCString mScope;
   nsCString mScriptSpec;
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   nsRefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
 
   ~ServiceWorkerRegisterJob()
   { }
 
   enum
   {
     REGISTER_JOB = 0,
     UPDATE_JOB = 1,
@@ -392,55 +461,67 @@ class ServiceWorkerRegisterJob MOZ_FINAL
 
 public:
   NS_DECL_ISUPPORTS
 
   // [[Register]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            const nsCString& aScope,
                            const nsCString& aScriptSpec,
-                           ServiceWorkerUpdateFinishCallback* aCallback)
+                           ServiceWorkerUpdateFinishCallback* aCallback,
+                           nsIPrincipal* aPrincipal)
     : ServiceWorkerJob(aQueue)
     , mScope(aScope)
     , mScriptSpec(aScriptSpec)
     , mCallback(aCallback)
+    , mPrincipal(aPrincipal)
     , mJobType(REGISTER_JOB)
   { }
 
   // [[Update]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            ServiceWorkerRegistrationInfo* aRegistration,
                            ServiceWorkerUpdateFinishCallback* aCallback)
     : ServiceWorkerJob(aQueue)
     , mRegistration(aRegistration)
     , mCallback(aCallback)
     , mJobType(UPDATE_JOB)
   { }
 
   void
   Start() MOZ_OVERRIDE
   {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (!swm->HasBackgroundActor()) {
+      nsCOMPtr<nsIRunnable> runnable =
+        NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::Start);
+      swm->AppendPendingOperation(runnable);
+      return;
+    }
+
     if (mJobType == REGISTER_JOB) {
-      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
       mRegistration = swm->GetRegistration(mScope);
 
       if (mRegistration) {
         nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
         if (newest && mScriptSpec.Equals(newest->ScriptSpec()) &&
             mScriptSpec.Equals(mRegistration->mScriptSpec)) {
           mRegistration->mPendingUninstall = false;
           Succeed();
           Done(NS_OK);
           return;
         }
       } else {
-        mRegistration = swm->CreateNewRegistration(mScope);
+        mRegistration = swm->CreateNewRegistration(mScope, mPrincipal);
       }
 
       mRegistration->mScriptSpec = mScriptSpec;
+      swm->StoreRegistration(mPrincipal, mRegistration);
     } else {
       MOZ_ASSERT(mJobType == UPDATE_JOB);
     }
 
     Update();
   }
 
   NS_IMETHOD
@@ -861,23 +942,46 @@ ServiceWorkerManager::Register(nsIDOMWin
 
   ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(cleanedScope);
   MOZ_ASSERT(queue);
 
   nsRefPtr<ServiceWorkerResolveWindowPromiseOnUpdateCallback> cb =
     new ServiceWorkerResolveWindowPromiseOnUpdateCallback(window, promise);
 
   nsRefPtr<ServiceWorkerRegisterJob> job =
-    new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb);
+    new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal);
   queue->Append(job);
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
+void
+ServiceWorkerManager::AppendPendingOperation(ServiceWorkerJobQueue* aQueue,
+                                             ServiceWorkerJob* aJob)
+{
+  MOZ_ASSERT(!mActor);
+  MOZ_ASSERT(aQueue);
+  MOZ_ASSERT(aJob);
+
+  PendingOperation* opt = mPendingOperations.AppendElement();
+  opt->mQueue = aQueue;
+  opt->mJob = aJob;
+}
+
+void
+ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable)
+{
+  MOZ_ASSERT(!mActor);
+  MOZ_ASSERT(aRunnable);
+
+  PendingOperation* opt = mPendingOperations.AppendElement();
+  opt->mRunnable = aRunnable;
+}
+
 /*
  * Used to handle ExtendableEvent::waitUntil() and proceed with
  * installation/activation.
  */
 class LifecycleEventPromiseHandler MOZ_FINAL : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mActivateImmediately;
@@ -1022,16 +1126,17 @@ ServiceWorkerRegistrationInfo::Activate(
     exitingWorker->UpdateState(ServiceWorkerState::Redundant);
   }
 
   mActiveWorker = activatingWorker.forget();
   mWaitingWorker = nullptr;
   mActiveWorker->UpdateState(ServiceWorkerState::Activating);
 
   swm->CheckPendingReadyPromises();
+  swm->StoreRegistration(mPrincipal, this);
 
   // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr<nsIRunnable> controllerChangeRunnable =
     NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm, &ServiceWorkerManager::FireControllerChange, this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
   // XXXnsm I have my doubts about this. Leaving the main thread means that
   // subsequent calls to Activate() not from a Register() call, i.e. due to all
@@ -1377,27 +1482,30 @@ ServiceWorkerManager::CheckReadyPromise(
   return false;
 }
 
 class ServiceWorkerUnregisterJob MOZ_FINAL : public ServiceWorkerJob
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   const nsCString mScope;
   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
+  PrincipalInfo mPrincipalInfo;
 
   ~ServiceWorkerUnregisterJob()
   { }
 
 public:
   ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
                              const nsACString& aScope,
-                             nsIServiceWorkerUnregisterCallback* aCallback)
+                             nsIServiceWorkerUnregisterCallback* aCallback,
+                             PrincipalInfo& aPrincipalInfo)
     : ServiceWorkerJob(aQueue)
     , mScope(aScope)
     , mCallback(aCallback)
+    , mPrincipalInfo(aPrincipalInfo)
   {
     AssertIsOnMainThread();
   }
 
   void
   Start() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
@@ -1440,51 +1548,68 @@ private:
         return NS_OK;
       }
 
       // "Invoke [[Clear Registration]]..."
       registration->Clear();
       swm->RemoveRegistration(registration);
     }
 
+    MOZ_ASSERT(swm->mActor);
+    swm->mActor->SendUnregisterServiceWorker(mPrincipalInfo,
+                                             NS_ConvertUTF8toUTF16(mScope));
     return NS_OK;
   }
 
   // The unregister job is done irrespective of success or failure of any sort.
   void
   UnregisterAndDone()
   {
     Done(Unregister());
   }
 };
 
 NS_IMETHODIMP
-ServiceWorkerManager::Unregister(nsIServiceWorkerUnregisterCallback* aCallback,
+ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
+                                 nsIServiceWorkerUnregisterCallback* aCallback,
                                  const nsAString& aScope)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 
 // This is not accessible by content, and callers should always ensure scope is
 // a correct URI, so this is wrapped in DEBUG
 #ifdef DEBUG
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 #endif
 
   NS_ConvertUTF16toUTF8 scope(aScope);
   ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scope);
   MOZ_ASSERT(queue);
 
+  PrincipalInfo principalInfo;
+  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
+                                                    &principalInfo)))) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
   nsRefPtr<ServiceWorkerUnregisterJob> job =
-    new ServiceWorkerUnregisterJob(queue, scope, aCallback);
-  queue->Append(job);
+    new ServiceWorkerUnregisterJob(queue, scope, aCallback, principalInfo);
+
+  if (mActor) {
+    queue->Append(job);
+    return NS_OK;
+  }
+
+  AppendPendingOperation(queue, job);
   return NS_OK;
 }
 
 /* static */
 already_AddRefed<ServiceWorkerManager>
 ServiceWorkerManager::GetInstance()
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
@@ -1608,16 +1733,95 @@ ServiceWorkerManager::CreateServiceWorke
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
 
+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();
+
+    registration->mActiveWorker =
+      new ServiceWorkerInfo(registration, aRegistrations[i].currentWorkerURL());
+  }
+}
+
+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;
+
+  // 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);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch a runnable.");
+        return;
+      }
+    } else {
+      mPendingOperations[i].mQueue->Append(mPendingOperations[i].mJob);
+    }
+  }
+
+  mPendingOperations.Clear();
+}
+
+void
+ServiceWorkerManager::StoreRegistration(
+                                   nsIPrincipal* aPrincipal,
+                                   ServiceWorkerRegistrationInfo* aRegistration)
+{
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aRegistration);
+
+  ServiceWorkerRegistrationData data;
+  nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  PrincipalInfo principalInfo;
+  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
+                                                    &principalInfo)))) {
+    return;
+  }
+
+  mActor->SendRegisterServiceWorker(data);
+}
+
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow)
 {
   nsCOMPtr<nsIDocument> document = aWindow->GetExtantDoc();
   return GetServiceWorkerRegistrationInfo(document);
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
@@ -2161,25 +2365,26 @@ ServiceWorkerManager::GetServicedClients
 
 void
 ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
 {
   mControlledDocuments.EnumerateRead(FireControllerChangeOnMatchingDocument, aRegistration);
 }
 
 ServiceWorkerRegistrationInfo*
-ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope)
+ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope,
+                                            nsIPrincipal* aPrincipal)
 {
 #ifdef DEBUG
   AssertIsOnMainThread();
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 #endif
-  ServiceWorkerRegistrationInfo* registration = new ServiceWorkerRegistrationInfo(aScope);
+  ServiceWorkerRegistrationInfo* registration = new ServiceWorkerRegistrationInfo(aScope, aPrincipal);
   // From now on ownership of registration is with
   // mServiceWorkerRegistrationInfos.
   mServiceWorkerRegistrationInfos.Put(aScope, registration);
   AddScope(mOrderedScopes, aScope);
   return registration;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -3,34 +3,44 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_serviceworkermanager_h
 #define mozilla_dom_workers_serviceworkermanager_h
 
 #include "nsIServiceWorkerManager.h"
 #include "nsCOMPtr.h"
 
+#include "ipc/IPCMessageUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
 #include "mozilla/dom/ServiceWorkerCommon.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsTObserverArray.h"
 
 class nsIScriptError;
 
 namespace mozilla {
+
+namespace ipc {
+class BackgroundChild;
+}
+
 namespace dom {
 
 class ServiceWorkerRegistration;
 
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerInfo;
@@ -126,27 +136,30 @@ class ServiceWorkerRegistrationInfo MOZ_
 public:
   NS_DECL_ISUPPORTS
 
   nsCString mScope;
   // The scriptURL for the registration. This may be completely different from
   // the URLs of the following three workers.
   nsCString mScriptSpec;
 
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
   nsRefPtr<ServiceWorkerInfo> mActiveWorker;
   nsRefPtr<ServiceWorkerInfo> mWaitingWorker;
   nsRefPtr<ServiceWorkerInfo> mInstallingWorker;
 
   // When unregister() is called on a registration, it is not immediately
   // removed since documents may be controlled. It is marked as
   // pendingUninstall and when all controlling documents go away, removed.
   bool mPendingUninstall;
   bool mWaitingToActivate;
 
-  explicit ServiceWorkerRegistrationInfo(const nsACString& aScope);
+  explicit ServiceWorkerRegistrationInfo(const nsACString& aScope,
+                                         nsIPrincipal* aPrincipal);
 
   already_AddRefed<ServiceWorkerInfo>
   Newest()
   {
     nsRefPtr<ServiceWorkerInfo> newest;
     if (mInstallingWorker) {
       newest = mInstallingWorker;
     } else if (mWaitingWorker) {
@@ -213,16 +226,22 @@ public:
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
 
   const nsCString&
   ScriptSpec() const
   {
     return mScriptSpec;
   }
 
+  void SetScriptSpec(const nsCString& aSpec)
+  {
+    MOZ_ASSERT(!aSpec.IsEmpty());
+    mScriptSpec = aSpec;
+  }
+
   explicit ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
                              const nsACString& aScriptSpec)
     : mRegistration(aReg)
     , mScriptSpec(aScriptSpec)
     , mState(ServiceWorkerState::EndGuard_)
   {
     MOZ_ASSERT(mRegistration);
   }
@@ -261,38 +280,38 @@ public:
   { 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 }     \
 }
 
 /*
  * The ServiceWorkerManager is a per-process global that deals with the
  * installation, querying and event dispatch of ServiceWorkers for all the
  * origins in the process.
  */
-class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
+class ServiceWorkerManager MOZ_FINAL
+  : public nsIServiceWorkerManager
+  , public nsIIPCBackgroundChildCreateCallback
 {
   friend class ActivationRunnable;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerRegisterJob;
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
   friend class QueueFireUpdateFoundRunnable;
   friend class ServiceWorkerUnregisterJob;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
 
   static ServiceWorkerManager* FactoryCreate()
   {
     AssertIsOnMainThread();
-    if (!Preferences::GetBool("dom.serviceWorkers.enabled")) {
-      return nullptr;
-    }
 
     ServiceWorkerManager* res = new ServiceWorkerManager;
     NS_ADDREF(res);
     return res;
   }
 
   // Ordered list of scopes for glob matching.
   // Each entry is an absolute URL representing the scope.
@@ -320,32 +339,35 @@ public:
   GetRegistration(const nsCString& aScope) const
   {
     nsRefPtr<ServiceWorkerRegistrationInfo> reg;
     mServiceWorkerRegistrationInfos.Get(aScope, getter_AddRefs(reg));
     return reg.forget();
   }
 
   ServiceWorkerRegistrationInfo*
-  CreateNewRegistration(const nsCString& aScope);
+  CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal);
 
   void
   RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
   {
     MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
     ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
     mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope);
   }
 
   ServiceWorkerJobQueue*
   GetOrCreateJobQueue(const nsCString& aScope)
   {
     return mJobQueues.LookupOrAdd(aScope);
   }
 
+  void StoreRegistration(nsIPrincipal* aPrincipal,
+                         ServiceWorkerRegistrationInfo* aRegistration);
+
   void
   FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
 
   // Returns true if the error was handled, false if normal worker error
   // handling should continue.
   bool
   HandleError(JSContext* aCx,
               const nsCString& aScope,
@@ -359,16 +381,19 @@ public:
 
   void
   GetServicedClients(const nsCString& aScope,
                      nsTArray<uint64_t>* aControlledDocuments);
 
   static already_AddRefed<ServiceWorkerManager>
   GetInstance();
 
+ void LoadRegistrations(
+                 const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
+
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
@@ -445,22 +470,36 @@ private:
     PendingReadyPromise(nsIURI* aURI, Promise* aPromise)
       : mURI(aURI), mPromise(aPromise)
     { }
 
     nsCOMPtr<nsIURI> mURI;
     nsRefPtr<Promise> mPromise;
   };
 
+  void AppendPendingOperation(nsIRunnable* aRunnable);
+  void AppendPendingOperation(ServiceWorkerJobQueue* aQueue,
+                              ServiceWorkerJob* aJob);
+
+  bool HasBackgroundActor() const
+  {
+    return !!mActor;
+  }
+
   static PLDHashOperator
   CheckPendingReadyPromisesEnumerator(nsISupports* aSupports,
                                       nsAutoPtr<PendingReadyPromise>& aData,
                                       void* aUnused);
 
   nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
+
+  mozilla::ipc::PBackgroundChild* mActor;
+
+  struct PendingOperation;
+  nsTArray<PendingOperation> mPendingOperations;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
                               NS_SERVICEWORKERMANAGER_IMPL_IID);
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -0,0 +1,671 @@
+/* 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 "ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+
+#include "nsIEventTarget.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsIObserverService.h"
+
+#include "MainThreadUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsAutoPtr.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
+
+} // anonymous namespace
+
+NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
+                  nsIObserver)
+
+void
+ServiceWorkerRegistrar::Initialize()
+{
+  MOZ_ASSERT(!gServiceWorkerRegistrar);
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return;
+  }
+
+  gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
+  ClearOnShutdown(&gServiceWorkerRegistrar);
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
+                                              "profile-after-change", false);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    rv = obs->AddObserver(gServiceWorkerRegistrar, "profile-before-change",
+                          false);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+}
+
+/* static */ already_AddRefed<ServiceWorkerRegistrar>
+ServiceWorkerRegistrar::Get()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  MOZ_ASSERT(gServiceWorkerRegistrar);
+  nsRefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
+  return service.forget();
+}
+
+ServiceWorkerRegistrar::ServiceWorkerRegistrar()
+  : mMonitor("ServiceWorkerRegistrar.mMonitor")
+  , mDataLoaded(false)
+  , mShuttingDown(false)
+  , mShutdownCompleteFlag(nullptr)
+  , mRunnableCounter(0)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
+{
+  MOZ_ASSERT(!mRunnableCounter);
+}
+
+void
+ServiceWorkerRegistrar::GetRegistrations(
+                               nsTArray<ServiceWorkerRegistrationData>& aValues)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aValues.IsEmpty());
+
+  // If we don't have the profile directory, profile is not started yet (and
+  // probably we are in a utest).
+  if (!mProfileDir) {
+    return;
+  }
+
+  // We care just about the first execution because this can be blocked by
+  // loading data from disk.
+  static bool firstTime = true;
+  TimeStamp startTime;
+
+  if (firstTime) {
+    startTime = TimeStamp::NowLoRes();
+  }
+
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    // Waiting for data loaded.
+    mMonitor.AssertCurrentThreadOwns();
+    while (!mDataLoaded) {
+      mMonitor.Wait();
+    }
+
+    aValues.AppendElements(mData);
+  }
+
+  if (firstTime) {
+    firstTime = false;
+    Telemetry::AccumulateTimeDelta(
+      Telemetry::SERVICE_WORKER_REGISTRATION_LOADING,
+      startTime);
+  }
+}
+
+void
+ServiceWorkerRegistrar::RegisterServiceWorker(
+                                     const ServiceWorkerRegistrationData& aData)
+{
+  AssertIsOnBackgroundThread();
+
+  if (mShuttingDown) {
+    NS_WARNING("Failed to register a serviceWorker during shutting down.");
+    return;
+  }
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    MOZ_ASSERT(mDataLoaded);
+
+    bool found = false;
+    for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
+      if (mData[i].scope() == aData.scope()) {
+        mData[i] = aData;
+        found = true;
+        break;
+      }
+    }
+
+    if (!found) {
+      mData.AppendElement(aData);
+    }
+  }
+
+  ScheduleSaveData();
+}
+
+void
+ServiceWorkerRegistrar::UnregisterServiceWorker(const nsACString& aScope)
+{
+  AssertIsOnBackgroundThread();
+
+  if (mShuttingDown) {
+    NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
+    return;
+  }
+
+  bool deleted = false;
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    MOZ_ASSERT(mDataLoaded);
+
+    for (uint32_t i = 0; i < mData.Length(); ++i) {
+      if (mData[i].scope() == aScope) {
+        mData.RemoveElementAt(i);
+        deleted = true;
+        break;
+      }
+    }
+  }
+
+  if (deleted) {
+    ScheduleSaveData();
+  }
+}
+
+void
+ServiceWorkerRegistrar::LoadData()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mDataLoaded);
+
+  nsresult rv = ReadData();
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    DeleteData();
+    // Also if the reading failed we have to notify what is waiting for data.
+  }
+
+  MonitorAutoLock lock(mMonitor);
+  MOZ_ASSERT(!mDataLoaded);
+  mDataLoaded = true;
+  mMonitor.Notify();
+}
+
+nsresult
+ServiceWorkerRegistrar::ReadData()
+{
+  // We cannot assert about the correct thread because normally this method
+  // runs on a IO thread, but in gTests we call it from the main-thread.
+
+  MOZ_ASSERT(mProfileDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool exists;
+  rv = file->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
+  MOZ_ASSERT(lineInputStream);
+
+  nsAutoCString line;
+  bool hasMoreLines;
+  rv = lineInputStream->ReadLine(line, &hasMoreLines);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // The file is corrupted ?
+  // FIXME: in case we implement a version 2, we should inform the user using
+  // the console about this issue.
+  if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  while (hasMoreLines) {
+    ServiceWorkerRegistrationData* entry = mData.AppendElement();
+
+#define GET_LINE(x)                                   \
+    rv = lineInputStream->ReadLine(x, &hasMoreLines); \
+    if (NS_WARN_IF(NS_FAILED(rv))) {                  \
+      return rv;                                      \
+    }                                                 \
+    if (NS_WARN_IF(!hasMoreLines)) {                  \
+      return NS_ERROR_FAILURE;                        \
+    }
+
+    GET_LINE(line);
+
+    if (line.EqualsLiteral(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL)) {
+      entry->principal() = mozilla::ipc::SystemPrincipalInfo();
+    } else {
+      if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      GET_LINE(line);
+
+      uint32_t appId = line.ToInteger(&rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      GET_LINE(line);
+
+      if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
+          !line.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      bool isInBrowserElement = line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
+
+      GET_LINE(line);
+      entry->principal() =
+        mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement,
+                                           line);
+    }
+
+    GET_LINE(entry->scope());
+    GET_LINE(entry->scriptSpec());
+    GET_LINE(entry->currentWorkerURL());
+
+#undef GET_LINE
+
+    rv = lineInputStream->ReadLine(line, &hasMoreLines);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+ServiceWorkerRegistrar::DeleteData()
+{
+  // We cannot assert about the correct thread because normally this method
+  // runs on a IO thread, but in gTests we call it from the main-thread.
+
+  MOZ_ASSERT(mProfileDir);
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    mData.Clear();
+  }
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = file->Remove(false);
+  if (rv == NS_ERROR_FILE_NOT_FOUND) {
+    return;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+}
+
+class ServiceWorkerRegistrarSaveDataRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  ServiceWorkerRegistrarSaveDataRunnable()
+    : mThread(do_GetCurrentThread())
+  {
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    nsRefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->SaveData();
+
+    nsRefPtr<nsRunnable> runnable =
+      NS_NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved);
+    nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIThread> mThread;
+};
+
+void
+ServiceWorkerRegistrar::ScheduleSaveData()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mShuttingDown);
+
+  nsCOMPtr<nsIEventTarget> target =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target, "Must have stream transport service");
+
+  nsRefPtr<nsRunnable> runnable =
+    new ServiceWorkerRegistrarSaveDataRunnable();
+  nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  ++mRunnableCounter;
+}
+
+void
+ServiceWorkerRegistrar::ShutdownCompleted()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(mShutdownCompleteFlag && !*mShutdownCompleteFlag);
+  *mShutdownCompleteFlag = true;
+}
+
+void
+ServiceWorkerRegistrar::SaveData()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  nsresult rv = WriteData();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to write data for the ServiceWorker Registations.");
+    DeleteData();
+  }
+}
+
+void
+ServiceWorkerRegistrar::DataSaved()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mRunnableCounter);
+
+  --mRunnableCounter;
+  MaybeScheduleShutdownCompleted();
+}
+
+void
+ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
+{
+  AssertIsOnBackgroundThread();
+
+  if (mRunnableCounter || !mShuttingDown) {
+    return;
+  }
+
+  nsRefPtr<nsRunnable> runnable =
+     NS_NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+}
+
+nsresult
+ServiceWorkerRegistrar::WriteData()
+{
+  // We cannot assert about the correct thread because normally this method
+  // runs on a IO thread, but in gTests we call it from the main-thread.
+
+  MOZ_ASSERT(mProfileDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // We need a lock to take a snapshot of the data.
+  nsTArray<ServiceWorkerRegistrationData> data;
+  {
+    MonitorAutoLock lock(mMonitor);
+    data = mData;
+  }
+
+  nsCOMPtr<nsIOutputStream> stream;
+  rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString buffer;
+  buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
+  buffer.Append('\n');
+
+  uint32_t count;
+  rv = stream->Write(buffer.Data(), buffer.Length(), &count);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (count != buffer.Length()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
+    const mozilla::ipc::PrincipalInfo& info = data[i].principal();
+
+    if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
+      buffer.AssignLiteral(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL);
+    } else {
+      MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
+
+      const mozilla::ipc::ContentPrincipalInfo& cInfo =
+        info.get_ContentPrincipalInfo();
+
+      buffer.AssignLiteral(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL);
+      buffer.Append('\n');
+
+      buffer.AppendInt(cInfo.appId());
+      buffer.Append('\n');
+
+      if (cInfo.isInBrowserElement()) {
+        buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TRUE);
+      } else {
+        buffer.AppendLiteral(SERVICEWORKERREGISTRAR_FALSE);
+      }
+
+      buffer.Append('\n');
+      buffer.Append(cInfo.spec());
+    }
+
+    buffer.Append('\n');
+
+    buffer.Append(data[i].scope());
+    buffer.Append('\n');
+
+    buffer.Append(data[i].scriptSpec());
+    buffer.Append('\n');
+
+    buffer.Append(data[i].currentWorkerURL());
+    buffer.Append('\n');
+
+    buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
+    buffer.Append('\n');
+
+    rv = stream->Write(buffer.Data(), buffer.Length(), &count);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (count != buffer.Length()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+
+  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
+  MOZ_ASSERT(safeStream);
+
+  rv = safeStream->Finish();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+ServiceWorkerRegistrar::ProfileStarted()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mProfileDir);
+
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(mProfileDir));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  nsCOMPtr<nsIEventTarget> target =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target, "Must have stream transport service");
+
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData);
+  rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the LoadDataRunnable.");
+  }
+}
+
+void
+ServiceWorkerRegistrar::ProfileStopped()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mProfileDir) {
+    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                         getter_AddRefs(mProfileDir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+  PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
+  if (!child) {
+    return;
+  }
+
+  bool completed = false;
+  mShutdownCompleteFlag = &completed;
+
+  child->SendShutdownServiceWorkerRegistrar();
+
+  nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+  while (true) {
+    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread));
+    if (completed) {
+      break;
+    }
+  }
+}
+
+void
+ServiceWorkerRegistrar::Shutdown()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mShuttingDown);
+
+  mShuttingDown = true;
+  MaybeScheduleShutdownCompleted();
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
+                                const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!strcmp(aTopic, "profile-after-change")) {
+    nsCOMPtr<nsIObserverService> observerService =
+      services::GetObserverService();
+    observerService->RemoveObserver(this, "profile-after-change");
+
+    // The profile is fully loaded, now we can proceed with the loading of data
+    // from disk.
+    ProfileStarted();
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "profile-before-change")) {
+    nsCOMPtr<nsIObserverService> observerService =
+      services::GetObserverService();
+    observerService->RemoveObserver(this, "profile-before-change");
+
+    // Shutting down, let's sync the data.
+    ProfileStopped();
+
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerRegistrar.h
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_ServiceWorkerRegistrar_h
+#define mozilla_dom_workers_ServiceWorkerRegistrar_h
+
+#include "mozilla/Monitor.h"
+#include "mozilla/Telemetry.h"
+#include "nsClassHashtable.h"
+#include "nsIObserver.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+#define SERVICEWORKERREGISTRAR_FILE "serviceworker.txt"
+#define SERVICEWORKERREGISTRAR_VERSION "1"
+#define SERVICEWORKERREGISTRAR_TERMINATOR "#"
+#define SERVICEWORKERREGISTRAR_TRUE "true"
+#define SERVICEWORKERREGISTRAR_FALSE "false"
+#define SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL "system"
+#define SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL "content"
+
+
+class nsIFile;
+class nsRunnable;
+
+namespace mozilla {
+namespace dom {
+
+class ServiceWorkerRegistrationData;
+
+class ServiceWorkerRegistrar : public nsIObserver
+{
+  friend class ServiceWorkerRegistrarSaveDataRunnable;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static void Initialize();
+
+  void Shutdown();
+
+  void DataSaved();
+
+  static already_AddRefed<ServiceWorkerRegistrar> Get();
+
+  void GetRegistrations(nsTArray<ServiceWorkerRegistrationData>& aValues);
+
+  void RegisterServiceWorker(const ServiceWorkerRegistrationData& aData);
+  void UnregisterServiceWorker(const nsACString& aScope);
+
+protected:
+  // These methods are protected because we test this class using gTest
+  // subclassing it.
+  void LoadData();
+  void SaveData();
+
+  nsresult ReadData();
+  nsresult WriteData();
+  void DeleteData();
+
+  ServiceWorkerRegistrar();
+  virtual ~ServiceWorkerRegistrar();
+
+private:
+  void ProfileStarted();
+  void ProfileStopped();
+
+  void ScheduleSaveData();
+  void ShutdownCompleted();
+  void MaybeScheduleShutdownCompleted();
+
+  mozilla::Monitor mMonitor;
+
+protected:
+  // mData and mDataLoaded are protected by mMonitor.
+  nsTArray<ServiceWorkerRegistrationData> mData;
+  bool mDataLoaded;
+
+  bool mShuttingDown;
+  bool* mShutdownCompleteFlag;
+  uint32_t mRunnableCounter;
+
+  nsCOMPtr<nsIFile> mProfileDir;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_workers_ServiceWorkerRegistrar_h
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerRegistrarTypes.ipdlh
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* 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 PBackgroundSharedTypes;
+
+namespace mozilla {
+namespace dom {
+
+struct ServiceWorkerRegistrationData
+{
+  nsCString scope;
+  nsCString scriptSpec;
+  nsCString currentWorkerURL;
+  PrincipalInfo principal;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -195,17 +195,17 @@ ServiceWorkerRegistration::Unregister(Er
   nsRefPtr<Promise> promise = Promise::Create(go, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsRefPtr<UnregisterCallback> cb = new UnregisterCallback(promise);
 
   NS_ConvertUTF8toUTF16 scope(uriSpec);
-  aRv = swm->Unregister(cb, scope);
+  aRv = swm->Unregister(documentPrincipal, cb, scope);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return promise.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -585,17 +585,17 @@ public:
     if (NS_WARN_IF(NS_FAILED(rv))) {
       UnregisterFailed();
       return NS_OK;
     }
 
     // We don't need to check if the principal can load this mScope because a
     // ServiceWorkerGlobalScope can always unregister itself.
 
-    rv = swm->Unregister(this, mScope);
+    rv = swm->Unregister(mWorkerPrivate->GetPrincipal(), this, mScope);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       UnregisterFailed();
       return NS_OK;
     }
 
     return NS_OK;
   }
 
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 # Public stuff.
 EXPORTS.mozilla.dom += [
     'ServiceWorkerCommon.h',
     'ServiceWorkerContainer.h',
     'ServiceWorkerEvents.h',
+    'ServiceWorkerRegistrar.h',
     'ServiceWorkerRegistration.h',
     'WorkerPrivate.h',
     'WorkerRunnable.h',
     'WorkerScope.h',
 ]
 
 EXPORTS.mozilla.dom.workers += [
     'ServiceWorkerManager.h',
@@ -61,28 +62,33 @@ UNIFIED_SOURCES += [
     'RuntimeService.cpp',
     'ScriptLoader.cpp',
     'ServiceWorker.cpp',
     'ServiceWorkerClient.cpp',
     'ServiceWorkerClients.cpp',
     'ServiceWorkerContainer.cpp',
     'ServiceWorkerEvents.cpp',
     'ServiceWorkerManager.cpp',
+    'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
     'WorkerDebuggerManager.cpp',
     'WorkerPrivate.cpp',
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
     'XMLHttpRequest.cpp',
     'XMLHttpRequestUpload.cpp',
 ]
 
+IPDL_SOURCES += [
+    'ServiceWorkerRegistrarTypes.ipdlh',
+]
+
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
     '/dom/base',
@@ -103,8 +109,10 @@ MOCHITEST_MANIFESTS += [
     'test/fetch/mochitest.ini',
     'test/mochitest.ini',
     'test/serviceworkers/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
+
+TEST_DIRS += ['test/gtest']
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -0,0 +1,282 @@
+/* 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 "gtest/gtest.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIOutputStream.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+class ServiceWorkerRegistrarTest : public ServiceWorkerRegistrar
+{
+public:
+  ServiceWorkerRegistrarTest()
+  {
+    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(mProfileDir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+  nsresult TestReadData() { return ReadData(); }
+  nsresult TestWriteData() { return WriteData(); }
+  void TestDeleteData() { DeleteData(); }
+
+  nsTArray<ServiceWorkerRegistrationData>& TestGetData() { return mData; }
+};
+
+already_AddRefed<nsIFile>
+GetFile()
+{
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  return file.forget();
+}
+
+bool
+CreateFile(const nsACString& aData)
+{
+  nsCOMPtr<nsIFile> file = GetFile();
+
+  nsCOMPtr<nsIOutputStream> stream;
+  nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  uint32_t count;
+  rv = stream->Write(aData.Data(), aData.Length(), &count);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  if (count != aData.Length()) {
+    return false;
+  }
+
+  return true;
+}
+
+TEST(ServiceWorkerRegistrar, TestNoFile)
+{
+  nsCOMPtr<nsIFile> file = GetFile();
+  ASSERT_TRUE(file) << "GetFile must return a nsIFIle";
+
+  bool exists;
+  nsresult rv = file->Exists(&exists);
+  ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail";
+
+  if (exists) {
+    rv = file->Remove(false);
+    ASSERT_EQ(NS_OK, rv) << "nsIFile::Remove cannot fail";
+  }
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestEmptyFile)
+{
+  ASSERT_TRUE(CreateFile(EmptyCString())) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_NE(NS_OK, rv) << "ReadData() should fail if the file is empty";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestRightVersionFile)
+{
+  ASSERT_TRUE(CreateFile(nsAutoCString(SERVICEWORKERREGISTRAR_VERSION "\n"))) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail when the version is correct";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestWrongVersionFile)
+{
+  ASSERT_TRUE(CreateFile(nsAutoCString(SERVICEWORKERREGISTRAR_VERSION "bla\n"))) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_NE(NS_OK, rv) << "ReadData() should fail when the version is not correct";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestReadData)
+{
+  nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n");
+
+  buffer.Append(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL "\n");
+  buffer.Append("scope 0\nscriptSpec 0\ncurrentWorkerURL 0\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  buffer.Append(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL "\n");
+  buffer.Append("123\n" SERVICEWORKERREGISTRAR_TRUE "\n");
+  buffer.Append("spec 1\nscope 1\nscriptSpec 1\ncurrentWorkerURL 1\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  buffer.Append(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL "\n");
+  buffer.Append("0\n" SERVICEWORKERREGISTRAR_FALSE "\n");
+  buffer.Append("spec 2\nscope 2\nscriptSpec 2\ncurrentWorkerURL 2\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)3, data.Length()) << "4 entries should be found";
+
+  const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
+  ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) << "First principal must be system";
+
+  ASSERT_STREQ("scope 0", data[0].scope().get());
+  ASSERT_STREQ("scriptSpec 0", data[0].scriptSpec().get());
+  ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
+
+  const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
+  ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
+
+  ASSERT_EQ((uint32_t)123, cInfo1.appId());
+  ASSERT_EQ((uint32_t)true, cInfo1.isInBrowserElement());
+  ASSERT_STREQ("spec 1", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", data[1].scope().get());
+  ASSERT_STREQ("scriptSpec 1", data[1].scriptSpec().get());
+  ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
+
+  const mozilla::ipc::PrincipalInfo& info2 = data[2].principal();
+  ASSERT_EQ(info2.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo2 = data[2].principal();
+
+  ASSERT_EQ((uint32_t)0, cInfo2.appId());
+  ASSERT_EQ((uint32_t)false, cInfo2.isInBrowserElement());
+  ASSERT_STREQ("spec 2", cInfo2.spec().get());
+  ASSERT_STREQ("scope 2", data[2].scope().get());
+  ASSERT_STREQ("scriptSpec 2", data[2].scriptSpec().get());
+  ASSERT_STREQ("currentWorkerURL 2", data[2].currentWorkerURL().get());
+}
+
+TEST(ServiceWorkerRegistrar, TestDeleteData)
+{
+  ASSERT_TRUE(CreateFile(nsAutoCString("Foobar"))) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  swr->TestDeleteData();
+
+  nsCOMPtr<nsIFile> file = GetFile();
+
+  bool exists;
+  nsresult rv = file->Exists(&exists);
+  ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail";
+
+  ASSERT_FALSE(exists) << "The file should not exist after a DeleteData().";
+}
+
+TEST(ServiceWorkerRegistrar, TestWriteData)
+{
+  {
+    nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+    nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+
+    for (int i = 0; i < 10; ++i) {
+      ServiceWorkerRegistrationData* d = data.AppendElement();
+
+      if ((i % 2) == 0) {
+        d->principal() = mozilla::ipc::SystemPrincipalInfo();
+      } else if ((i % 2) == 1) {
+        nsAutoCString spec;
+        spec.AppendPrintf("spec write %d", i);
+        d->principal() = mozilla::ipc::ContentPrincipalInfo(i, i % 2, spec);
+      }
+
+      d->scope().AppendPrintf("scope write %d", i);
+      d->scriptSpec().AppendPrintf("scriptSpec write %d", i);
+      d->currentWorkerURL().AppendPrintf("currentWorkerURL write %d", i);
+    }
+
+    nsresult rv = swr->TestWriteData();
+    ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail";
+  }
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)10, data.Length()) << "10 entries should be found";
+
+  for (int i = 0; i < 10; ++i) {
+    nsAutoCString test;
+
+    if ((i % 2) == 0) {
+      ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo);
+    } else if ((i % 2) == 1) {
+      ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
+      const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal();
+
+      ASSERT_EQ((uint32_t)i, cInfo.appId());
+      ASSERT_EQ((uint32_t)(i %2), cInfo.isInBrowserElement());
+
+      test.AppendPrintf("spec write %d", i);
+      ASSERT_STREQ(test.get(), cInfo.spec().get());
+    }
+
+    test.Truncate();
+    test.AppendPrintf("scope write %d", i);
+    ASSERT_STREQ(test.get(), data[i].scope().get());
+
+    test.Truncate();
+    test.AppendPrintf("scriptSpec write %d", i);
+    ASSERT_STREQ(test.get(), data[i].scriptSpec().get());
+
+    test.Truncate();
+    test.AppendPrintf("currentWorkerURL write %d", i);
+    ASSERT_STREQ(test.get(), data[i].currentWorkerURL().get());
+  }
+}
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  int rv = RUN_ALL_TESTS();
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/gtest/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES = [
+    'TestReadWrite.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -5,20 +5,22 @@
 #include "BackgroundParentImpl.h"
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "nsNetUtil.h"
 #include "nsRefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
 
@@ -66,16 +68,17 @@ public:
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace ipc {
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::BroadcastChannelParent;
+using mozilla::dom::ServiceWorkerRegistrationData;
 
 BackgroundParentImpl::BackgroundParentImpl()
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
 
   MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
 }
@@ -348,16 +351,207 @@ BackgroundParentImpl::DeallocPBroadcastC
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<BroadcastChannelParent*>(aActor);
   return true;
 }
 
+namespace {
+
+class RegisterServiceWorkerCallback MOZ_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 MOZ_FINAL : public nsRunnable
+{
+public:
+  explicit UnregisterServiceWorkerCallback(const nsString& aScope)
+    : mScope(aScope)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    nsRefPtr<dom::ServiceWorkerRegistrar> service =
+      dom::ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->UnregisterServiceWorker(NS_ConvertUTF16toUTF8(mScope));
+    return NS_OK;
+  }
+
+private:
+  nsString mScope;
+};
+
+class CheckPrincipalWithCallbackRunnable MOZ_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) {
+    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) {
+    return false;
+  }
+
+  nsRefPtr<UnregisterServiceWorkerCallback> callback =
+    new UnregisterServiceWorkerCallback(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;
+  }
+
+  nsRefPtr<dom::ServiceWorkerRegistrar> service =
+    dom::ServiceWorkerRegistrar::Get();
+  MOZ_ASSERT(service);
+
+  service->Shutdown();
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -78,14 +78,25 @@ protected:
   virtual bool
   RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsString& origin,
                                    const nsString& channel) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvRegisterServiceWorker(const ServiceWorkerRegistrationData& aData)
+                            MOZ_OVERRIDE;
+
+  virtual bool
+  RecvUnregisterServiceWorker(const PrincipalInfo& aPrincipalInfo,
+                              const nsString& aScope) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvShutdownServiceWorkerRegistrar() MOZ_OVERRIDE;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -7,16 +7,17 @@ include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PFileDescriptorSet;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
+include ServiceWorkerRegistrarTypes;
 
 namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
 {
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
@@ -30,16 +31,21 @@ parent:
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
 
   PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);
 
+  RegisterServiceWorker(ServiceWorkerRegistrationData data);
+  UnregisterServiceWorker(PrincipalInfo principalInfo,
+                          nsString scope);
+  ShutdownServiceWorkerRegistrar();
+
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -296,16 +296,18 @@ nsLayoutStatics::Initialize()
   CacheObserver::Init();
 
   CounterStyleManager::InitializeBuiltinCounterStyles();
 
   CameraPreferences::Initialize();
 
   IMEStateManager::Init();
 
+  ServiceWorkerRegistrar::Initialize();
+
 #ifdef MOZ_B2G
   RequestSyncWifiService::Init();
 #endif
 
   return NS_OK;
 }
 
 void
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7034,16 +7034,23 @@
     "description": "Tracking protection shield (0 = not shown, 1 = loaded, 2 = blocked, 3 = due to mixed content"
   },
   "TRACKING_PROTECTION_EVENTS": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 3,
     "description": "Doorhanger shown = 0, Disable = 1, Enable = 2"
   },
+  "SERVICE_WORKER_REGISTRATION_LOADING": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "5000",
+    "n_buckets": 20,
+    "description": "Tracking how ServiceWorkerRegistrar loads data before the first content is shown"
+  },
   "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "Stores 1 if generating a call URL succeeded, and 0 if it failed."
   },
   "LOOP_CLIENT_CALL_URL_SHARED": {
     "expires_in_version": "never",
     "kind": "boolean",