Bug 1289658 Make ExtendableMessageEvent.waitUntil() hold the service worker alive. r=asuth
authorBen Kelly <ben@wanderview.com>
Wed, 27 Jul 2016 10:32:12 -0400
changeset 348992 4d7badaa705c87a15855a701663f07f9911f6186
parent 348991 f0d2679582bea4d7d74ea47d4b1f7f458b1bd359
child 348993 950a962b75eae61cb9f0c5638b6b362427f7ebe6
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1289658
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1289658 Make ExtendableMessageEvent.waitUntil() hold the service worker alive. r=asuth
dom/workers/ServiceWorkerPrivate.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -99,33 +99,71 @@ ServiceWorkerPrivate::~ServiceWorkerPriv
   MOZ_ASSERT(!mWorkerPrivate);
   MOZ_ASSERT(!mTokenCount);
   MOZ_ASSERT(!mInfo);
   MOZ_ASSERT(mSupportsArray.IsEmpty());
 
   mIdleWorkerTimer->Cancel();
 }
 
+namespace {
+
+class MessageWaitUntilHandler final : public PromiseNativeHandler
+{
+ nsMainThreadPtrHandle<nsISupports> mKeepAliveToken;
+
+  ~MessageWaitUntilHandler()
+  {
+  }
+
+public:
+  explicit MessageWaitUntilHandler(const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken)
+    : mKeepAliveToken(aKeepAliveToken)
+  {
+    MOZ_ASSERT(mKeepAliveToken);
+  }
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    mKeepAliveToken = nullptr;
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    mKeepAliveToken = nullptr;
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS0(MessageWaitUntilHandler)
+
+} // anonymous namespace
+
 nsresult
 ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
                                        JS::Handle<JS::Value> aMessage,
                                        const Optional<Sequence<JS::Value>>& aTransferable,
                                        UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
 {
   ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent, nullptr));
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   MOZ_ASSERT(mKeepAliveToken);
   nsMainThreadPtrHandle<nsISupports> token(
     new nsMainThreadPtrHolder<nsISupports>(mKeepAliveToken));
 
+  RefPtr<PromiseNativeHandler> handler = new MessageWaitUntilHandler(token);
+
   mWorkerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
-                                             Move(aClientInfo), token,
+                                             Move(aClientInfo), handler,
                                              rv);
   return rv.StealNSResult();
 }
 
 namespace {
 
 class CheckScriptEvaluationWithCallback final : public WorkerRunnable
 {
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -54,16 +54,17 @@
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseDebugging.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/SimpleGlobalObject.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerGlobalScopeBinding.h"
 #include "mozilla/Preferences.h"
@@ -659,35 +660,33 @@ private:
 };
 
 class MessageEventRunnable final : public WorkerRunnable
                                  , public StructuredCloneHolder
 {
   // This is only used for messages dispatched to a service worker.
   UniquePtr<ServiceWorkerClientInfo> mEventSource;
 
-  // This is only used to hold service workers alive while dispatching the
-  // message event.
-  nsMainThreadPtrHandle<nsISupports> mKeepAliveToken;
+  RefPtr<PromiseNativeHandler> mHandler;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHolder(CloningSupported, TransferringSupported,
                           SameProcessDifferentThread)
   {
   }
 
   void
   SetServiceWorkerData(UniquePtr<ServiceWorkerClientInfo>&& aSource,
-                       const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken)
+                       PromiseNativeHandler* aHandler)
   {
     mEventSource = Move(aSource);
-    mKeepAliveToken = aKeepAliveToken;
+    mHandler = aHandler;
   }
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(aTarget->GetParentObject());
 
@@ -736,42 +735,43 @@ public:
     if (NS_WARN_IF(rv.Failed())) {
       xpc::Throw(aCx, rv.StealNSResult());
       return false;
     }
 
     nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts();
 
     nsCOMPtr<nsIDOMEvent> domEvent;
+    RefPtr<ExtendableMessageEvent> extendableEvent;
     // For messages dispatched to service worker, use ExtendableMessageEvent
     // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#extendablemessage-event-section
     if (mEventSource) {
       RefPtr<ServiceWorkerClient> client =
         new ServiceWorkerWindowClient(aTarget, *mEventSource);
 
       RootedDictionary<ExtendableMessageEventInit> init(aCx);
 
       init.mBubbles = false;
       init.mCancelable = false;
 
       init.mData = messageData;
       init.mPorts.Construct();
       init.mPorts.Value().SetNull();
 
       ErrorResult rv;
-      RefPtr<ExtendableMessageEvent> event = ExtendableMessageEvent::Constructor(
+      extendableEvent = ExtendableMessageEvent::Constructor(
         aTarget, NS_LITERAL_STRING("message"), init, rv);
       if (NS_WARN_IF(rv.Failed())) {
         rv.SuppressException();
         return false;
       }
-      event->SetSource(client);
-      event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
-                                          ports));
-      domEvent = do_QueryObject(event);
+      extendableEvent->SetSource(client);
+      extendableEvent->SetPorts(new MessagePortList(static_cast<dom::Event*>(extendableEvent.get()),
+                                                    ports));
+      domEvent = do_QueryObject(extendableEvent);
     } else {
       RefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
       event->InitMessageEvent(nullptr,
                               NS_LITERAL_STRING("message"),
                               false /* non-bubbling */,
                               false /* cancelable */,
                               messageData,
                               EmptyString(),
@@ -782,16 +782,30 @@ public:
                                           ports));
       domEvent = do_QueryObject(event);
     }
 
     domEvent->SetTrusted(true);
 
     nsEventStatus dummy = nsEventStatus_eIgnore;
     aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
+
+    if (extendableEvent && mHandler) {
+      RefPtr<Promise> waitUntilPromise = extendableEvent->GetPromise();
+      if (!waitUntilPromise) {
+        waitUntilPromise = Promise::Resolve(parent, aCx,
+                                            JS::UndefinedHandleValue, rv);
+        MOZ_RELEASE_ASSERT(!rv.Failed());
+      }
+
+      MOZ_ASSERT(waitUntilPromise);
+
+      waitUntilPromise->AppendNativeHandler(mHandler);
+    }
+
     return true;
   }
 
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     if (mBehavior == ParentThreadUnchangedBusyCount) {
@@ -2943,17 +2957,17 @@ WorkerPrivateParent<Derived>::ForgetMain
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::PostMessageInternal(
                                             JSContext* aCx,
                                             JS::Handle<JS::Value> aMessage,
                                             const Optional<Sequence<JS::Value>>& aTransferable,
                                             UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
-                                            const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken,
+                                            PromiseNativeHandler* aHandler,
                                             ErrorResult& aRv)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     if (mParentStatus > Running) {
       return;
@@ -3005,17 +3019,17 @@ WorkerPrivateParent<Derived>::PostMessag
     timelines->AddMarkerForAllObservedDocShells(start);
     timelines->AddMarkerForAllObservedDocShells(end);
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  runnable->SetServiceWorkerData(Move(aClientInfo), aKeepAliveToken);
+  runnable->SetServiceWorkerData(Move(aClientInfo), aHandler);
 
   if (!runnable->Dispatch()) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
 template <class Derived>
 void
@@ -3028,22 +3042,22 @@ WorkerPrivateParent<Derived>::PostMessag
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::PostMessageToServiceWorker(
                              JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
-                             const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken,
+                             PromiseNativeHandler* aHandler,
                              ErrorResult& aRv)
 {
   AssertIsOnMainThread();
   PostMessageInternal(aCx, aMessage, aTransferable, Move(aClientInfo),
-                      aKeepAliveToken, aRv);
+                      aHandler, aRv);
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateContextOptions(
                                     const JS::ContextOptions& aContextOptions)
 {
   AssertIsOnParentThread();
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -52,16 +52,17 @@ namespace JS {
 struct RuntimeStats;
 } // namespace JS
 
 namespace mozilla {
 namespace dom {
 class Function;
 class MessagePort;
 class MessagePortIdentifier;
+class PromiseNativeHandler;
 class StructuredCloneHolder;
 class WorkerDebuggerGlobalScope;
 class WorkerGlobalScope;
 } // namespace dom
 namespace ipc {
 class PrincipalInfo;
 } // namespace ipc
 } // namespace mozilla
@@ -229,17 +230,17 @@ private:
   {
     return NotifyPrivate(Terminating);
   }
 
   void
   PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const Optional<Sequence<JS::Value>>& aTransferable,
                       UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
-                      const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken,
+                      PromiseNativeHandler* aHandler,
                       ErrorResult& aRv);
 
   nsresult
   DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget);
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -344,17 +345,17 @@ public:
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
               ErrorResult& aRv);
 
   void
   PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
-                             const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken,
+                             PromiseNativeHandler* aHandler,
                              ErrorResult& aRv);
 
   void
   UpdateContextOptions(const JS::ContextOptions& aContextOptions);
 
   void
   UpdateLanguages(const nsTArray<nsString>& aLanguages);