Bug 1057994 - DataStore should not dispatch runnables in a worker when it is shutting down, r=bent
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 17 Dec 2014 14:49:36 +0000
changeset 220194 aa82fdbf336298bbaa2a48ee8d0ab6245b635fcd
parent 220193 8cd573dc801ac3a8b9857ff3136e6e6ee8ad33a0
child 220195 9d0ed89e7c5820309c55f12b8a81122298934666
push id10457
push userryanvm@gmail.com
push dateThu, 18 Dec 2014 01:54:25 +0000
treeherderfx-team@0e441ff66c5e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs1057994
milestone37.0a1
Bug 1057994 - DataStore should not dispatch runnables in a worker when it is shutting down, r=bent
dom/datastore/tests/file_worker_close.html
dom/datastore/tests/file_worker_close.js
dom/datastore/tests/mochitest.ini
dom/datastore/tests/test_worker_close.html
dom/fetch/Fetch.cpp
dom/promise/Promise.cpp
dom/promise/PromiseWorkerProxy.h
dom/workers/DataStore.cpp
dom/workers/DataStoreCursor.cpp
dom/workers/Navigator.cpp
copy from dom/datastore/tests/file_basic_worker.html
copy to dom/datastore/tests/file_worker_close.html
--- a/dom/datastore/tests/file_basic_worker.html
+++ b/dom/datastore/tests/file_worker_close.html
@@ -1,26 +1,25 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Test for DataStore - basic operation on a readonly db</title>
+  <title>Test for DataStore</title>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
   var messages = [];
-  var worker = new Worker("file_basic_worker.js");
+  var worker = new Worker("file_worker_close.js");
 
   worker.onmessage = function(event) {
     messages.push(event.data)
 
     if (event.data == 'DONE') {
-      // Free the worker when all the tests are done.
       worker.terminate();
 
       // Fire message to the test_basic_worker.html.
       for (var i = 0; i < messages.length; i++) {
         alert(messages[i]);
       }
     }
   }
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_worker_close.js
@@ -0,0 +1,18 @@
+function is(a, b, msg) {
+  postMessage((a == b ? 'OK' : 'KO')+ ' ' + msg)
+}
+
+var store;
+navigator.getDataStores('foo').then(function(stores) {
+  is(stores.length, 1, "getDataStores('foo') returns 1 element");
+  is(stores[0].name, 'foo', 'The dataStore.name is foo');
+  is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
+  store = stores[0];
+  postMessage('DONE');
+});
+
+onclose = function() {
+  for (var i = 0; i < 100; ++i) {
+    store.get(123);
+  }
+}
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -27,16 +27,18 @@ support-files =
   file_event_maker.html
   file_event_receiver.html
   file_transactions.html
   file_basic_common.js
   file_sync_common.js
   file_bug1008044.html
   file_bug957086.html
   file_notify_system_message.html
+  file_worker_close.html
+  file_worker_close.js
 
 [test_app_install.html]
 skip-if = toolkit == 'gonk' # embed-apps doesn't work in the mochitest app
 [test_readonly.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 [test_basic.html]
 [test_basic_worker.html]
 [test_changes.html]
@@ -54,8 +56,9 @@ skip-if = (toolkit == 'android' && proce
 [test_bug986056.html]
 [test_oop_events.html]
 [test_transactions.html]
 [test_bug1008044.html]
 [test_bug957086.html]
 [test_bug1058108.html]
 [test_notify_system_message.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || toolkit == 'win' #bug 1053662 - Timeout prone
+[test_worker_close.html]
copy from dom/datastore/tests/test_basic_worker.html
copy to dom/datastore/tests/test_worker_close.html
--- a/dom/datastore/tests/test_basic_worker.html
+++ b/dom/datastore/tests/test_worker_close.html
@@ -1,21 +1,21 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Test for DataStore - basic operation on a readonly db</title>
+  <title>Test for DataStore - Worker.onclose</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
-  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_basic_worker.html';
+  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_worker_close.html';
   var gApp;
 
   function cbError() {
     ok(false, "Error callback invoked");
     finish();
   }
 
   function installApp() {
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -209,18 +209,18 @@ public:
     promise->MaybeResolve(response);
 
     mResolver->mPromiseProxy->CleanUp(aCx);
     return true;
   }
 };
 
 WorkerFetchResolver::WorkerFetchResolver(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
-  : mPromiseProxy(new PromiseWorkerProxy(aWorkerPrivate, aPromise))
 {
+  mPromiseProxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
 }
 
 WorkerFetchResolver::~WorkerFetchResolver()
 {
 }
 
 void
 WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1294,35 +1294,51 @@ private:
   nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const JSStructuredCloneCallbacks* mCallbacks;
   JSAutoStructuredCloneBuffer mBuffer;
 
   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
   PromiseWorkerProxy::RunCallbackFunc mFunc;
 };
 
+/* static */
+already_AddRefed<PromiseWorkerProxy>
+PromiseWorkerProxy::Create(WorkerPrivate* aWorkerPrivate,
+                           Promise* aWorkerPromise,
+                           const JSStructuredCloneCallbacks* aCb)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(aWorkerPromise);
+
+  nsRefPtr<PromiseWorkerProxy> proxy =
+    new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise, aCb);
+
+  // We do this to make sure the worker thread won't shut down before the
+  // promise is resolved/rejected on the worker thread.
+  if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), proxy)) {
+    // Probably the worker is terminating. We cannot complete the operation
+    // and we have to release all the resources.
+    proxy->mCleanedUp = true;
+    proxy->mWorkerPromise = nullptr;
+    return nullptr;
+  }
+
+  return proxy.forget();
+}
+
 PromiseWorkerProxy::PromiseWorkerProxy(WorkerPrivate* aWorkerPrivate,
                                        Promise* aWorkerPromise,
                                        const JSStructuredCloneCallbacks* aCallbacks)
   : mWorkerPrivate(aWorkerPrivate)
   , mWorkerPromise(aWorkerPromise)
   , mCleanedUp(false)
   , mCallbacks(aCallbacks)
   , mCleanUpLock("cleanUpLock")
 {
-  MOZ_ASSERT(mWorkerPrivate);
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mWorkerPromise);
-
-  // We do this to make sure the worker thread won't shut down before the
-  // promise is resolved/rejected on the worker thread.
-  if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
-    MOZ_ASSERT(false, "cannot add the worker feature!");
-    return;
-  }
 }
 
 PromiseWorkerProxy::~PromiseWorkerProxy()
 {
   MOZ_ASSERT(mCleanedUp);
   MOZ_ASSERT(!mWorkerPromise);
 }
 
@@ -1347,16 +1363,46 @@ PromiseWorkerProxy::StoreISupports(nsISu
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsMainThreadPtrHandle<nsISupports> supports(
     new nsMainThreadPtrHolder<nsISupports>(aSupports));
   mSupportsArray.AppendElement(supports);
 }
 
+namespace {
+
+class PromiseWorkerProxyControlRunnable MOZ_FINAL
+  : public WorkerControlRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mProxy;
+
+public:
+  PromiseWorkerProxyControlRunnable(WorkerPrivate* aWorkerPrivate,
+                                    PromiseWorkerProxy* aProxy)
+    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  virtual bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    mProxy->CleanUp(aCx);
+    return true;
+  }
+
+private:
+  ~PromiseWorkerProxyControlRunnable()
+  {}
+};
+
+} // anonymous namespace
+
 void
 PromiseWorkerProxy::RunCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue,
                                 RunCallbackFunc aFunc)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   MutexAutoLock lock(mCleanUpLock);
@@ -1375,17 +1421,21 @@ PromiseWorkerProxy::RunCallback(JSContex
   }
 
   nsRefPtr<PromiseWorkerProxyRunnable> runnable =
     new PromiseWorkerProxyRunnable(this,
                                    mCallbacks,
                                    Move(buffer),
                                    aFunc);
 
-  runnable->Dispatch(aCx);
+  if (!runnable->Dispatch(aCx)) {
+    nsRefPtr<WorkerControlRunnable> runnable =
+      new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this);
+    mWorkerPrivate->DispatchControlRunnable(runnable);
+  }
 }
 
 void
 PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
                                      JS::Handle<JS::Value> aValue)
 {
   RunCallback(aCx, aValue, &Promise::ResolveInternal);
 }
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -60,19 +60,20 @@ class PromiseWorkerProxy : public Promis
                            public workers::WorkerFeature
 {
   friend class PromiseWorkerProxyRunnable;
 
   // This overrides the non-threadsafe refcounting in PromiseNativeHandler.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseWorkerProxy)
 
 public:
-  PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
-                     Promise* aWorkerPromise,
-                     const JSStructuredCloneCallbacks* aCallbacks = nullptr);
+  static already_AddRefed<PromiseWorkerProxy>
+  Create(workers::WorkerPrivate* aWorkerPrivate,
+         Promise* aWorkerPromise,
+         const JSStructuredCloneCallbacks* aCallbacks = nullptr);
 
   workers::WorkerPrivate* GetWorkerPrivate() const;
 
   Promise* GetWorkerPromise() const;
 
   void StoreISupports(nsISupports* aSupports);
 
   void CleanUp(JSContext* aCx);
@@ -82,16 +83,20 @@ protected:
                                 JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
   virtual void RejectedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
   virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
 
 private:
+  PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
+                     Promise* aWorkerPromise,
+                     const JSStructuredCloneCallbacks* aCallbacks = nullptr);
+
   virtual ~PromiseWorkerProxy();
 
   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
   typedef void (Promise::*RunCallbackFunc)(JSContext*,
                                            JS::Handle<JS::Value>);
 
   void RunCallback(JSContext* aCx,
                    JS::Handle<JS::Value> aValue,
--- a/dom/workers/DataStore.cpp
+++ b/dom/workers/DataStore.cpp
@@ -132,89 +132,112 @@ protected:
   {
     AssertIsOnMainThread();
 
     mReadOnly = mBackingStore->GetReadOnly(mRv);
     return true;
   }
 };
 
+class DataStoreProxyRunnable : public DataStoreRunnable
+{
+public:
+  DataStoreProxyRunnable(WorkerPrivate* aWorkerPrivate,
+                         const nsMainThreadPtrHandle<DataStore>& aBackingStore,
+                         Promise* aWorkerPromise)
+    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    mPromiseWorkerProxy =
+      PromiseWorkerProxy::Create(aWorkerPrivate, aWorkerPromise);
+  }
+
+  bool Dispatch(JSContext* aCx)
+  {
+    if (mPromiseWorkerProxy) {
+      return DataStoreRunnable::Dispatch(aCx);
+    }
+
+    // If the creation of mProxyWorkerProxy failed, the worker is terminating.
+    // In this case we don't want to dispatch the runnable and we should stop
+    // the promise chain here.
+    return true;
+  }
+
+protected:
+  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+};
+
 // A DataStoreRunnable to run DataStore::Get(...) on the main thread.
-class DataStoreGetRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreGetRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   Sequence<OwningStringOrUnsignedLong> mId;
   ErrorResult& mRv;
 
 public:
   DataStoreGetRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        const Sequence<OwningStringOrUnsignedLong>& aId,
                        ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     if (!mId.AppendElements(aId)) {
       mRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     }
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     nsRefPtr<Promise> promise = mBackingStore->Get(mId, mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Put(...) on the main thread.
-class DataStorePutRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStorePutRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   JSAutoStructuredCloneBuffer mObjBuffer;
   const StringOrUnsignedLong& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStorePutRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        JSContext* aCx,
                        JS::Handle<JS::Value> aObj,
                        const StringOrUnsignedLong& aId,
                        const nsAString& aRevisionId,
                        ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // This needs to be structured cloned while it's still on the worker thread.
     if (!mObjBuffer.write(aCx, aObj)) {
       JS_ClearPendingException(aCx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     }
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -239,49 +262,45 @@ protected:
                                                    mRevisionId,
                                                    mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Add(...) on the main thread.
-class DataStoreAddRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreAddRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   JSAutoStructuredCloneBuffer mObjBuffer;
   const Optional<StringOrUnsignedLong>& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreAddRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        JSContext* aCx,
                        JS::Handle<JS::Value> aObj,
                        const Optional<StringOrUnsignedLong>& aId,
                        const nsAString& aRevisionId,
                        ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // This needs to be structured cloned while it's still on the worker thread.
     if (!mObjBuffer.write(aCx, aObj)) {
       JS_ClearPendingException(aCx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     }
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -307,76 +326,68 @@ protected:
                                                    mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Remove(...) on the main
 // thread.
-class DataStoreRemoveRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreRemoveRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const StringOrUnsignedLong& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreRemoveRunnable(WorkerPrivate* aWorkerPrivate,
                           const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                           Promise* aWorkerPromise,
                           const StringOrUnsignedLong& aId,
                           const nsAString& aRevisionId,
                           ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     nsRefPtr<Promise> promise = mBackingStore->Remove(mId, mRevisionId, mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Clear(...) on the main thread.
-class DataStoreClearRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreClearRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreClearRunnable(WorkerPrivate* aWorkerPrivate,
                          const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                          Promise* aWorkerPromise,
                          const nsAString& aRevisionId,
                          ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -621,34 +632,30 @@ WorkerDataStore::GetRevisionId(JSContext
                                    mBackingStore,
                                    &DataStore::GetRevisionId,
                                    aRevisionId,
                                    aRv);
   runnable->Dispatch(aCx);
 }
 
 // A DataStoreRunnable to run DataStore::GetLength(...) on the main thread.
-class DataStoreGetLengthRunnable MOZ_FINAL : public DataStoreRunnable
+class DataStoreGetLengthRunnable MOZ_FINAL : public DataStoreProxyRunnable
 {
-  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   ErrorResult& mRv;
 
 public:
   DataStoreGetLengthRunnable(WorkerPrivate* aWorkerPrivate,
                              const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                              Promise* aWorkerPromise,
                              ErrorResult& aRv)
-    : DataStoreRunnable(aWorkerPrivate, aBackingStore)
+    : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
--- a/dom/workers/DataStoreCursor.cpp
+++ b/dom/workers/DataStoreCursor.cpp
@@ -77,17 +77,29 @@ public:
                               ErrorResult& aRv)
     : DataStoreCursorRunnable(aWorkerPrivate, aBackingCursor)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
+      PromiseWorkerProxy::Create(aWorkerPrivate, aWorkerPromise);
+  }
+
+  bool Dispatch(JSContext* aCx)
+  {
+    if (mPromiseWorkerProxy) {
+      return DataStoreCursorRunnable::Dispatch(aCx);
+    }
+
+    // If the creation of mProxyWorkerProxy failed, the worker is terminating.
+    // In this case we don't want to dispatch the runnable and we should stop
+    // the promise chain here.
+    return true;
   }
 
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -218,22 +218,36 @@ public:
     : WorkerMainThreadRunnable(aWorkerPrivate)
     , mName(aName)
     , mOwner(aOwner)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
+    // this might return null if the worker has started the close handler.
     mPromiseWorkerProxy =
-      new PromiseWorkerProxy(aWorkerPrivate,
-                             aWorkerPromise,
-                             &kGetDataStoresStructuredCloneCallbacks);
+      PromiseWorkerProxy::Create(aWorkerPrivate,
+                                 aWorkerPromise,
+                                 &kGetDataStoresStructuredCloneCallbacks);
   }
 
+  bool Dispatch(JSContext* aCx)
+  {
+    if (mPromiseWorkerProxy) {
+      return WorkerMainThreadRunnable::Dispatch(aCx);
+    }
+
+    // If the creation of mProxyWorkerProxy failed, the worker is terminating.
+    // In this case we don't want to dispatch the runnable and we should stop
+    // the promise chain here.
+    return true;
+  }
+
+
 protected:
   virtual bool
   MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     // Walk up to the containing window.
     WorkerPrivate* wp = mWorkerPrivate;