Backed out 14 changesets (bug 1425975) for M(5) permafails mochitest/test_ext_webrequest_filter.html. r=backout a=backout
authorCsoregi Natalia <ncsoregi@mozilla.com>
Sun, 24 Dec 2017 00:23:57 +0200
changeset 397395 2e0db1b48499fcf1598f23e04211360f9d39b7af
parent 397394 363ff8639a8e3d2eb7a88bdce6232419adabab74
child 397396 cd7926a8269c6f4125be6f30002639bc16d37f9d
push id33139
push userncsoregi@mozilla.com
push dateSat, 23 Dec 2017 22:24:13 +0000
treeherdermozilla-central@2e0db1b48499 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout, backout
bugs1425975
milestone59.0a1
backs oute6f4a2d1df9a4a50b1b659f87dca99d88a5ef63a
1e657fa97b71a183c521c65e8078d928580bc849
9e1544ec814d46dcf8a771a221f3ac1349663497
0e50d9d1d06961d9866e1c34bec7b97839bb5834
fb89dbd922ba50ebf7a5da5a555416f449a6c715
f2b451ce55d4ff70661c66a6e5e87947ed48e5d1
4ce186c6d8f5eb88ef10dd8602bf11382839b497
6f520ab76d6a1fe6eca397c1ef72c7abcb7fe16c
f091f5e182c442289c53ca0b68955e0bdbab44b7
82d39ed8c8315b05798c963097b18cc7ea355df4
df13eba47970f655872b9bbfda0a862895eae4fc
553628a56e6a8ceec8ebfdeb7bf804e4c9218be4
acf4d61babab3a78c0f4d045a68bb102604d7437
b7ae11b5bce80e23a8edc94bf971608d7ff8cc24
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 14 changesets (bug 1425975) for M(5) permafails mochitest/test_ext_webrequest_filter.html. r=backout a=backout Backed out changeset e6f4a2d1df9a (bug 1425975) Backed out changeset 1e657fa97b71 (bug 1425975) Backed out changeset 9e1544ec814d (bug 1425975) Backed out changeset 0e50d9d1d069 (bug 1425975) Backed out changeset fb89dbd922ba (bug 1425975) Backed out changeset f2b451ce55d4 (bug 1425975) Backed out changeset 4ce186c6d8f5 (bug 1425975) Backed out changeset 6f520ab76d6a (bug 1425975) Backed out changeset f091f5e182c4 (bug 1425975) Backed out changeset 82d39ed8c831 (bug 1425975) Backed out changeset df13eba47970 (bug 1425975) Backed out changeset 553628a56e6a (bug 1425975) Backed out changeset acf4d61babab (bug 1425975) Backed out changeset b7ae11b5bce8 (bug 1425975)
docshell/base/nsDocShell.cpp
dom/base/nsDocument.cpp
dom/base/nsGlobalWindowInner.cpp
dom/clients/manager/ClientHandle.cpp
dom/clients/manager/ClientHandle.h
dom/clients/manager/ClientInfo.cpp
dom/clients/manager/ClientInfo.h
dom/clients/manager/ClientSource.cpp
dom/clients/manager/ClientThing.h
dom/interfaces/base/nsIServiceWorkerManager.idl
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerRegistrationInfo.cpp
dom/workers/ServiceWorkerRegistrationInfo.h
dom/workers/ServiceWorkerUnregisterJob.cpp
dom/workers/test/serviceworkers/skip_waiting_scope/index.html
dom/workers/test/serviceworkers/test_skip_waiting.html
dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
dom/workers/test/serviceworkers/worker_updatefoundevent.js
testing/web-platform/tests/service-workers/service-worker/unregister-then-register-new-script.https.html
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -150,17 +150,16 @@
 #include "nsIStructuredCloneContainer.h"
 #include "nsISupportsPrimitives.h"
 #ifdef MOZ_PLACES
 #include "nsIFaviconService.h"
 #include "mozIPlacesPendingOperation.h"
 #include "mozIAsyncFavicons.h"
 #endif
 #include "nsINetworkPredictor.h"
-#include "nsIServiceWorkerManager.h"
 
 // Editor-related
 #include "nsIEditingSession.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsGlobalWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsICachingChannel.h"
@@ -3413,17 +3412,17 @@ nsDocShell::MaybeCreateInitialClientSour
   // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
   if (mInitialClientSource) {
     return;
   }
 
   // Don't pre-allocate the client when we are sandboxed.  The inherited
   // principal does not take sandboxing into account.
   // TODO: Refactor sandboxing principal code out so we can use it here.
-  if (!aPrincipal && mSandboxFlags) {
+  if (!aPrincipal && (mSandboxFlags & SANDBOXED_ORIGIN)) {
     return;
   }
 
   nsIPrincipal* principal = aPrincipal ? aPrincipal
                                        : GetInheritedPrincipal(false);
 
   // Sometimes there is no principal available when we are called from
   // CreateAboutBlankContentViewer.  For example, sometimes the principal
@@ -3455,45 +3454,28 @@ nsDocShell::MaybeCreateInitialClientSour
   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
   nsPIDOMWindowInner* parentInner =
     parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
   if (!parentInner) {
     return;
   }
 
-  // We're done if there is no parent controller.  Also, don't inherit
-  // the controller if we're sandboxed.  This matches our behavior in
-  // ShouldPrepareForIntercept(),
   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
-  if (controller.isNothing() || mSandboxFlags) {
-    return;
-  }
-
-  nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
-  if (!swm) {
+  if (controller.isNothing()) {
     return;
   }
 
   // If the parent is controlled then propagate that controller to the
   // initial about:blank client as well.  This will set the controller
   // in the ClientManagerService in the parent.
-  //
-  // Note: If the registration is missing from the SWM we avoid setting
-  //       the controller on the client.  We can do this synchronously
-  //       for now since SWM is in the child process.  In the future
-  //       when SWM is in the parent process we will probably have to
-  //       always set the initial client source and then somehow clear
-  //       it if we find the registration is acutally gone.  Its also
-  //       possible this race only occurs in cases where the resulting
-  //       window is no longer exposed.  For example, in theory the SW
-  //       should not go away if our parent window is controlled.
-  if (!swm->StartControlling(mInitialClientSource->Info(), controller.ref())) {
-    return;
-  }
+  RefPtr<ClientHandle> handle =
+    ClientManager::CreateHandle(mInitialClientSource->Info(),
+                                parentInner->EventTargetFor(TaskCategory::Other));
+  handle->Control(controller.ref());
 
   // Also mark the ClientSource as controlled directly in case script
   // immediately accesses navigator.serviceWorker.controller.
   mInitialClientSource->SetController(controller.ref());
 }
 
 Maybe<ClientInfo>
 nsDocShell::GetInitialClientInfo() const
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5594,20 +5594,17 @@ nsDocument::DispatchContentLoadedEvents(
                                         NS_LITERAL_STRING("MozApplicationManifest"),
                                         true, true);
   }
 
   if (mMaybeServiceWorkerControlled) {
     using mozilla::dom::workers::ServiceWorkerManager;
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     if (swm) {
-      Maybe<ClientInfo> clientInfo = GetClientInfo();
-      if (clientInfo.isSome()) {
-        swm->MaybeCheckNavigationUpdate(clientInfo.ref());
-      }
+      swm->MaybeCheckNavigationUpdate(this);
     }
   }
 
   UnblockOnload(true);
 }
 
 void
 nsDocument::EndLoad()
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1781,74 +1781,41 @@ nsGlobalWindowInner::EnsureClientSource(
   // In this case we want to inherit this placeholder Client here.
   if (!mClientSource) {
     mClientSource = Move(initialClientSource);
     if (mClientSource) {
       newClientSource = true;
     }
   }
 
-  // Verify the final ClientSource principal matches the final document
-  // principal.  The ClientChannelHelper handles things like network
-  // redirects, but there are other ways the document principal can change.
-  // For example, if something sets the nsIChannel.owner property, then
-  // the final channel principal can be anything.  Unfortunately there is
-  // no good way to detect this until after the channel completes loading.
-  //
-  // For now we handle this just by reseting the ClientSource.  This will
-  // result in a new ClientSource with the correct principal being created.
-  // To APIs like ServiceWorker and Clients API it will look like there was
-  // an initial content page created that was then immediately replaced.
-  // This is pretty close to what we are actually doing.
-  if (mClientSource) {
-    nsCOMPtr<nsIPrincipal> clientPrincipal(mClientSource->Info().GetPrincipal());
-    if (!clientPrincipal || !clientPrincipal->Equals(mDoc->NodePrincipal())) {
-      mClientSource.reset();
-    }
-  }
-
   // If we don't have a reserved client or an initial client, then create
   // one now.  This can happen in certain cases where we avoid preallocating
   // the client in the docshell.  This mainly occurs in situations where
   // the principal is not clearly inherited from the parent; e.g. sandboxed
   // iframes, window.open(), etc.
-  //
-  // We also do this late ClientSource creation if the final document ended
-  // up with a different principal.
-  //
   // TODO: We may not be marking initial about:blank documents created
   //       this way as controlled by a service worker properly.  The
   //       controller should be coming from the same place as the inheritted
   //       principal.  We do this in docshell, but as mentioned we aren't
   //       smart enough to handle all cases yet.  For example, a
   //       window.open() with new URL should inherit the controller from
   //       the opener, but we probably don't handle that yet.
   if (!mClientSource) {
     mClientSource = ClientManager::CreateSource(ClientType::Window,
                                                 EventTargetFor(TaskCategory::Other),
                                                 mDoc->NodePrincipal());
     MOZ_DIAGNOSTIC_ASSERT(mClientSource);
     newClientSource = true;
-
-    // Note, we don't apply the loadinfo controller below if we create
-    // the ClientSource here.
   }
 
   // The load may have started controlling the Client as well.  If
   // so, mark it as controlled immediately here.  The actor may
   // or may not have been notified by the parent side about being
   // controlled yet.
-  //
-  // Note: We should be careful not to control a client that was created late.
-  //       These clients were not seen by the ServiceWorkerManager when it
-  //       marked the LoadInfo controlled and it won't know about them.  Its
-  //       also possible we are creating the client late due to the final
-  //       principal changing and these clients should definitely not be
-  //       controlled by a service worker with a different principal.
-  else if (loadInfo) {
+  if (loadInfo) {
     const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
     if (controller.isSome()) {
       mClientSource->SetController(controller.ref());
     }
 
     // We also have to handle the case where te initial about:blank is
     // controlled due to inheritting the service worker from its parent,
     // but the actual nsIChannel load is not covered by any service worker.
--- a/dom/clients/manager/ClientHandle.cpp
+++ b/dom/clients/manager/ClientHandle.cpp
@@ -60,26 +60,16 @@ ClientHandle::StartOp(const ClientOpCons
   }, [promise] {
     promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
   });
 
   RefPtr<ClientOpPromise> ref = promise.get();
   return ref.forget();
 }
 
-void
-ClientHandle::OnShutdownThing()
-{
-  NS_ASSERT_OWNINGTHREAD(ClientHandle);
-  if (!mDetachPromise) {
-    return;
-  }
-  mDetachPromise->Resolve(true, __func__);
-}
-
 ClientHandle::ClientHandle(ClientManager* aManager,
                            nsISerialEventTarget* aSerialEventTarget,
                            const ClientInfo& aClientInfo)
   : mManager(aManager)
   , mSerialEventTarget(aSerialEventTarget)
   , mClientInfo(aClientInfo)
 {
   MOZ_DIAGNOSTIC_ASSERT(mManager);
@@ -187,26 +177,10 @@ ClientHandle::PostMessage(StructuredClon
     }, [outerPromise](const ClientOpResult& aResult) {
       outerPromise->Reject(aResult.get_nsresult(), __func__);
     });
 
   ref = outerPromise.get();
   return ref.forget();
 }
 
-RefPtr<GenericPromise>
-ClientHandle::OnDetach()
-{
-  NS_ASSERT_OWNINGTHREAD(ClientSource);
-
-  if (!mDetachPromise) {
-    mDetachPromise = new GenericPromise::Private(__func__);
-    if (IsShutdown()) {
-      mDetachPromise->Resolve(true, __func__);
-    }
-  }
-
-  RefPtr<GenericPromise> ref(mDetachPromise);
-  return Move(ref);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientHandle.h
+++ b/dom/clients/manager/ClientHandle.h
@@ -37,31 +37,26 @@ class StructuredCloneData;
 // is destroyed, but this could be added in the future.
 class ClientHandle final : public ClientThing<ClientHandleChild>
 {
   friend class ClientManager;
   friend class ClientHandleChild;
 
   RefPtr<ClientManager> mManager;
   nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
-  RefPtr<GenericPromise::Private> mDetachPromise;
   ClientInfo mClientInfo;
 
   ~ClientHandle();
 
   void
   Shutdown();
 
   already_AddRefed<ClientOpPromise>
   StartOp(const ClientOpConstructorArgs& aArgs);
 
-  // ClientThing interface
-  void
-  OnShutdownThing() override;
-
   // Private methods called by ClientHandleChild
   void
   ExecutionReady(const ClientInfo& aClientInfo);
 
   // Private methods called by ClientManager
   ClientHandle(ClientManager* aManager,
                nsISerialEventTarget* aSerialEventTarget,
                const ClientInfo& aClientInfo);
@@ -90,26 +85,15 @@ public:
   // dispatched to the Client's navigator.serviceWorker event target.  The
   // returned promise will resolve if the MessageEvent is dispatched or if
   // it triggers an error handled in the Client's context.  Other errors
   // will result in the promise rejecting.
   RefPtr<GenericPromise>
   PostMessage(ipc::StructuredCloneData& aData,
               const ServiceWorkerDescriptor& aSource);
 
-  // Return a Promise that resolves when the ClientHandle object is detached
-  // from its remote actors.  This will happen if the ClientSource is destroyed
-  // and triggers the cleanup of the handle actors.  It will also naturally
-  // happen when the ClientHandle is de-referenced and tears down its own
-  // actors.
-  //
-  // Note: This method can only be called on the ClientHandle owning thread,
-  //       but the MozPromise lets you Then() to another thread.
-  RefPtr<GenericPromise>
-  OnDetach();
-
   NS_INLINE_DECL_REFCOUNTING(ClientHandle);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientHandle_h
--- a/dom/clients/manager/ClientInfo.cpp
+++ b/dom/clients/manager/ClientInfo.cpp
@@ -2,23 +2,21 @@
 /* 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 "ClientInfo.h"
 
 #include "mozilla/dom/ClientIPCTypes.h"
-#include "mozilla/ipc/BackgroundUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 using mozilla::ipc::PrincipalInfo;
-using mozilla::ipc::PrincipalInfoToPrincipal;
 
 ClientInfo::ClientInfo(const nsID& aId,
                        ClientType aType,
                        const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                        const TimeStamp& aCreationTime)
   : mData(MakeUnique<IPCClientInfo>(aId, aType, aPrincipalInfo, aCreationTime,
                                     EmptyCString(),
                                     mozilla::dom::FrameType::None))
@@ -135,18 +133,10 @@ ClientInfo::IsPrivateBrowsing() const
     default:
     {
       // clients should never be expanded principals
       MOZ_CRASH("unexpected principal type!");
     }
   }
 }
 
-nsCOMPtr<nsIPrincipal>
-ClientInfo::GetPrincipal() const
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  nsCOMPtr<nsIPrincipal> ref = PrincipalInfoToPrincipal(PrincipalInfo());
-  return Move(ref);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientInfo.h
+++ b/dom/clients/manager/ClientInfo.h
@@ -90,19 +90,14 @@ public:
 
   // Convert to the ipdl generated type.
   const IPCClientInfo&
   ToIPC() const;
 
   // Determine if the client is in private browsing mode.
   bool
   IsPrivateBrowsing() const;
-
-  // Get a main-thread nsIPrincipal for the client.  This may return nullptr
-  // if the PrincipalInfo() fails to deserialize for some reason.
-  nsCOMPtr<nsIPrincipal>
-  GetPrincipal() const;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientInfo_h
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -201,24 +201,16 @@ ClientSource::WorkerExecutionReady(Worke
 {
   MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
 
   if (IsShutdown()) {
     return;
   }
 
-  // A client without access to storage should never be controlled by
-  // a service worker.  Check this here in case we were controlled before
-  // execution ready.  We can't reliably determine what our storage policy
-  // is before execution ready, unfortunately.
-  if (mController.isSome()) {
-    MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate->IsStorageAllowed());
-  }
-
   // Its safe to store the WorkerPrivate* here because the ClientSource
   // is explicitly destroyed by WorkerPrivate before exiting its run loop.
   MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
   mOwner = AsVariant(aWorkerPrivate);
 
   ClientSourceExecutionReadyArgs args(
     aWorkerPrivate->GetLocationInfo().mHref,
     FrameType::None);
@@ -238,25 +230,16 @@ ClientSource::WindowExecutionReady(nsPID
     return NS_OK;
   }
 
   nsIDocument* doc = aInnerWindow->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  // A client without access to storage should never be controlled by
-  // a service worker.  Check this here in case we were controlled before
-  // execution ready.  We can't reliably determine what our storage policy
-  // is before execution ready, unfortunately.
-  if (mController.isSome()) {
-    MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(aInnerWindow) ==
-                          nsContentUtils::StorageAccess::eAllow);
-  }
-
   // Don't use nsAutoCString here since IPC requires a full nsCString anyway.
   nsCString spec;
 
   nsIURI* uri = doc->GetOriginalURI();
   if (uri) {
     nsresult rv = uri->GetSpec(spec);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -302,20 +285,16 @@ ClientSource::DocShellExecutionReady(nsI
     return NS_OK;
   }
 
   nsPIDOMWindowOuter* outer = aDocShell->GetWindow();
   if (NS_WARN_IF(!outer)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  // Note: We don't assert storage access for a controlled client.  If
-  // the about:blank actually gets used then WindowExecutionReady() will
-  // get called which asserts storage access.
-
   // TODO: dedupe this with WindowExecutionReady
   FrameType frameType = FrameType::Top_level;
   if (!outer->IsTopLevelWindow()) {
     frameType = FrameType::Nested;
   } else if(outer->HadOriginalOpener()) {
     frameType = FrameType::Auxiliary;
   }
 
@@ -376,26 +355,16 @@ ClientSource::SetController(const Servic
 {
   NS_ASSERT_OWNINGTHREAD(ClientSource);
 
   // A client in private browsing mode should never be controlled by
   // a service worker.  The principal origin attributes should guarantee
   // this invariant.
   MOZ_DIAGNOSTIC_ASSERT(!mClientInfo.IsPrivateBrowsing());
 
-  // A client without access to storage should never be controlled a
-  // a service worker.  If we are already execution ready with a real
-  // window or worker, then verify assert the storage policy is correct.
-  if (GetInnerWindow()) {
-    MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(GetInnerWindow()) ==
-                          nsContentUtils::StorageAccess::eAllow);
-  } else if (GetWorkerPrivate()) {
-    MOZ_DIAGNOSTIC_ASSERT(GetWorkerPrivate()->IsStorageAllowed());
-  }
-
   if (mController.isSome() && mController.ref() == aServiceWorker) {
     return;
   }
 
   mController.reset();
   mController.emplace(aServiceWorker);
 
   RefPtr<ServiceWorkerContainer> swc;
--- a/dom/clients/manager/ClientThing.h
+++ b/dom/clients/manager/ClientThing.h
@@ -85,42 +85,31 @@ protected:
 
     // If we are shutdown before the actor, then clear the weak references
     // between the actor and the thing.
     if (mActor) {
       mActor->RevokeOwner(this);
       mActor->MaybeStartTeardown();
       mActor = nullptr;
     }
-
-    OnShutdownThing();
-  }
-
-  // Allow extending classes to take action when shutdown.
-  virtual void
-  OnShutdownThing()
-  {
-    // by default do nothing
   }
 
 public:
   // Clear the weak references between the thing and its IPC actor.
   void
   RevokeActor(ActorType* aActor)
   {
     MOZ_DIAGNOSTIC_ASSERT(mActor);
     MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
     mActor->RevokeOwner(this);
     mActor = nullptr;
 
     // Also consider the ClientThing shutdown.  We simply set the flag
     // instead of calling ShutdownThing() to avoid calling MaybeStartTeardown()
     // on the destroyed actor.
     mShutdown = true;
-
-    OnShutdownThing();
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientThing_h
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -10,27 +10,16 @@ interface mozIDOMWindow;
 interface nsPIDOMWindowInner;
 interface mozIDOMWindowProxy;
 interface nsIArray;
 interface nsIDocument;
 interface nsIInterceptedChannel;
 interface nsIPrincipal;
 interface nsIRunnable;
 interface nsIURI;
-%{C++
-namespace mozilla {
-namespace dom {
-class ClientInfo;
-class ServiceWorkerDescriptor;
-} // namespace dom
-} // namespace mozilla
-%}
-
-[ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
-[ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
 
 [scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
   void unregisterFailed();
@@ -156,19 +145,16 @@ interface nsIServiceWorkerManager : nsIS
   /**
    * Call this to request that document `aDoc` be controlled by a ServiceWorker
    * if a registration exists for it's scope.
    *
    * This MUST only be called once per document!
    */
   [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
 
-  [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
-                                              in const_ServiceWorkerDescriptorRef aServiceWorker);
-
   /**
    * Documents that have called MaybeStartControlling() should call this when
    * they are destroyed. This function may be called multiple times, and is
    * idempotent.
    */
   [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
 
   /*
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -308,85 +308,16 @@ ServiceWorkerManager::Init(ServiceWorker
   if (!actor) {
     MaybeStartShutdown();
     return;
   }
 
   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
 }
 
-RefPtr<GenericPromise>
-ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
-                                             ServiceWorkerRegistrationInfo* aRegistrationInfo)
-{
-  MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
-
-  RefPtr<GenericPromise> ref;
-
-  const ServiceWorkerDescriptor& active =
-    aRegistrationInfo->GetActive()->Descriptor();
-
-  auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
-  if (entry) {
-    RefPtr<ServiceWorkerRegistrationInfo> old =
-      entry.Data()->mRegistrationInfo.forget();
-
-    ref = Move(entry.Data()->mClientHandle->Control(active));
-    entry.Data()->mRegistrationInfo = aRegistrationInfo;
-
-    if (old != aRegistrationInfo) {
-      StopControllingRegistration(old);
-      aRegistrationInfo->StartControllingClient();
-    }
-
-    Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
-
-    return Move(ref);
-  }
-
-  RefPtr<ClientHandle> clientHandle =
-    ClientManager::CreateHandle(aClientInfo,
-                                SystemGroup::EventTargetFor(TaskCategory::Other));
-
-  ref = Move(clientHandle->Control(active));
-
-  aRegistrationInfo->StartControllingClient();
-
-  entry.OrInsert([&] {
-    return new ControlledClientData(clientHandle, aRegistrationInfo);
-  });
-
-  RefPtr<ServiceWorkerManager> self(this);
-  clientHandle->OnDetach()->Then(
-    SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
-    [self = Move(self), aClientInfo] {
-      self->StopControllingClient(aClientInfo);
-    });
-
-  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
-
-  return Move(ref);
-}
-
-void
-ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
-{
-  auto entry = mControlledClients.Lookup(aClientInfo.Id());
-  if (!entry) {
-    return;
-  }
-
-  RefPtr<ServiceWorkerRegistrationInfo> reg =
-    entry.Data()->mRegistrationInfo.forget();
-
-  entry.Remove();
-
-  StopControllingRegistration(reg);
-}
-
 void
 ServiceWorkerManager::MaybeStartShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown) {
     return;
   }
@@ -1496,23 +1427,18 @@ ServiceWorkerManager::GetActiveWorkerInf
   return registration->GetActive();
 }
 
 ServiceWorkerInfo*
 ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
 {
   AssertIsOnMainThread();
 
-  Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
-  if (clientInfo.isNothing()) {
-    return nullptr;
-  }
-
   RefPtr<ServiceWorkerRegistrationInfo> registration;
-  GetClientRegistration(clientInfo.ref(), getter_AddRefs(registration));
+  GetDocumentRegistration(aDocument, getter_AddRefs(registration));
 
   if (!registration) {
     return nullptr;
   }
 
   return registration->GetActive();
 }
 
@@ -1647,17 +1573,17 @@ ServiceWorkerManager::WorkerIsIdle(Servi
   if (!reg) {
     return;
   }
 
   if (reg->GetActive() != aWorker) {
     return;
   }
 
-  if (!reg->IsControllingClients() && reg->mPendingUninstall) {
+  if (!reg->IsControllingDocuments() && reg->mPendingUninstall) {
     RemoveRegistration(reg);
     return;
   }
 
   reg->TryToActivateAsync();
 }
 
 already_AddRefed<ServiceWorkerJobQueue>
@@ -2338,35 +2264,25 @@ ServiceWorkerManager::RemoveScopeAndRegi
     return;
   }
 
   if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
     entry.Data()->Cancel();
     entry.Remove();
   }
 
-  // Verify there are no controlled clients for the purged registration.
-  for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
-    auto& reg = iter.UserData()->mRegistrationInfo;
-    if (reg->mScope.Equals(aRegistration->mScope)) {
-      MOZ_DIAGNOSTIC_ASSERT(false,
-                            "controlled client when removing registration");
-      iter.Remove();
-      break;
-    }
-  }
-
-  // Registration lifecycle is managed via mControlledClients now.  Do not
-  // assert on on mControlledDocuments as races may cause this to still be
-  // set when the registration is destroyed.
+  // The registration should generally only be removed if there are no controlled
+  // documents, but mControlledDocuments can contain references to potentially
+  // controlled docs.  This happens when the service worker is not active yet.
+  // We must purge these references since we are evicting the registration.
   for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
     ServiceWorkerRegistrationInfo* reg = iter.UserData();
+    MOZ_ASSERT(reg);
     if (reg->mScope.Equals(aRegistration->mScope)) {
       iter.Remove();
-      break;
     }
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
   swm->NotifyListenersOnUnregister(info);
 
@@ -2387,89 +2303,97 @@ ServiceWorkerManager::MaybeRemoveRegistr
 
 void
 ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aDoc);
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aDoc);
-  if (registration && registration->GetActive() &&
-      aDoc->GetSandboxFlags() == 0) {
+  if (registration) {
     MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
     StartControllingADocument(registration, aDoc);
   }
 }
 
-bool
-ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
-                                       const ServiceWorkerDescriptor& aServiceWorker)
-{
-  AssertIsOnMainThread();
-
-  nsCOMPtr<nsIPrincipal> principal =
-    PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
-  NS_ENSURE_TRUE(principal, false);
-
-  nsCOMPtr<nsIURI> scope;
-  nsresult rv =
-    NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  RefPtr<ServiceWorkerRegistrationInfo> registration =
-    GetServiceWorkerRegistrationInfo(principal, scope);
-  NS_ENSURE_TRUE(registration, false);
-
-  StartControllingClient(aClientInfo, registration);
-
-  return true;
-}
-
 void
 ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aDoc);
-  mControlledDocuments.Remove(aDoc);
+  RefPtr<ServiceWorkerRegistrationInfo> registration;
+  mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
+  // A document which was uncontrolled does not maintain that state itself, so
+  // it will always call MaybeStopControlling() even if there isn't an
+  // associated registration. So this check is required.
+  if (registration) {
+    StopControllingADocument(registration);
+  }
 }
 
 void
-ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
+ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aDoc);
   // We perform these success path navigation update steps when the
   // document tells us its more or less done loading.  This avoids
   // slowing down page load and also lets pages consistently get
   // updatefound events when they fire.
   //
   // 9.8.20 If respondWithEntered is false, then:
   // 9.8.22 Else: (respondWith was entered and succeeded)
   //    If request is a non-subresource request, then: Invoke Soft Update
   //    algorithm.
-  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
-  if (data && data->mRegistrationInfo) {
-    data->mRegistrationInfo->MaybeScheduleUpdate();
+  RefPtr<ServiceWorkerRegistrationInfo> registration;
+  mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
+  if (registration) {
+    registration->MaybeScheduleUpdate();
   }
 }
 
-void
+RefPtr<GenericPromise>
 ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                                                 nsIDocument* aDoc)
 {
   MOZ_ASSERT(aRegistration);
   MOZ_ASSERT(aDoc);
 
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+  MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
+#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
+  RefPtr<GenericPromise> ref = GenericPromise::CreateAndResolve(true, __func__);
+
+  aRegistration->StartControllingADocument();
   mControlledDocuments.Put(aDoc, aRegistration);
+
+  // Mark the document's ClientSource as controlled using the ClientHandle
+  // interface.  While we could get at the ClientSource directly from the
+  // document here, our goal is to move ServiceWorkerManager to a separate
+  // process.  Using the ClientHandle supports this remote operation.
+  ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
+  Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
+  if (activeWorker && clientInfo.isSome()) {
+    RefPtr<ClientHandle> clientHandle =
+      ClientManager::CreateHandle(clientInfo.ref(),
+                                  SystemGroup::EventTargetFor(TaskCategory::Other));
+    ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+  }
+
+  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+  return Move(ref);
 }
 
 void
-ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
+ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
 {
-  aRegistration->StopControllingClient();
-  if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
+  aRegistration->StopControllingADocument();
+  if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
     return;
   }
 
   if (aRegistration->mPendingUninstall) {
     RemoveRegistration(aRegistration);
     return;
   }
 
@@ -2789,17 +2713,20 @@ ServiceWorkerManager::DispatchFetchEvent
         //       initial about:blank global.  See bug 1419620 and the spec
         //       issue here: https://github.com/w3c/ServiceWorker/issues/1232
       }
 
       if (clientInfo.isSome()) {
         // First, attempt to mark the reserved client controlled directly.  This
         // will update the controlled status in the ClientManagerService in the
         // parent.  It will also eventually propagate back to the ClientSource.
-        StartControllingClient(clientInfo.ref(), registration);
+        RefPtr<ClientHandle> clientHandle =
+          ClientManager::CreateHandle(clientInfo.ref(),
+                                      SystemGroup::EventTargetFor(TaskCategory::Other));
+        clientHandle->Control(serviceWorker->Descriptor());
       }
 
       // But we also note the reserved state on the LoadInfo.  This allows the
       // ClientSource to be updated immediately after the nsIChannel starts.
       // This is necessary to have the correct controller in place for immediate
       // follow-on requests.
       loadInfo->SetController(serviceWorker->Descriptor());
     }
@@ -2853,31 +2780,30 @@ ServiceWorkerManager::IsAvailable(nsIPri
   MOZ_ASSERT(aURI);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
   return registration && registration->GetActive();
 }
 
 nsresult
-ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo,
-                                            ServiceWorkerRegistrationInfo** aRegistrationInfo)
+ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
+                                              ServiceWorkerRegistrationInfo** aRegistrationInfo)
 {
-  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
-  if (!data || !data->mRegistrationInfo) {
+  RefPtr<ServiceWorkerRegistrationInfo> registration;
+  if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // If the document is controlled, the current worker MUST be non-null.
-  if (!data->mRegistrationInfo->GetActive()) {
+  if (!registration->GetActive()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
-  ref.forget(aRegistrationInfo);
+  registration.forget(aRegistrationInfo);
   return NS_OK;
 }
 
 /*
  * The .controller is for the registration associated with the document when
  * the document was loaded.
  */
 NS_IMETHODIMP
@@ -3238,40 +3164,35 @@ ServiceWorkerManager::MaybeClaimClient(n
   RefPtr<GenericPromise> ref;
 
   // Same origin check
   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
     return ref.forget();
   }
 
-  Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
-  if (NS_WARN_IF(clientInfo.isNothing())) {
-    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
-                                          __func__);
-    return ref.forget();
-  }
-
   // The registration that should be controlling the client
   RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
     GetServiceWorkerRegistrationInfo(aDocument);
 
   // The registration currently controlling the client
   RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
-  GetClientRegistration(clientInfo.ref(),
-                        getter_AddRefs(controllingRegistration));
+  GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
 
   if (aWorkerRegistration != matchingRegistration ||
       aWorkerRegistration == controllingRegistration) {
     ref = GenericPromise::CreateAndResolve(true, __func__);
     return ref.forget();
   }
 
-  StartControllingADocument(aWorkerRegistration, aDocument);
-  ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
+  if (controllingRegistration) {
+    StopControllingADocument(controllingRegistration);
+  }
+
+  ref = StartControllingADocument(aWorkerRegistration, aDocument);
   return ref.forget();
 }
 
 already_AddRefed<GenericPromise>
 ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
                                        const ServiceWorkerDescriptor& aServiceWorker)
 {
   RefPtr<GenericPromise> ref;
@@ -3322,29 +3243,41 @@ ServiceWorkerManager::SetSkipWaitingFlag
 void
 ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
 {
   AssertIsOnMainThread();
 
   RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
   MOZ_DIAGNOSTIC_ASSERT(activeWorker);
 
-  AutoTArray<RefPtr<ClientHandle>, 16> handleList;
-  for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) {
-    if (iter.UserData()->mRegistrationInfo != aRegistration) {
+  AutoTArray<nsCOMPtr<nsIDocument>, 16> docList;
+  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+    if (iter.UserData() != aRegistration) {
+      continue;
+    }
+
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+    if (NS_WARN_IF(!doc)) {
       continue;
     }
 
-    handleList.AppendElement(iter.UserData()->mClientHandle);
+    docList.AppendElement(doc.forget());
   }
 
-  // Fire event after iterating mControlledClients is done to prevent
+  // Fire event after iterating mControlledDocuments is done to prevent
   // modification by reentering from the event handlers during iteration.
-  for (auto& handle : handleList) {
-    handle->Control(activeWorker->Descriptor());
+  for (auto& doc : docList) {
+    Maybe<ClientInfo> clientInfo = doc->GetClientInfo();
+    if (clientInfo.isNothing()) {
+      continue;
+    }
+    RefPtr<ClientHandle> clientHandle =
+      ClientManager::CreateHandle(clientInfo.ref(),
+                                  SystemGroup::EventTargetFor(TaskCategory::Other));
+    clientHandle->Control(activeWorker->Descriptor());
   }
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
                                       const nsACString& aScope) const
 {
   MOZ_ASSERT(aPrincipal);
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -16,17 +16,16 @@
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/ClientHandle.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerCommon.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
 #include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
@@ -101,31 +100,16 @@ public:
 
   struct RegistrationDataPerPrincipal;
   nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
 
   nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
 
   nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
 
-  struct ControlledClientData
-  {
-    RefPtr<ClientHandle> mClientHandle;
-    RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
-
-    ControlledClientData(ClientHandle* aClientHandle,
-                         ServiceWorkerRegistrationInfo* aRegistrationInfo)
-      : mClientHandle(aClientHandle)
-      , mRegistrationInfo(aRegistrationInfo)
-    {
-    }
-  };
-
-  nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
-
   // Track all documents that have attempted to register a service worker for a
   // given scope.
   typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
   nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
 
   // Track all intercepted navigation channels for a given scope.  Channels are
   // placed in the appropriate list before dispatch the FetchEvent to the worker
   // thread and removed once FetchEvent processing dispatches back to the main
@@ -315,17 +299,17 @@ public:
   AddRegistrationEventListener(const nsAString& aScope,
                                ServiceWorkerRegistrationListener* aListener);
 
   NS_IMETHOD
   RemoveRegistrationEventListener(const nsAString& aScope,
                                   ServiceWorkerRegistrationListener* aListener);
 
   void
-  MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
+  MaybeCheckNavigationUpdate(nsIDocument* aDoc);
 
   nsresult
   SendPushEvent(const nsACString& aOriginAttributes,
                 const nsACString& aScope,
                 const nsAString& aMessageId,
                 const Maybe<nsTArray<uint8_t>>& aData);
 
   nsresult
@@ -339,23 +323,16 @@ public:
 
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   Init(ServiceWorkerRegistrar* aRegistrar);
 
-  RefPtr<GenericPromise>
-  StartControllingClient(const ClientInfo& aClientInfo,
-                         ServiceWorkerRegistrationInfo* aRegistrationInfo);
-
-  void
-  StopControllingClient(const ClientInfo& aClientInfo);
-
   void
   MaybeStartShutdown();
 
   already_AddRefed<ServiceWorkerJobQueue>
   GetOrCreateJobQueue(const nsACString& aOriginSuffix,
                       const nsACString& aScope);
 
   void
@@ -367,18 +344,18 @@ private:
 
   void
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   Update(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
-  GetClientRegistration(const ClientInfo& aClientInfo,
-                        ServiceWorkerRegistrationInfo** aRegistrationInfo);
+  GetDocumentRegistration(nsIDocument* aDoc,
+                          ServiceWorkerRegistrationInfo** aRegistrationInfo);
 
   nsresult
   GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
                            const nsAString& aScope,
                            WhichServiceWorker aWhichWorker,
                            nsISupports** aServiceWorker);
 
   ServiceWorkerInfo*
@@ -393,22 +370,22 @@ private:
                                             WhichServiceWorker aWhichOne);
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
 
-  void
+  RefPtr<GenericPromise>
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
 
   void
-  StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+  StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
@@ -76,30 +76,32 @@ ServiceWorkerRegistrationInfo::Clear()
 
   NotifyChromeRegistrationListeners();
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
     const nsACString& aScope,
     nsIPrincipal* aPrincipal,
     ServiceWorkerUpdateViaCache aUpdateViaCache)
-  : mControlledClientsCounter(0)
+  : mControlledDocumentsCounter(0)
   , mUpdateState(NoUpdate)
   , mCreationTime(PR_Now())
   , mCreationTimeStamp(TimeStamp::Now())
   , mLastUpdateTime(0)
   , mUpdateViaCache(aUpdateViaCache)
   , mScope(aScope)
   , mPrincipal(aPrincipal)
   , mPendingUninstall(false)
 {}
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
-  MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
+  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.");
+  }
 }
 
 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
 
 NS_IMETHODIMP
 ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal)
 {
   AssertIsOnMainThread();
@@ -241,17 +243,17 @@ ServiceWorkerRegistrationInfo::TryToActi
 
 /*
  * TryToActivate should not be called directly, use TryToActivateAsync instead.
  */
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
   AssertIsOnMainThread();
-  bool controlling = IsControllingClients();
+  bool controlling = IsControllingDocuments();
   bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag();
   bool idle = IsIdle();
   if (idle && (!controlling || skipWaiting)) {
     Activate();
   }
 }
 
 void
--- a/dom/workers/ServiceWorkerRegistrationInfo.h
+++ b/dom/workers/ServiceWorkerRegistrationInfo.h
@@ -12,17 +12,17 @@
 
 namespace mozilla {
 namespace dom {
 namespace workers {
 
 class ServiceWorkerRegistrationInfo final
   : public nsIServiceWorkerRegistrationInfo
 {
-  uint32_t mControlledClientsCounter;
+  uint32_t mControlledDocumentsCounter;
 
   enum
   {
     NoUpdate,
     NeedTimeCheckAndUpdate,
     NeedUpdate
   } mUpdateState;
 
@@ -74,32 +74,32 @@ public:
 
     return newest.forget();
   }
 
   already_AddRefed<ServiceWorkerInfo>
   GetServiceWorkerInfoById(uint64_t aId);
 
   void
-  StartControllingClient()
+  StartControllingADocument()
   {
-    ++mControlledClientsCounter;
+    ++mControlledDocumentsCounter;
   }
 
   void
-  StopControllingClient()
+  StopControllingADocument()
   {
-    MOZ_ASSERT(mControlledClientsCounter);
-    --mControlledClientsCounter;
+    MOZ_ASSERT(mControlledDocumentsCounter);
+    --mControlledDocumentsCounter;
   }
 
   bool
-  IsControllingClients() const
+  IsControllingDocuments() const
   {
-    return mActiveWorker && mControlledClientsCounter;
+    return mActiveWorker && mControlledDocumentsCounter;
   }
 
   void
   Clear();
 
   void
   TryToActivateAsync();
 
--- a/dom/workers/ServiceWorkerUnregisterJob.cpp
+++ b/dom/workers/ServiceWorkerUnregisterJob.cpp
@@ -133,17 +133,17 @@ ServiceWorkerUnregisterJob::Unregister()
   // "Set registration's uninstalling flag."
   registration->mPendingUninstall = true;
 
   // "Resolve promise with true"
   mResult = true;
   InvokeResultCallbacks(NS_OK);
 
   // "If no service worker client is using registration..."
-  if (!registration->IsControllingClients() && registration->IsIdle()) {
+  if (!registration->IsControllingDocuments() && registration->IsIdle()) {
     // "Invoke [[Clear Registration]]..."
     swm->RemoveRegistration(registration);
   }
 
   Finish(NS_OK);
 }
 
 } // namespace workers
--- a/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
+++ b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
@@ -14,16 +14,20 @@
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   if (!parent) {
     info("skip_waiting_scope/index.html shouldn't be launched directly!");
   }
 
+  navigator.serviceWorker.ready.then(function() {
+    parent.postMessage("READY", "*");
+  });
+
   navigator.serviceWorker.oncontrollerchange = function() {
     parent.postMessage({
       event: "controllerchange",
       controllerScriptURL: navigator.serviceWorker.controller &&
                            navigator.serviceWorker.controller.scriptURL
     }, "*");
   }
 
--- a/dom/workers/test/serviceworkers/test_skip_waiting.html
+++ b/dom/workers/test/serviceworkers/test_skip_waiting.html
@@ -8,36 +8,45 @@
   <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
-<script src="utils.js"></script>
 <script class="testbody" type="text/javascript">
   var registration, iframe, content;
 
   function start() {
     return navigator.serviceWorker.register("worker.js",
                                             {scope: "./skip_waiting_scope/"});
   }
 
-  async function waitForActivated(swr) {
+  function waitForActivated(swr) {
     registration = swr;
-    await waitForState(registration.installing, "activated")
+    var promise = new Promise(function(resolve, reject) {
+      window.onmessage = function(e) {
+        if (e.data === "READY") {
+          ok(true, "Active worker is activated now");
+          resolve();
+        } else {
+          ok(false, "Wrong value. Somenting went wrong");
+          resolve();
+        }
+      }
+    });
 
     iframe = document.createElement("iframe");
     iframe.setAttribute("src", "skip_waiting_scope/index.html");
 
     content = document.getElementById("content");
     content.appendChild(iframe);
 
-    await new Promise(resolve => iframe.onload = resolve);
+    return promise;
   }
 
   function checkWhetherItSkippedWaiting() {
     var promise = new Promise(function(resolve, reject) {
       window.onmessage = function (evt) {
         if (evt.data.event === "controllerchange") {
           ok(evt.data.controllerScriptURL.match("skip_waiting_installed_worker"),
              "The controller changed after skiping the waiting step");
--- a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
+++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
@@ -8,25 +8,24 @@
   <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content"></div>
 <pre id="test"></pre>
-<script src="utils.js"></script>
 <script class="testbody" type="text/javascript">
   var registration;
   var promise;
 
-  async function start() {
-    registration = await navigator.serviceWorker.register("worker_updatefoundevent.js",
-                                                          { scope: "./updatefoundevent.html" })
-    await waitForState(registration.installing, 'activated');
+  function start() {
+    return navigator.serviceWorker.register("worker_updatefoundevent.js",
+                                            { scope: "./updatefoundevent.html" })
+      .then((swr) => registration = swr);
   }
 
   function startWaitForUpdateFound() {
     registration.onupdatefound = function(e) {
     }
 
     promise = new Promise(function(resolve, reject) {
       window.onmessage = function(e) {
--- a/dom/workers/test/serviceworkers/worker_updatefoundevent.js
+++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
@@ -1,18 +1,23 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-registration.onupdatefound = function(e) {
-  clients.matchAll().then(function(clients) {
-    if (!clients.length) {
-      reject("No clients found");
-    }
+onactivate = function(e) {
+  e.waitUntil(new Promise(function(resolve, reject) {
+    registration.onupdatefound = function(e) {
+      clients.matchAll().then(function(clients) {
+        if (!clients.length) {
+          reject("No clients found");
+        }
 
-    if (registration.scope.match(/updatefoundevent\.html$/)) {
-      clients[0].postMessage("finish");
-    } else {
-      dump("Scope did not match");
+        if (registration.scope.match(/updatefoundevent\.html$/)) {
+          clients[0].postMessage("finish");
+          resolve();
+        } else {
+          dump("Scope did not match");
+        }
+      }, reject);
     }
-  });
+  }));
 }
--- a/testing/web-platform/tests/service-workers/service-worker/unregister-then-register-new-script.https.html
+++ b/testing/web-platform/tests/service-workers/service-worker/unregister-then-register-new-script.https.html
@@ -85,51 +85,37 @@ async_test(function(t) {
       .then(function() {
           return with_iframe(scope);
         })
       .then(function(frame) {
           iframe = frame;
           return registration.unregister();
         })
       .then(function() {
-          // Step 5.1 of Register clears the uninstall flag before fetching
-          // the script:
-          //
-          //  https://w3c.github.io/ServiceWorker/#register-algorithm
           var promise = navigator.serviceWorker.register('this-will-404',
                                                          { scope: scope });
+          iframe.remove();
           return promise;
         })
       .then(
         function() {
           assert_unreached('register should reject the promise');
         },
         function() {
-          assert_equals(registration.installing, null,
-                        'registration.installing');
-          assert_equals(registration.waiting, null,
-                        'registration.waiting');
-          assert_equals(registration.active.scriptURL, normalizeURL(worker_url),
-                        'registration.active');
-          iframe.remove();
           return with_iframe(scope);
         })
       .then(function(frame) {
-          assert_equals(
-              frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
-              normalizeURL(worker_url),
-              'the original worker should control a new document');
+          assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
+                        null,
+                        'document should not load with a controller');
           frame.remove();
-          return registration.unregister();
-        })
-      .then(function() {
           t.done();
         })
       .catch(unreached_rejection(t));
-}, 'Registering a new script URL that 404s does resurrect an ' +
+}, 'Registering a new script URL that 404s does not resurrect an ' +
        'unregistered registration');
 
 async_test(function(t) {
     var scope = 'resources/scope/unregister-then-register-reject-install-worker';
     var iframe;
     var registration;
 
     service_worker_unregister_and_register(t, worker_url, scope)
@@ -140,45 +126,34 @@ async_test(function(t) {
       .then(function() {
           return with_iframe(scope);
         })
       .then(function(frame) {
           iframe = frame;
           return registration.unregister();
         })
       .then(function() {
-          // Step 5.1 of Register clears the uninstall flag before firing
-          // the install event:
-          //
-          //  https://w3c.github.io/ServiceWorker/#register-algorithm
           var promise = navigator.serviceWorker.register(
               'resources/reject-install-worker.js', { scope: scope });
+          iframe.remove();
           return promise;
         })
       .then(function(r) {
           registration = r;
           return wait_for_state(t, r.installing, 'redundant');
         })
       .then(function() {
-          assert_equals(registration.installing, null,
-                        'registration.installing');
-          assert_equals(registration.waiting, null,
-                        'registration.waiting');
-          assert_equals(registration.active.scriptURL, normalizeURL(worker_url),
-                        'registration.active');
-          iframe.remove();
           return with_iframe(scope);
         })
       .then(function(frame) {
-          assert_equals(
-              frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
-              normalizeURL(worker_url),
-              'the original worker should control a new document');
+          assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
+                        null,
+                        'document should not load with a controller');
           frame.remove();
           return registration.unregister();
         })
       .then(function() {
           t.done();
         })
       .catch(unreached_rejection(t));
-  }, 'Registering a new script URL that fails to install does resurrect ' +
+  }, 'Registering a new script URL that fails to install does not resurrect ' +
        'an unregistered registration');
 </script>