Backed out changeset 2c01f22661fa (bug 1263734) for wpt failures in fetch/api/request/destination/fetch-destination-no-load-event.https.html. CLOSED TREE
authorDorel Luca <dluca@mozilla.com>
Wed, 10 Oct 2018 18:31:01 +0300
changeset 440488 db4990bc9b4cefd8f4e85a17de49796093cf6e90
parent 440487 68f1999b78a1bc6f33ce2fa9e578697cfd794c89
child 440489 3ca5afad36517c9cd2ce76a8ebc06c1b65620555
push id70694
push userdluca@mozilla.com
push dateWed, 10 Oct 2018 15:31:21 +0000
treeherderautoland@db4990bc9b4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1263734
milestone64.0a1
backs out2c01f22661fabd177c81e658e93319bc4faba8df
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 changeset 2c01f22661fa (bug 1263734) for wpt failures in fetch/api/request/destination/fetch-destination-no-load-event.https.html. CLOSED TREE
dom/base/nsDocument.cpp
dom/clients/manager/ClientSource.cpp
dom/serviceworkers/ServiceWorkerContainer.cpp
dom/serviceworkers/ServiceWorkerContainer.h
dom/webidl/ServiceWorkerContainer.webidl
testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html
testing/web-platform/tests/fetch/api/request/destination/resources/fetch-destination-worker-no-load-event.js
testing/web-platform/tests/service-workers/service-worker/postmessage-to-client.https.html
testing/web-platform/tests/service-workers/service-worker/resources/about-blank-replacement-ping-frame.py
testing/web-platform/tests/service-workers/service-worker/resources/enable-client-message-queue.html
testing/web-platform/tests/service-workers/service-worker/resources/message-vs-microtask.html
testing/web-platform/tests/service-workers/service-worker/resources/nested-iframe-parent.html
testing/web-platform/tests/service-workers/service-worker/resources/stalling-service-worker.js
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -62,18 +62,16 @@
 
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/dom/FramingChecker.h"
 #include "mozilla/dom/HTMLSharedElement.h"
-#include "mozilla/dom/Navigator.h"
-#include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/SVGUseElement.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "nsDOMString.h"
 #include "nsNodeUtils.h"
 #include "nsLayoutUtils.h" // for GetFrameForPoint
 #include "nsIFrame.h"
@@ -5192,24 +5190,16 @@ nsIDocument::DispatchContentLoadedEvents
 
   // Fire a DOM event notifying listeners that this document has been
   // loaded (excluding images and other loads initiated by this
   // document).
   nsContentUtils::DispatchTrustedEvent(this, this,
                                        NS_LITERAL_STRING("DOMContentLoaded"),
                                        CanBubble::eYes, Cancelable::eNo);
 
-  if (auto* const window = GetInnerWindow()) {
-    const RefPtr<ServiceWorkerContainer> serviceWorker = window->Navigator()->ServiceWorker();
-
-    // This could cause queued messages from a service worker to get
-    // dispatched on serviceWorker.
-    serviceWorker->StartMessages();
-  }
-
   if (MayStartLayout()) {
     MaybeResolveReadyForIdle();
   }
 
   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
   nsIDocShell* docShell = this->GetDocShell();
 
   if (timelines && timelines->HasConsumer(docShell)) {
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -600,27 +600,135 @@ ClientSource::Focus(const ClientFocusArg
   ref = ClientOpPromise::CreateAndResolve(state.ToIPC(), __func__);
   return ref.forget();
 }
 
 RefPtr<ClientOpPromise>
 ClientSource::PostMessage(const ClientPostMessageArgs& aArgs)
 {
   NS_ASSERT_OWNINGTHREAD(ClientSource);
+  RefPtr<ClientOpPromise> ref;
 
-  // TODO: Currently this function only supports clients whose global
-  // object is a Window; it should also support those whose global
-  // object is a WorkerGlobalScope.
-  if (nsPIDOMWindowInner* const window = GetInnerWindow()) {
-    const RefPtr<ServiceWorkerContainer> container = window->Navigator()->ServiceWorker();
-    container->ReceiveMessage(aArgs);
-    return ClientOpPromise::CreateAndResolve(NS_OK, __func__).forget();
+  ServiceWorkerDescriptor source(aArgs.serviceWorker());
+  const PrincipalInfo& principalInfo = source.PrincipalInfo();
+
+  StructuredCloneData clonedData;
+  clonedData.BorrowFromClonedMessageDataForBackgroundChild(aArgs.clonedData());
+
+  // Currently we only support firing these messages on window Clients.
+  // Once we expose ServiceWorkerContainer and the ServiceWorker on Worker
+  // threads then this will need to change.  See bug 1113522.
+  if (mClientInfo.Type() != ClientType::Window) {
+    ref = ClientOpPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
+    return ref.forget();
+  }
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<ServiceWorkerContainer> target;
+  nsCOMPtr<nsIGlobalObject> globalObject;
+
+  // We don't need to force the creation of the about:blank document
+  // here because there is no postMessage listener.  If a listener
+  // was registered then the document will already be created.
+  nsPIDOMWindowInner* window = GetInnerWindow();
+  if (window) {
+    globalObject = do_QueryInterface(window);
+    target = window->Navigator()->ServiceWorker();
+  }
+
+  if (NS_WARN_IF(!target)) {
+    ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                                           __func__);
+    return ref.forget();
+  }
+
+  // If AutoJSAPI::Init() fails then either global is nullptr or not
+  // in a usable state.
+  AutoJSAPI jsapi;
+  if (!jsapi.Init(globalObject)) {
+    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+    return ref.forget();
+  }
+
+  JSContext* cx = jsapi.cx();
+
+  ErrorResult result;
+  JS::Rooted<JS::Value> messageData(cx);
+  clonedData.Read(cx, &messageData, result);
+  if (result.MaybeSetPendingException(cx)) {
+    // We reported the error in the current window context.  Resolve
+    // promise instead of rejecting.
+    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+    return ref.forget();
   }
 
-  return ClientOpPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__).forget();
+  RootedDictionary<MessageEventInit> init(cx);
+
+  init.mData = messageData;
+  if (!clonedData.TakeTransferredPortsAsSequence(init.mPorts)) {
+    // Report the error in the current window context and resolve the
+    // promise instead of rejecting.
+    xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
+    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+    return ref.forget();
+  }
+
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(principalInfo, &rv);
+  if (NS_FAILED(rv) || !principal) {
+    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+    return ref.forget();
+  }
+
+  nsAutoCString origin;
+  rv = principal->GetOriginNoSuffix(origin);
+  if (NS_SUCCEEDED(rv)) {
+    CopyUTF8toUTF16(origin, init.mOrigin);
+  }
+
+  RefPtr<ServiceWorker> instance;
+
+  if (ServiceWorkerParentInterceptEnabled()) {
+    instance = globalObject->GetOrCreateServiceWorker(source);
+  } else {
+    // If we are in legacy child-side intercept mode then we need to verify
+    // this registration exists in the current process.
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (!swm) {
+      // Shutting down. Just don't deliver this message.
+      ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+      return ref.forget();
+    }
+
+    RefPtr<ServiceWorkerRegistrationInfo> reg =
+      swm->GetRegistration(principal, source.Scope());
+    if (reg) {
+      instance = globalObject->GetOrCreateServiceWorker(source);
+    }
+  }
+
+  if (instance) {
+    init.mSource.SetValue().SetAsServiceWorker() = instance;
+  }
+
+  RefPtr<MessageEvent> event =
+    MessageEvent::Constructor(target, NS_LITERAL_STRING("message"), init);
+  event->SetTrusted(true);
+
+  target->DispatchEvent(*event, result);
+  if (result.Failed()) {
+    result.SuppressException();
+    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+    return ref.forget();
+  }
+
+  ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+  return ref.forget();
 }
 
 RefPtr<ClientOpPromise>
 ClientSource::Claim(const ClientClaimArgs& aArgs)
 {
   // The ClientSource::Claim method is only needed in the legacy
   // mode where the ServiceWorkerManager is run in each child-process.
   // In parent-process mode this method should not be called.
--- a/dom/serviceworkers/ServiceWorkerContainer.cpp
+++ b/dom/serviceworkers/ServiceWorkerContainer.cpp
@@ -7,49 +7,38 @@
 #include "ServiceWorkerContainer.h"
 
 #include "nsContentPolicyUtils.h"
 #include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsIServiceWorkerManager.h"
 #include "nsIScriptError.h"
-#include "nsThreadUtils.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPrefs.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/LoadInfo.h"
-#include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/DOMMozPromiseRequestHolder.h"
-#include "mozilla/dom/MessageEvent.h"
-#include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerContainerBinding.h"
-#include "mozilla/dom/ServiceWorkerManager.h"
-#include "mozilla/dom/ipc/StructuredCloneData.h"
 
 #include "RemoteServiceWorkerContainerImpl.h"
 #include "ServiceWorker.h"
 #include "ServiceWorkerContainerImpl.h"
 #include "ServiceWorkerRegistration.h"
 #include "ServiceWorkerUtils.h"
 
-// This is defined to something else on Windows
-#ifdef DispatchMessage
-  #undef DispatchMessage
-#endif
-
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerContainer)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
@@ -158,49 +147,16 @@ ServiceWorkerContainer::ControllerChange
   if (!go) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   mControllerWorker = go->GetOrCreateServiceWorker(go->GetController().ref());
   aRv = DispatchTrustedEvent(NS_LITERAL_STRING("controllerchange"));
 }
 
-using mozilla::dom::ipc::StructuredCloneData;
-
-// A ReceivedMessage represents a message sent via
-// Client.postMessage(). It is used as used both for queuing of
-// incoming messages and as an interface to DispatchMessage().
-struct MOZ_HEAP_CLASS ServiceWorkerContainer::ReceivedMessage
-{
-  explicit ReceivedMessage(const ClientPostMessageArgs& aArgs)
-    : mServiceWorker(aArgs.serviceWorker())
-  {
-    mClonedData.CopyFromClonedMessageDataForBackgroundChild(aArgs.clonedData());
-  }
-
-  ServiceWorkerDescriptor mServiceWorker;
-  StructuredCloneData mClonedData;
-
-  NS_INLINE_DECL_REFCOUNTING(ReceivedMessage)
-
-private:
-  ~ReceivedMessage() = default;
-};
-
-void
-ServiceWorkerContainer::ReceiveMessage(const ClientPostMessageArgs& aArgs)
-{
-  RefPtr<ReceivedMessage> message = new ReceivedMessage(aArgs);
-  if (mMessagesStarted) {
-    EnqueueReceivedMessageDispatch(message.forget());
-  } else {
-    mPendingMessages.AppendElement(message.forget());
-  }
-}
-
 JSObject*
 ServiceWorkerContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ServiceWorkerContainer_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 namespace {
 
@@ -465,26 +421,16 @@ ServiceWorkerContainer::GetRegistrations
       outer->MaybeResolve(regList);
     }, [self, outer] (ErrorResult& aRv) {
       outer->MaybeReject(aRv);
     });
 
   return outer.forget();
 }
 
-void
-ServiceWorkerContainer::StartMessages()
-{
-  while (!mPendingMessages.IsEmpty()) {
-    EnqueueReceivedMessageDispatch(mPendingMessages.ElementAt(0));
-    mPendingMessages.RemoveElementAt(0);
-  }
-  mMessagesStarted = true;
-}
-
 already_AddRefed<Promise>
 ServiceWorkerContainer::GetRegistration(const nsAString& aURL,
                                         ErrorResult& aRv)
 {
   nsIGlobalObject* global = GetGlobalIfValid(aRv, [](nsIDocument* aDoc) {
     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                     NS_LITERAL_CSTRING("Service Workers"), aDoc,
                                     nsContentUtils::eDOM_PROPERTIES,
@@ -667,158 +613,10 @@ ServiceWorkerContainer::GetGlobalIfValid
   if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   return window->AsGlobal();
 }
 
-void
-ServiceWorkerContainer::EnqueueReceivedMessageDispatch(RefPtr<ReceivedMessage> aMessage) {
-  if (nsPIDOMWindowInner* const window = GetOwner()) {
-    if (auto* const target = window->EventTargetFor(TaskCategory::Other)) {
-      target->Dispatch(
-        NewRunnableMethod<RefPtr<ReceivedMessage>>(
-          "ServiceWorkerContainer::DispatchMessage",
-          this,
-          &ServiceWorkerContainer::DispatchMessage,
-          std::move(aMessage)
-        )
-      );
-    }
-  }
-}
-
-template <typename F>
-void
-ServiceWorkerContainer::RunWithJSContext(F&& aCallable)
-{
-  nsCOMPtr<nsIGlobalObject> globalObject;
-  if (nsPIDOMWindowInner* const window = GetOwner()) {
-    globalObject = do_QueryInterface(window);
-  }
-
-  // If AutoJSAPI::Init() fails then either global is nullptr or not
-  // in a usable state.
-  AutoJSAPI jsapi;
-  if (!jsapi.Init(globalObject)) {
-    return;
-  }
-
-  aCallable(jsapi.cx(), globalObject);
-}
-
-void
-ServiceWorkerContainer::DispatchMessage(RefPtr<ReceivedMessage> aMessage)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // When dispatching a message, either DOMContentLoaded has already
-  // been fired, or someone called startMessages() or set onmessage.
-  // Either way, a global object is supposed to be present. If it's
-  // not, we'd fail to initialize the JS API and exit.
-  RunWithJSContext([this, message = std::move(aMessage)](JSContext* const aCx,
-                                                         nsIGlobalObject* const aGlobal) {
-    RootedDictionary<MessageEventInit> init(aCx);
-    if (!FillInMessageEventInit(aCx, aGlobal, *message, init)) {
-      // TODO: The spec requires us to fire a messageerror event here.
-      return;
-    }
-
-    RefPtr<MessageEvent> event =
-      MessageEvent::Constructor(this, NS_LITERAL_STRING("message"), init);
-    event->SetTrusted(true);
-
-    ErrorResult result;
-    DispatchEvent(*event, result);
-    if (result.Failed()) {
-      result.SuppressException();
-    }
-  });
-}
-
-namespace {
-
-nsresult
-FillInOriginNoSuffix(const ServiceWorkerDescriptor& aServiceWorker, nsString& aOrigin)
-{
-  using mozilla::ipc::PrincipalInfoToPrincipal;
-
-  nsresult rv;
-
-  nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo(), &rv);
-  if (NS_FAILED(rv) || !principal) {
-    return rv;
-  }
-
-  nsAutoCString originUTF8;
-  rv = principal->GetOriginNoSuffix(originUTF8);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  CopyUTF8toUTF16(originUTF8, aOrigin);
-  return NS_OK;
-}
-
-already_AddRefed<ServiceWorker>
-GetOrCreateServiceWorkerWithoutWarnings(nsIGlobalObject* const aGlobal,
-                                        const ServiceWorkerDescriptor& aDescriptor)
-{
-  // In child-intercept mode we have to verify that the registration
-  // exists in the current process. This exact check is also performed
-  // (indirectly) in nsIGlobalObject::GetOrCreateServiceWorker, but it
-  // also emits a warning when the registration is not present. To
-  // to avoid having too many warnings, we do a precheck here.
-  if (!ServiceWorkerParentInterceptEnabled()) {
-    const RefPtr<ServiceWorkerManager> serviceWorkerManager = ServiceWorkerManager::GetInstance();
-    if (!serviceWorkerManager) {
-      return nullptr;
-    }
-
-    const RefPtr<ServiceWorkerRegistrationInfo> registration =
-      serviceWorkerManager->GetRegistration(aDescriptor.PrincipalInfo(), aDescriptor.Scope());
-    if (!registration) {
-      return nullptr;
-    }
-  }
-
-  return aGlobal->GetOrCreateServiceWorker(aDescriptor).forget();
-}
-
-}
-
-bool
-ServiceWorkerContainer::FillInMessageEventInit(JSContext* const aCx,
-                                               nsIGlobalObject* const aGlobal,
-                                               ReceivedMessage& aMessage,
-                                               MessageEventInit& aInit)
-{
-  ErrorResult result;
-  JS::Rooted<JS::Value> messageData(aCx);
-  aMessage.mClonedData.Read(aCx, &messageData, result);
-  if (result.Failed()) {
-    return false;
-  }
-
-  aInit.mData = messageData;
-
-  if (!aMessage.mClonedData.TakeTransferredPortsAsSequence(aInit.mPorts)) {
-    return false;
-  }
-
-  const nsresult rv = FillInOriginNoSuffix(aMessage.mServiceWorker, aInit.mOrigin);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  const RefPtr<ServiceWorker> serviceWorkerInstance =
-    GetOrCreateServiceWorkerWithoutWarnings(aGlobal, aMessage.mServiceWorker);
-  if (serviceWorkerInstance) {
-    aInit.mSource.SetValue().SetAsServiceWorker() = serviceWorkerInstance;
-  }
-
-  return true;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorkerContainer.h
+++ b/dom/serviceworkers/ServiceWorkerContainer.h
@@ -10,18 +10,16 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 
 class nsIGlobalWindow;
 
 namespace mozilla {
 namespace dom {
 
-class ClientPostMessageArgs;
-struct MessageEventInit;
 class Promise;
 struct RegistrationOptions;
 class ServiceWorker;
 
 // Lightweight serviceWorker APIs collection.
 class ServiceWorkerContainer final : public DOMEventTargetHelper
 {
 public:
@@ -61,29 +59,17 @@ public:
     NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
   };
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(controllerchange)
   IMPL_EVENT_HANDLER(error)
-  
-  // Almost a manual expansion of IMPL_EVENT_HANDLER(message), but
-  // with the additional StartMessages() when setting the handler, as
-  // required by the spec.
-  inline mozilla::dom::EventHandlerNonNull* GetOnmessage()
-  {
-    return GetEventHandler(nsGkAtoms::onmessage);
-  }
-  inline void SetOnmessage(mozilla::dom::EventHandlerNonNull* aCallback)
-  {
-    SetEventHandler(nsGkAtoms::onmessage, aCallback);
-    StartMessages();
-  }
+  IMPL_EVENT_HANDLER(message)
 
   static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
 
   static already_AddRefed<ServiceWorkerContainer>
   Create(nsIGlobalObject* aGlobal);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -98,37 +84,31 @@ public:
 
   already_AddRefed<Promise>
   GetRegistration(const nsAString& aDocumentURL,
                   ErrorResult& aRv);
 
   already_AddRefed<Promise>
   GetRegistrations(ErrorResult& aRv);
 
-  void
-  StartMessages();
-
   Promise*
   GetReady(ErrorResult& aRv);
 
   // Testing only.
   void
   GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv);
 
   // DOMEventTargetHelper
   void DisconnectFromOwner() override;
 
   // Invalidates |mControllerWorker| and dispatches a "controllerchange"
   // event.
   void
   ControllerChanged(ErrorResult& aRv);
 
-  void
-  ReceiveMessage(const ClientPostMessageArgs& aArgs);
-
 private:
   ServiceWorkerContainer(nsIGlobalObject* aGlobal,
                          already_AddRefed<ServiceWorkerContainer::Inner> aInner);
 
   ~ServiceWorkerContainer();
 
   // Utility method to get the global if its present and if certain
   // additional validaty checks pass.  One of these additional checks
@@ -136,50 +116,22 @@ private:
   // vary based on user settings we want to often provide some error
   // message if the storage check fails.  This method takes an optional
   // callback that can be used to report the storage failure to the
   // devtools console.
   nsIGlobalObject*
   GetGlobalIfValid(ErrorResult& aRv,
                    const std::function<void(nsIDocument*)>&& aStorageFailureCB = nullptr) const;
 
-  struct ReceivedMessage;
-
-  // Dispatch a Runnable that dispatches the given message on this
-  // object. When the owner of this object is a Window, the Runnable
-  // is dispatched on the corresponding TabGroup.
-  void
-  EnqueueReceivedMessageDispatch(RefPtr<ReceivedMessage> aMessage);
-
-  template <typename F>
-  void
-  RunWithJSContext(F&& aCallable);
-
-  void
-  DispatchMessage(RefPtr<ReceivedMessage> aMessage);
-
-  static bool
-  FillInMessageEventInit(JSContext* aCx,
-                         nsIGlobalObject* aGlobal,
-                         ReceivedMessage& aMessage,
-                         MessageEventInit& aInit);
-
   RefPtr<Inner> mInner;
 
   // This only changes when a worker hijacks everything in its scope by calling
   // claim.
   RefPtr<ServiceWorker> mControllerWorker;
 
   RefPtr<Promise> mReadyPromise;
   MozPromiseRequestHolder<ServiceWorkerRegistrationPromise> mReadyPromiseHolder;
-
-  // Set after StartMessages() has been called.
-  bool mMessagesStarted = false;
-
-  // Queue holding messages posted from service worker as long as
-  // StartMessages() hasn't been called.
-  nsTArray<RefPtr<ReceivedMessage>> mPendingMessages;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_serviceworkercontainer_h__ */
--- a/dom/webidl/ServiceWorkerContainer.webidl
+++ b/dom/webidl/ServiceWorkerContainer.webidl
@@ -1,15 +1,15 @@
 /* -*- 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/.
  *
  * The origin of this IDL file is
- * https://w3c.github.io/ServiceWorker/#serviceworkercontainer
+ * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
  *
  */
 
 [Func="ServiceWorkerContainer::IsEnabled",
  Exposed=Window]
 interface ServiceWorkerContainer : EventTarget {
   // FIXME(nsm):
   // https://github.com/slightlyoff/ServiceWorker/issues/198
@@ -24,18 +24,16 @@ interface ServiceWorkerContainer : Event
                                               optional RegistrationOptions options);
 
   [NewObject]
   Promise<any> getRegistration(optional USVString documentURL = "");
 
   [NewObject]
   Promise<sequence<ServiceWorkerRegistration>> getRegistrations();
 
-  void startMessages();
-
   attribute EventHandler oncontrollerchange;
   attribute EventHandler onerror;
   attribute EventHandler onmessage;
 };
 
 // Testing only.
 partial interface ServiceWorkerContainer {
   [Throws,Pref="dom.serviceWorkers.testing.enabled"]
--- a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html
+++ b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html
@@ -16,36 +16,30 @@
   async function setupRegistration(t, scope) {
     const reg = await navigator.serviceWorker.register('../resources/sw-intercept.js', { scope });
     await wait_for_state(t, reg.installing, 'activated');
     add_completion_callback(_ => reg.unregister());
     return reg;
   }
 
   promise_test(async t => {
-    const suffix = "?q=aborted-not-intercepted";
-    const scope = SCOPE + suffix;
+    const scope = SCOPE + "?q=aborted-not-intercepted";
     await setupRegistration(t, scope);
     const iframe = await with_iframe(scope);
     add_completion_callback(_ => iframe.remove());
     const w = iframe.contentWindow;
 
     const controller = new w.AbortController();
     const signal = controller.signal;
     controller.abort();
 
     const nextData = new Promise(resolve => {
       w.navigator.serviceWorker.addEventListener('message', function once(event) {
-        // The message triggered by the iframe's document's fetch
-        // request cannot get dispatched by the time we add the event
-        // listener, so we have to guard against it.
-        if (!event.data.endsWith(suffix)) {
-          w.navigator.serviceWorker.removeEventListener('message', once);
-          resolve(event.data);
-        }
+        w.navigator.serviceWorker.removeEventListener('message', once);
+        resolve(event.data);
       })
     });
 
     const fetchPromise = w.fetch('data.json', { signal });
 
     await promise_rejects(t, "AbortError", fetchPromise);
 
     await w.fetch('data.json?no-abort');
--- a/testing/web-platform/tests/fetch/api/request/destination/resources/fetch-destination-worker-no-load-event.js
+++ b/testing/web-platform/tests/fetch/api/request/destination/resources/fetch-destination-worker-no-load-event.js
@@ -1,10 +1,10 @@
 self.addEventListener('fetch', function(event) {
-    if (event.request.url.includes('dummy.html?')) {
+    if (event.request.url.includes('dummy')) {
         event.waitUntil(async function() {
             let destination = new URL(event.request.url).searchParams.get("dest");
             var result = "FAIL";
             if (event.request.destination == destination) {
               result = "PASS";
             }
             let cl = await clients.matchAll({includeUncontrolled: true});
             for (i = 0; i < cl.length; i++) {
--- a/testing/web-platform/tests/service-workers/service-worker/postmessage-to-client.https.html
+++ b/testing/web-platform/tests/service-workers/service-worker/postmessage-to-client.https.html
@@ -46,213 +46,9 @@ promise_test(t => {
           assert_equals(e.ports.length, 0, 'ports should be an empty array.');
           assert_equals(message, 'Sending message via clients');
           return new Promise(resolve => {
               w.navigator.serviceWorker.onmessage = resolve;
             });
         })
       .then(e => { assert_equals(e.data, 'quit'); });
   }, 'postMessage from ServiceWorker to Client.');
-
-// This function creates a message listener that captures all messages
-// sent to this window and matches them with corresponding requests.
-// This frees test code from having to use clunky constructs just to
-// avoid race conditions, since the relative order of message and
-// request arrival doesn't matter.
-function create_message_listener(t) {
-    const listener = {
-        messages: new Set(),
-        requests: new Set(),
-        waitFor: function(predicate) {
-            for (const event of this.messages) {
-                // If a message satisfying the predicate has already
-                // arrived, it gets matched to this request.
-                if (predicate(event)) {
-                    this.messages.delete(event);
-                    return Promise.resolve(event);
-                }
-            }
-
-            // If no match was found, the request is stored and a
-            // promise is returned.
-            const request = { predicate };
-            const promise = new Promise(resolve => request.resolve = resolve);
-            this.requests.add(request);
-            return promise;
-        }
-    };
-    window.onmessage = t.step_func(event => {
-        for (const request of listener.requests) {
-            // If the new message matches a stored request's
-            // predicate, the request's promise is resolved with this
-            // message.
-            if (request.predicate(event)) {
-                listener.requests.delete(request);
-                request.resolve(event);
-                return;
-            }
-        };
-
-        // No outstanding request for this message, store it in case
-        // it's requested later.
-        listener.messages.add(event);
-    });
-    return listener;
-}
-
-async function service_worker_register_and_activate(t, script, scope) {
-    const registration = await service_worker_unregister_and_register(t, script, scope);
-    t.add_cleanup(() => registration.unregister());
-    const worker = registration.installing;
-    await wait_for_state(t, worker, 'activated');
-    return worker;
-}
-
-// Add an iframe (parent) whose document contains a nested iframe
-// (child), then set the child's src attribute to child_url and return
-// its Window (without waiting for it to finish loading).
-async function with_nested_iframes(t, child_url) {
-    const parent = await with_iframe('resources/nested-iframe-parent.html?role=parent');
-    t.add_cleanup(() => parent.remove());
-    const child = parent.contentWindow.document.getElementById('child');
-    child.setAttribute('src', child_url);
-    return child.contentWindow;
-}
-
-// Returns a predicate matching a fetch message with the specified
-// key.
-function fetch_message(key) {
-    return event => event.data.type === 'fetch' && event.data.key === key;
-}
-
-// Returns a predicate matching a ping message with the specified
-// payload.
-function ping_message(data) {
-    return event => event.data.type === 'ping' && event.data.data === data;
-}
-
-// A client message queue test is a testharness.js test with some
-// additional setup:
-// 1. A listener (see create_message_listener)
-// 2. An active service worker
-// 3. Two nested iframes
-// 4. A state transition function that controls the order of events
-//    during the test
-function client_message_queue_test(url, test_function, description) {
-    promise_test(async t => {
-        t.listener = create_message_listener(t);
-
-        const script = 'resources/stalling-service-worker.js';
-        const scope = 'resources/';
-        t.service_worker = await service_worker_register_and_activate(t, script, scope);
-
-        // We create two nested iframes such that both are controlled by
-        // the newly installed service worker.
-        const child_url = url + '?role=child';
-        t.frame = await with_nested_iframes(t, child_url);
-
-        t.state_transition = async function(from, to, scripts) {
-            // A state transition begins with the child's parser
-            // fetching a script due to a <script> tag. The request
-            // arrives at the service worker, which notifies the
-            // parent, which in turn notifies the test. Note that the
-            // event loop keeps spinning while the parser is waiting.
-            const request = await this.listener.waitFor(fetch_message(to));
-
-            // The test instructs the service worker to send two ping
-            // messages through the Client interface: first to the
-            // child, then to the parent.
-            this.service_worker.postMessage(from);
-
-            // When the parent receives the ping message, it forwards
-            // it to the test. Assuming that messages to both child
-            // and parent are mapped to the same task queue (this is
-            // not [yet] required by the spec), receiving this message
-            // guarantees that the child has already dispatched its
-            // message if it was allowed to do so.
-            await this.listener.waitFor(ping_message(from));
-
-            // Finally, reply to the service worker's fetch
-            // notification with the script it should use as the fetch
-            // request's response. This is a defensive mechanism that
-            // ensures the child's parser really is blocked until the
-            // test is ready to continue.
-            request.ports[0].postMessage([`state = '${to}';`].concat(scripts));
-        };
-
-        await test_function(t);
-    }, description);
-}
-
-function client_message_queue_enable_test(
-    install_script,
-    start_script,
-    earliest_dispatch,
-    description)
-{
-    function later_state(state1, state2) {
-        const states = ['init', 'install', 'start', 'finish', 'loaded'];
-        const index1 = states.indexOf(state1);
-        const index2 = states.indexOf(state2);
-        const max_index = Math.max(index1, index2);
-        return states[max_index];
-    }
-
-    client_message_queue_test('enable-client-message-queue.html', async t => {
-        // While parsing the child's document, the child transitions
-        // from the 'init' state all the way to the 'finish' state.
-        // Once parsing is finished it would enter the final 'loaded'
-        // state. All but the last transition require assitance from
-        // the test.
-        await t.state_transition('init', 'install', [install_script]);
-        await t.state_transition('install', 'start', [start_script]);
-        await t.state_transition('start', 'finish', []);
-
-        // Wait for all messages to get dispatched on the child's
-        // ServiceWorkerContainer and then verify that each message
-        // was dispatched while the child was in the correct state.
-        const report = await t.frame.report;
-        ['init', 'install', 'start'].forEach(state => {
-            const dispatch = later_state(state, earliest_dispatch);
-            assert_equals(report[state], dispatch,
-                          `Message sent in state '${state}' dispatched in state '${dispatch}'`);
-        });
-    }, description);
-}
-
-const empty_script = ``;
-
-const add_event_listener =
-    `navigator.serviceWorker.addEventListener('message', handle_message);`;
-
-const set_onmessage = `navigator.serviceWorker.onmessage = handle_message;`;
-
-const start_messages = `navigator.serviceWorker.startMessages();`;
-
-client_message_queue_enable_test(add_event_listener, empty_script, 'loaded',
-    'Messages from ServiceWorker to Client only received after DOMContentLoaded event.');
-
-client_message_queue_enable_test(add_event_listener, start_messages, 'start',
-    'Messages from ServiceWorker to Client only received after calling startMessages().');
-
-client_message_queue_enable_test(set_onmessage, empty_script, 'install',
-    'Messages from ServiceWorker to Client only received after setting onmessage.');
-
-const resolve_manual_promise = `resolve_manual_promise();`
-
-async function test_microtasks_when_client_message_queue_enabled(t, scripts) {
-    await t.state_transition('init', 'start', scripts.concat([resolve_manual_promise]));
-    let result = await t.frame.result;
-    assert_equals(result[0], 'microtask', 'The microtask was executed first.');
-    assert_equals(result[1], 'message', 'The message was dispatched.');
-}
-
-client_message_queue_test('message-vs-microtask.html', t => {
-    return test_microtasks_when_client_message_queue_enabled(t, [
-        add_event_listener,
-        start_messages,
-    ]);
-}, 'Microtasks run before dispatching messages after calling startMessages().');
-
-client_message_queue_test('message-vs-microtask.html', t => {
-    return test_microtasks_when_client_message_queue_enabled(t, [set_onmessage]);
-}, 'Microtasks run before dispatching messages after setting onmessage.');
 </script>
--- a/testing/web-platform/tests/service-workers/service-worker/resources/about-blank-replacement-ping-frame.py
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/about-blank-replacement-ping-frame.py
@@ -36,14 +36,13 @@ if (win.location.href !== 'about:blank')
   win.navigator.serviceWorker.addEventListener('message', evt => {
     if (evt.data.type === 'PING') {
       evt.source.postMessage({
         type: 'PONG',
         location: win.location.toString()
       });
     }
   });
-  win.navigator.serviceWorker.startMessages();
 }
 </script>
 </body>
 </html>
 """)
deleted file mode 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/enable-client-message-queue.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!DOCTYPE html>
-<script>
-  // The state variable is used by handle_message to record the time
-  // at which a message was handled. It's updated by the scripts
-  // loaded by the <script> tags at the bottom of the file as well as
-  // by the event listener added here.
-  var state = 'init';
-  addEventListener('DOMContentLoaded', () => state = 'loaded');
-
-  // We expect to get three ping messages from the service worker.
-  const expected = ['init', 'install', 'start'];
-  let promises = {};
-  let resolvers = {};
-  expected.forEach(name => {
-    promises[name] = new Promise(resolve => resolvers[name] = resolve);
-  });
-
-  // Once all messages have been dispatched, the state in which each
-  // of them was dispatched is recorded in the draft. At that point
-  // the draft becomes the final report.
-  var draft = {};
-  var report = Promise.all(Object.values(promises)).then(() => window.draft);
-
-  // This message handler is installed by the 'install' script.
-  function handle_message(event) {
-    const data = event.data.data;
-    draft[data] = state;
-    resolvers[data]();
-  }
-</script>
-
-<!--
-  The controlling service worker will delay the response to these
-  fetch requests until the test instructs it how to reply. Note that
-  the event loop keeps spinning while the parser is blocked.
--->
-<script src="empty.js?key=install"></script>
-<script src="empty.js?key=start"></script>
-<script src="empty.js?key=finish"></script>
deleted file mode 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/message-vs-microtask.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<script>
-  let draft = [];
-  var resolve_manual_promise;
-  let manual_promise =
-    new Promise(resolve => resolve_manual_promise = resolve).then(() => draft.push('microtask'));
-
-  let resolve_message_promise;
-  let message_promise = new Promise(resolve => resolve_message_promise = resolve);
-  function handle_message(event) {
-    draft.push('message');
-    resolve_message_promise();
-  }
-
-  var result = Promise.all([manual_promise, message_promise]).then(() => draft);
-</script>
-
-<script src="empty.js?key=start"></script>
deleted file mode 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/nested-iframe-parent.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html>
-<script>
-  navigator.serviceWorker.onmessage = event => parent.postMessage(event.data, '*', event.ports);
-</script>
-<iframe id='child'></iframe>
deleted file mode 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/stalling-service-worker.js
+++ /dev/null
@@ -1,54 +0,0 @@
-async function post_message_to_client(role, message, ports) {
-    (await clients.matchAll()).forEach(client => {
-        if (new URL(client.url).searchParams.get('role') === role) {
-            client.postMessage(message, ports);
-        }
-    });
-}
-
-async function post_message_to_child(message, ports) {
-    await post_message_to_client('child', message, ports);
-}
-
-function ping_message(data) {
-    return { type: 'ping', data };
-}
-
-self.onmessage = event => {
-    const message = ping_message(event.data);
-    post_message_to_child(message);
-    post_message_to_parent(message);
-}
-
-async function post_message_to_parent(message, ports) {
-    await post_message_to_client('parent', message, ports);
-}
-
-function fetch_message(key) {
-    return { type: 'fetch', key };
-}
-
-// Send a message to the parent along with a MessagePort to respond
-// with.
-function report_fetch_request(key) {
-    const channel = new MessageChannel();
-    const reply = new Promise(resolve => {
-        channel.port1.onmessage = resolve;
-    }).then(event => event.data);
-    return post_message_to_parent(fetch_message(key), [channel.port2]).then(() => reply);
-}
-
-function respond_with_script(script) {
-    return new Response(new Blob(script, { type: 'text/javascript' }));
-}
-
-// Whenever a controlled document requests a URL with a 'key' search
-// parameter we report the request to the parent frame and wait for
-// a response. The content of the response is then used to respond to
-// the fetch request.
-addEventListener('fetch', event => {
-    let key = new URL(event.request.url).searchParams.get('key');
-    if (key) {
-        event.respondWith(report_fetch_request(key).then(respond_with_script));
-    }
-});