Backed out changeset b2d3c6629a37 (bug 1263304)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 11 Nov 2016 10:47:43 +0100
changeset 364990 a10fce0b6fc805631dc3065c513c0bcb60906015
parent 364989 41f7289afbe3a571c80b59b8fd01ba363b9378e4
child 364991 b1f4c3608bc067ec1d7fc93731cfcf6cd109bc6b
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1263304
milestone52.0a1
backs outb2d3c6629a37d02c1f57e6bb3465ba3e35ff81de
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 b2d3c6629a37 (bug 1263304)
dom/workers/ServiceWorkerEvents.cpp
dom/workers/ServiceWorkerEvents.h
dom/workers/ServiceWorkerPrivate.cpp
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -762,19 +762,19 @@ FetchEvent::RespondWith(JSContext* aCx, 
   mWaitToRespond = true;
   RefPtr<RespondWithHandler> handler =
     new RespondWithHandler(mChannel, mRegistration, mRequest->Mode(),
                            ir->IsClientRequest(), mRequest->Redirect(),
                            mScriptSpec, NS_ConvertUTF8toUTF16(requestURL),
                            spec, line, column);
   aArg.AppendNativeHandler(handler);
 
-  if (!WaitOnPromise(aArg)) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-  }
+  // Append directly to the lifecycle promises array.  Don't call WaitUntil()
+  // because that will lead to double-reporting any errors.
+  mPromises.AppendElement(&aArg);
 }
 
 void
 FetchEvent::PreventDefault(JSContext* aCx)
 {
   MOZ_ASSERT(aCx);
 
   if (mPreventDefaultScriptSpec.IsEmpty()) {
@@ -905,56 +905,69 @@ NS_INTERFACE_MAP_END_INHERITING(Extendab
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, ExtendableEvent, mRequest)
 
 ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
   : Event(aOwner, nullptr, nullptr)
 {
 }
 
-bool
-ExtendableEvent::WaitOnPromise(Promise& aPromise)
-{
-  MOZ_ASSERT(mExtensionsHandler);
-  return mExtensionsHandler->WaitOnPromise(aPromise);
-}
-
-void
-ExtendableEvent::SetKeepAliveHandler(ExtensionsHandler* aExtensionsHandler)
-{
-  MOZ_ASSERT(!mExtensionsHandler);
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-  mExtensionsHandler = aExtensionsHandler;
-}
-
 void
 ExtendableEvent::WaitUntil(JSContext* aCx, Promise& aPromise, ErrorResult& aRv)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
-  if (!WaitOnPromise(aPromise)) {
+  if (EventPhase() == nsIDOMEvent::NONE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Append our handler to each waitUntil promise separately so we
   // can record the location in script where waitUntil was called.
   RefPtr<WaitUntilHandler> handler =
     new WaitUntilHandler(GetCurrentThreadWorkerPrivate(), aCx);
   aPromise.AppendNativeHandler(handler);
+
+  mPromises.AppendElement(&aPromise);
+}
+
+already_AddRefed<Promise>
+ExtendableEvent::GetPromise()
+{
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  nsIGlobalObject* globalObj = worker->GlobalScope();
+
+  AutoJSAPI jsapi;
+  if (!jsapi.Init(globalObj)) {
+    return nullptr;
+  }
+  JSContext* cx = jsapi.cx();
+
+  GlobalObject global(cx, globalObj->GetGlobalJSObject());
+
+  ErrorResult result;
+  RefPtr<Promise> p = Promise::All(global, Move(mPromises), result);
+  if (NS_WARN_IF(result.MaybeSetPendingException(cx))) {
+    return nullptr;
+  }
+
+  return p.forget();
 }
 
 NS_IMPL_ADDREF_INHERITED(ExtendableEvent, Event)
 NS_IMPL_RELEASE_INHERITED(ExtendableEvent, Event)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ExtendableEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ExtendableEvent, Event, mPromises)
+
 namespace {
 nsresult
 ExtractBytesFromUSVString(const nsAString& aStr, nsTArray<uint8_t>& aBytes)
 {
   MOZ_ASSERT(aBytes.IsEmpty());
   nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
   if (NS_WARN_IF(!encoder)) {
     return NS_ERROR_OUT_OF_MEMORY;
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -46,46 +46,27 @@ public:
                         nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
                         nsresult aStatus);
 
   NS_IMETHOD Run() override;
 };
 
 class ExtendableEvent : public Event
 {
-public:
-  class ExtensionsHandler {
-  public:
-    virtual bool
-    WaitOnPromise(Promise& aPromise) = 0;
-
-    NS_IMETHOD_(MozExternalRefCountType)
-    AddRef() = 0;
-
-    NS_IMETHOD_(MozExternalRefCountType)
-    Release() = 0;
-  };
-
-private:
-  RefPtr<ExtensionsHandler> mExtensionsHandler;
-
 protected:
-  bool
-  WaitOnPromise(Promise& aPromise);
+  nsTArray<RefPtr<Promise>> mPromises;
 
   explicit ExtendableEvent(mozilla::dom::EventTarget* aOwner);
   ~ExtendableEvent() {}
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ExtendableEvent, Event)
   NS_FORWARD_TO_EVENT
 
-  void
-  SetKeepAliveHandler(ExtensionsHandler* aExtensionsHandler);
-
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return mozilla::dom::ExtendableEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
   static already_AddRefed<ExtendableEvent>
   Constructor(mozilla::dom::EventTarget* aOwner,
               const nsAString& aType,
@@ -107,16 +88,19 @@ public:
   {
     nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
     return Constructor(target, aType, aOptions);
   }
 
   void
   WaitUntil(JSContext* aCx, Promise& aPromise, ErrorResult& aRv);
 
+  already_AddRefed<Promise>
+  GetPromise();
+
   virtual ExtendableEvent* AsExtendableEvent() override
   {
     return this;
   }
 };
 
 class FetchEvent final : public ExtendableEvent
 {
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -176,196 +176,164 @@ ServiceWorkerPrivate::CheckScriptEvaluat
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 namespace {
 
-enum ExtendableEventResult {
-    Rejected = 0,
-    Resolved
-};
-
-class ExtendableEventCallback {
-public:
-  virtual void
-  FinishedWithResult(ExtendableEventResult aResult) = 0;
-
-  NS_IMETHOD_(MozExternalRefCountType)
-  AddRef() = 0;
-
-  NS_IMETHOD_(MozExternalRefCountType)
-  Release() = 0;
-};
-
-class KeepAliveHandler final : public WorkerHolder
-                             , public ExtendableEvent::ExtensionsHandler
-                             , public PromiseNativeHandler
+// Holds the worker alive until the waitUntil promise is resolved or
+// rejected.
+class KeepAliveHandler final
 {
-  // This class manages lifetime extensions added by calling WaitUntil()
-  // or RespondWith(). We allow new extensions as long as we still hold
-  // |mKeepAliveToken|. Once the last promise was settled, we queue a microtask
-  // which releases the token and prevents further extensions. By doing this,
-  // we give other pending microtasks a chance to continue adding extensions.
-
-  nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-  WorkerPrivate* MOZ_NON_OWNING_REF mWorkerPrivate;
-  bool mWorkerHolderAdded;
-
-  // We start holding a self reference when the first extension promise is
-  // added. As far as I can tell, the only case where this is useful is when
-  // we're waiting indefinitely on a promise that's no longer reachable
-  // and will never be settled.
-  // The cycle is broken when the last promise was settled or when the
-  // worker is shutting down.
-  RefPtr<KeepAliveHandler> mSelfRef;
-
-  // Called when the last promise was settled.
-  RefPtr<ExtendableEventCallback> mCallback;
+  // Use an internal class to listen for the promise resolve/reject
+  // callbacks.  This class also registers a feature so that it can
+  // preemptively cleanup if the service worker is timed out and
+  // terminated.
+  class InternalHandler final : public PromiseNativeHandler
+                              , public WorkerHolder
+  {
+    nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
 
-  uint32_t mPendingPromisesCount;
-
-  // We don't actually care what values the promises resolve to, only whether
-  // any of them were rejected.
-  bool mRejected;
-
-public:
-  NS_DECL_ISUPPORTS
+    // Worker thread only
+    WorkerPrivate* mWorkerPrivate;
+    RefPtr<Promise> mPromise;
+    bool mWorkerHolderAdded;
 
-  explicit KeepAliveHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
-                            ExtendableEventCallback* aCallback)
-    : mKeepAliveToken(aKeepAliveToken)
-    , mWorkerPrivate(GetCurrentThreadWorkerPrivate())
-    , mWorkerHolderAdded(false)
-    , mCallback(aCallback)
-    , mPendingPromisesCount(0)
-    , mRejected(false)
-  {
-    MOZ_ASSERT(mKeepAliveToken);
-    MOZ_ASSERT(mWorkerPrivate);
-  }
+    ~InternalHandler()
+    {
+      MaybeCleanup();
+    }
 
-  bool
-  UseWorkerHolder()
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(!mWorkerHolderAdded);
-    mWorkerHolderAdded = HoldWorker(mWorkerPrivate, Terminating);
-    return mWorkerHolderAdded;
-  }
-
-  bool
-  WaitOnPromise(Promise& aPromise) override
-  {
-    if (!mKeepAliveToken) {
-      MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
-      return false;
-    }
-    if (!mSelfRef) {
-      MOZ_ASSERT(!mPendingPromisesCount);
-      mSelfRef = this;
+    bool
+    UseWorkerHolder()
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      MOZ_ASSERT(!mWorkerHolderAdded);
+      mWorkerHolderAdded = HoldWorker(mWorkerPrivate, Terminating);
+      return mWorkerHolderAdded;
     }
 
-    ++mPendingPromisesCount;
-    aPromise.AppendNativeHandler(this);
-
-    return true;
-  }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    RemovePromise(Resolved);
-  }
+    void
+    MaybeCleanup()
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      if (!mPromise) {
+        return;
+      }
+      if (mWorkerHolderAdded) {
+        ReleaseWorker();
+      }
+      mPromise = nullptr;
+      mKeepAliveToken = nullptr;
+    }
 
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    RemovePromise(Rejected);
-  }
+    void
+    ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      MaybeCleanup();
+    }
 
-  bool
-  Notify(Status aStatus) override
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    if (aStatus < Terminating) {
+    void
+    RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      MaybeCleanup();
+    }
+
+    bool
+    Notify(Status aStatus) override
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      if (aStatus < Terminating) {
+        return true;
+      }
+      MaybeCleanup();
       return true;
     }
 
-    MaybeCleanup();
-    return true;
-  }
+    InternalHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
+                    WorkerPrivate* aWorkerPrivate,
+                    Promise* aPromise)
+      : mKeepAliveToken(aKeepAliveToken)
+      , mWorkerPrivate(aWorkerPrivate)
+      , mPromise(aPromise)
+      , mWorkerHolderAdded(false)
+    {
+      MOZ_ASSERT(mKeepAliveToken);
+      MOZ_ASSERT(mWorkerPrivate);
+      MOZ_ASSERT(mPromise);
+    }
 
-  void
-  MaybeDone()
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
+  public:
+    static already_AddRefed<InternalHandler>
+    Create(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
+           WorkerPrivate* aWorkerPrivate,
+           Promise* aPromise)
+    {
+      RefPtr<InternalHandler> ref = new InternalHandler(aKeepAliveToken,
+                                                        aWorkerPrivate,
+                                                        aPromise);
 
-    if (mPendingPromisesCount) {
-      return;
-    }
-    if (mCallback) {
-      mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
+      if (NS_WARN_IF(!ref->UseWorkerHolder())) {
+        return nullptr;
+      }
+
+      return ref.forget();
     }
 
-    MaybeCleanup();
-  }
+    NS_DECL_ISUPPORTS
+  };
 
-private:
-  ~KeepAliveHandler()
-  {
-    MaybeCleanup();
-  }
+  // This is really just a wrapper class to keep the InternalHandler
+  // private.  We don't want any code to accidentally call
+  // Promise::AppendNativeHandler() without also referencing the promise.
+  // Therefore we force all code through the static CreateAndAttachToPromise()
+  // and use the private InternalHandler object.
+  KeepAliveHandler() = delete;
+  ~KeepAliveHandler() = delete;
 
-  void
-  MaybeCleanup()
+public:
+  // Create a private handler object and attach it to the given Promise.
+  // This will also create a strong ref to the Promise in a ref cycle.  The
+  // ref cycle is broken when the Promise is fulfilled or the worker thread
+  // is Terminated.
+  static void
+  CreateAndAttachToPromise(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
+                           Promise* aPromise)
   {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    if (!mKeepAliveToken) {
-      return;
-    }
-    if (mWorkerHolderAdded) {
-      ReleaseWorker();
-    }
-
-    mKeepAliveToken = nullptr;
-    mSelfRef = nullptr;
-  }
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(aKeepAliveToken);
+    MOZ_ASSERT(aPromise);
 
-  void
-  RemovePromise(ExtendableEventResult aResult)
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
-    MOZ_ASSERT(mSelfRef);
-    MOZ_ASSERT(mKeepAliveToken);
-
-    mRejected |= (aResult == Rejected);
-
-    --mPendingPromisesCount;
-    if (mPendingPromisesCount) {
+    // This creates a strong ref to the promise.
+    RefPtr<InternalHandler> handler = InternalHandler::Create(aKeepAliveToken,
+                                                              workerPrivate,
+                                                              aPromise);
+    if (NS_WARN_IF(!handler)) {
       return;
     }
 
-    CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
-    MOZ_ASSERT(cx);
-
-    RefPtr<nsIRunnable> r = NewRunnableMethod(this, &KeepAliveHandler::MaybeDone);
-    cx->DispatchToMicroTask(r.forget());
+    // This then creates a strong ref cycle between the promise and the
+    // handler.  The cycle is broken when the Promise is fulfilled or
+    // the worker thread is Terminated.
+    aPromise->AppendNativeHandler(handler);
   }
 };
 
-NS_IMPL_ISUPPORTS0(KeepAliveHandler)
+NS_IMPL_ISUPPORTS0(KeepAliveHandler::InternalHandler)
 
 class RegistrationUpdateRunnable : public Runnable
 {
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
   const bool mNeedTimeCheck;
 
 public:
   RegistrationUpdateRunnable(nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
@@ -404,43 +372,50 @@ public:
     mKeepAliveToken =
       new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken);
   }
 
   bool
   DispatchExtendableEventOnWorkerScope(JSContext* aCx,
                                        WorkerGlobalScope* aWorkerScope,
                                        ExtendableEvent* aEvent,
-                                       ExtendableEventCallback* aCallback)
+                                       PromiseNativeHandler* aPromiseHandler)
   {
     MOZ_ASSERT(aWorkerScope);
     MOZ_ASSERT(aEvent);
     nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
     WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
 
-    RefPtr<KeepAliveHandler> keepAliveHandler =
-      new KeepAliveHandler(mKeepAliveToken, aCallback);
-    if (NS_WARN_IF(!keepAliveHandler->UseWorkerHolder())) {
-      return false;
-    }
-
-    // This must always be set *before* dispatching the event, otherwise
-    // waitUntil calls will fail.
-    aEvent->SetKeepAliveHandler(keepAliveHandler);
-
     ErrorResult result;
     result = aWorkerScope->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
     if (NS_WARN_IF(result.Failed()) || internalEvent->mFlags.mExceptionWasRaised) {
       result.SuppressException();
       return false;
     }
 
-    // [[ If e’s extend lifetime promises is empty, unset e’s extensions allowed
-    //    flag and abort these steps. ]]
-    keepAliveHandler->MaybeDone();
+    RefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
+    if (!waitUntilPromise) {
+      waitUntilPromise =
+        Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
+      MOZ_RELEASE_ASSERT(!result.Failed());
+    }
+
+    MOZ_ASSERT(waitUntilPromise);
+
+    // Make sure to append the caller's promise handler before attaching
+    // our keep alive handler.  This can avoid terminating the worker
+    // before a success result is delivered to the caller in cases where
+    // the idle timeout has been set to zero.  This low timeout value is
+    // sometimes set in tests.
+    if (aPromiseHandler) {
+      waitUntilPromise->AppendNativeHandler(aPromiseHandler);
+    }
+
+    KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
+                                               waitUntilPromise);
 
     return true;
   }
 };
 
 class SendMesssageEventRunnable final : public ExtendableEventWorkerRunnable
                                       , public StructuredCloneHolder
 {
@@ -621,17 +596,17 @@ private:
 
 };
 
 /*
  * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
  * termination during the execution of life cycle events. It is responsible
  * with advancing the job queue for install/activate tasks.
  */
-class LifeCycleEventWatcher final : public ExtendableEventCallback,
+class LifeCycleEventWatcher final : public PromiseNativeHandler,
                                     public WorkerHolder
 {
   WorkerPrivate* mWorkerPrivate;
   RefPtr<LifeCycleEventCallback> mCallback;
   bool mDone;
 
   ~LifeCycleEventWatcher()
   {
@@ -643,17 +618,17 @@ class LifeCycleEventWatcher final : publ
     // XXXcatalinb: If all the promises passed to waitUntil go out of scope,
     // the resulting Promise.all will be cycle collected and it will drop its
     // native handlers (including this object). Instead of waiting for a timeout
     // we report the failure now.
     ReportResult(false);
   }
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(LifeCycleEventWatcher, override)
+  NS_DECL_ISUPPORTS
 
   LifeCycleEventWatcher(WorkerPrivate* aWorkerPrivate,
                         LifeCycleEventCallback* aCallback)
     : mWorkerPrivate(aWorkerPrivate)
     , mCallback(aCallback)
     , mDone(false)
   {
     MOZ_ASSERT(aWorkerPrivate);
@@ -710,29 +685,41 @@ public:
     if (NS_WARN_IF(NS_FAILED(rv))) {
       NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
     }
 
     ReleaseWorker();
   }
 
   void
-  FinishedWithResult(ExtendableEventResult aResult) override
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
-    ReportResult(aResult == Resolved);
+
+    ReportResult(true);
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    ReportResult(false);
 
     // Note, all WaitUntil() rejections are reported to client consoles
     // by the WaitUntilHandler in ServiceWorkerEvents.  This ensures that
     // errors in non-lifecycle events like FetchEvent and PushEvent are
     // reported properly.
   }
 };
 
+NS_IMPL_ISUPPORTS0(LifeCycleEventWatcher)
+
 bool
 LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx,
                                                      WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
 
   RefPtr<ExtendableEvent> event;
@@ -786,42 +773,46 @@ ServiceWorkerPrivate::SendLifeCycleEvent
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 namespace {
 
-class PushErrorReporter final : public ExtendableEventCallback
+class PushErrorReporter final : public PromiseNativeHandler
 {
   WorkerPrivate* mWorkerPrivate;
   nsString mMessageId;
 
   ~PushErrorReporter()
   {
   }
 
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushErrorReporter, override)
+  NS_DECL_THREADSAFE_ISUPPORTS
 
   PushErrorReporter(WorkerPrivate* aWorkerPrivate,
                     const nsAString& aMessageId)
     : mWorkerPrivate(aWorkerPrivate)
     , mMessageId(aMessageId)
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
   }
 
-  void
-  FinishedWithResult(ExtendableEventResult aResult) override
+  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
-    if (aResult == Rejected) {
-      Report(nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
-    }
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    mWorkerPrivate = nullptr;
+    // Do nothing; we only use this to report errors to the Push service.
+  }
+
+  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    Report(nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
   }
 
   void Report(uint16_t aReason = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR)
   {
     WorkerPrivate* workerPrivate = mWorkerPrivate;
     mWorkerPrivate->AssertIsOnWorkerThread();
     mWorkerPrivate = nullptr;
 
@@ -843,16 +834,18 @@ public:
       do_GetService("@mozilla.org/push/Service;1");
     if (reporter) {
       nsresult rv = reporter->ReportDeliveryError(mMessageId, aReason);
       Unused << NS_WARN_IF(NS_FAILED(rv));
     }
   }
 };
 
+NS_IMPL_ISUPPORTS0(PushErrorReporter)
+
 class SendPushEventRunnable final : public ExtendableFunctionalEventWorkerRunnable
 {
   nsString mMessageId;
   Maybe<nsTArray<uint8_t>> mData;
 
 public:
   SendPushEventRunnable(WorkerPrivate* aWorkerPrivate,
                         KeepAliveToken* aKeepAliveToken,
@@ -1045,17 +1038,17 @@ private:
     // are cancelled.
     mHandler = nullptr;
     return WorkerRunnable::Cancel();
   }
 
   RefPtr<AllowWindowInteractionHandler> mHandler;
 };
 
-class AllowWindowInteractionHandler final : public ExtendableEventCallback
+class AllowWindowInteractionHandler final : public PromiseNativeHandler
 {
   friend class ClearWindowAllowedRunnable;
   nsCOMPtr<nsITimer> mTimer;
 
   ~AllowWindowInteractionHandler()
   {
   }
 
@@ -1124,31 +1117,40 @@ class AllowWindowInteractionHandler fina
                                       nsITimer::TYPE_ONE_SHOT);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       ClearWindowAllowed(aWorkerPrivate);
       return;
     }
   }
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(AllowWindowInteractionHandler, override)
+  NS_DECL_ISUPPORTS
 
   explicit AllowWindowInteractionHandler(WorkerPrivate* aWorkerPrivate)
   {
     StartClearWindowTimer(aWorkerPrivate);
   }
 
   void
-  FinishedWithResult(ExtendableEventResult /* aResult */) override
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+    ClearWindowAllowed(workerPrivate);
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
     ClearWindowAllowed(workerPrivate);
   }
 };
 
+NS_IMPL_ISUPPORTS0(AllowWindowInteractionHandler)
+
 bool
 ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 {
   mHandler->ClearWindowAllowed(aWorkerPrivate);
   mHandler = nullptr;
   return true;
 }
 
@@ -1227,17 +1229,17 @@ public:
     }
 
     event->SetTrusted(true);
     aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
     RefPtr<AllowWindowInteractionHandler> allowWindowInteraction =
       new AllowWindowInteractionHandler(aWorkerPrivate);
     if (!DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
                                               event, allowWindowInteraction)) {
-      allowWindowInteraction->FinishedWithResult(Rejected);
+      allowWindowInteraction->RejectedCallback(aCx, JS::UndefinedHandleValue);
     }
     aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
 
     return true;
   }
 };
 
 } // namespace anonymous
@@ -1560,20 +1562,19 @@ private:
     if (NS_WARN_IF(result.Failed())) {
       result.SuppressException();
       return false;
     }
 
     event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec);
     event->SetTrusted(true);
 
-    bool rv2 =
-      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                           event, nullptr);
-    if (NS_WARN_IF(!rv2) || !event->WaitToRespond()) {
+    RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
+    nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
       nsCOMPtr<nsIRunnable> runnable;
       if (event->DefaultPrevented(aCx)) {
         event->ReportCanceled();
       } else if (event->WidgetEventPtr()->mFlags.mExceptionWasRaised) {
         // Exception logged via the WorkerPrivate ErrorReporter
       } else {
         runnable = new ResumeRequest(mInterceptedChannel);
       }
@@ -1582,16 +1583,22 @@ private:
         runnable = new CancelChannelRunnable(mInterceptedChannel,
                                              mRegistration,
                                              NS_ERROR_INTERCEPTION_FAILED);
       }
 
       MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
     }
 
+    RefPtr<Promise> waitUntilPromise = event->GetPromise();
+    if (waitUntilPromise) {
+      KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
+                                                 waitUntilPromise);
+    }
+
     return true;
   }
 
   nsresult
   HandleBodyWithHeaders(nsIInputStream* aUploadStream)
   {
     // We are dealing with an nsMIMEInputStream which uses string input streams
     // under the hood, so all of the data is available synchronously.