Bug 790919 - Don't dispatch close event in Workers, r=bkelly
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 31 Aug 2016 21:33:05 -0700
changeset 312162 e4cb0bcf30a7b524f75a0b638018ed6af99d2fbb
parent 312161 edc6d676d4d73d1a0c97ade6aca2542fb58edaf8
child 312163 42031a1a09e87137a5cdbce322a78f9e38758f6b
push id81297
push useramarchesini@mozilla.com
push dateThu, 01 Sep 2016 04:33:50 +0000
treeherdermozilla-inbound@e4cb0bcf30a7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs790919
milestone51.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 790919 - Don't dispatch close event in Workers, r=bkelly
dom/webidl/WorkerGlobalScope.webidl
dom/workers/RuntimeService.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.h
dom/workers/test/closeOnGC_server.sjs
dom/workers/test/closeOnGC_worker.js
dom/workers/test/close_worker.js
dom/workers/test/mochitest.ini
dom/workers/test/terminate_worker.js
dom/workers/test/test_close.html
dom/workers/test/test_closeOnGC.html
dom/workers/test/test_terminate.html
toolkit/modules/subprocess/subprocess_worker_common.js
toolkit/modules/subprocess/subprocess_worker_unix.js
toolkit/modules/subprocess/subprocess_worker_win.js
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -48,16 +48,15 @@ WorkerGlobalScope implements GlobalCrypt
 WorkerGlobalScope implements IDBEnvironment;
 WorkerGlobalScope implements ImageBitmapFactories;
 
 // Not implemented yet: bug 1072107.
 // WorkerGlobalScope implements FontFaceSource;
 
 // Mozilla extensions
 partial interface WorkerGlobalScope {
-  attribute EventHandler onclose;
 
   void dump(optional DOMString str);
 
   // XXXbz no spec for this yet, because the webperf WG is a bit dysfunctional
   [Constant, Cached]
   readonly attribute Performance performance;
 };
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -210,28 +210,16 @@ public:
   {
     AssertIsOnMainThread();
     sDefaultJSSettings.ApplyGCSetting(aKey, aValue);
   }
 
   void
   UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue);
 
-  static uint32_t
-  GetContentCloseHandlerTimeoutSeconds()
-  {
-    return sDefaultJSSettings.content.maxScriptRuntime;
-  }
-
-  static uint32_t
-  GetChromeCloseHandlerTimeoutSeconds()
-  {
-    return sDefaultJSSettings.chrome.maxScriptRuntime;
-  }
-
 #ifdef JS_GC_ZEAL
   static void
   SetDefaultGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
   {
     AssertIsOnMainThread();
     sDefaultJSSettings.gcZeal = aGCZeal;
     sDefaultJSSettings.gcZealFrequency = aFrequency;
   }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -598,74 +598,16 @@ private:
     if (rv.MaybeSetPendingException(aCx)) {
       return false;
     }
 
     return true;
   }
 };
 
-class CloseEventRunnable final : public WorkerRunnable
-{
-public:
-  explicit CloseEventRunnable(WorkerPrivate* aWorkerPrivate)
-  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-  { }
-
-private:
-  virtual bool
-  PreDispatch(WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_CRASH("Don't call Dispatch() on CloseEventRunnable!");
-  }
-
-  virtual void
-  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
-  {
-    MOZ_CRASH("Don't call Dispatch() on CloseEventRunnable!");
-  }
-
-  virtual bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    aWorkerPrivate->CloseHandlerStarted();
-
-    WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
-
-    RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
-
-    event->InitEvent(NS_LITERAL_STRING("close"), false, false);
-    event->SetTrusted(true);
-
-    globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-
-    return true;
-  }
-
-  nsresult Cancel() override
-  {
-    // We need to run regardless.
-    Run();
-    return WorkerRunnable::Cancel();
-  }
-
-  virtual void
-  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
-          override
-  {
-    // Report errors.
-    WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
-
-    // Match the busy count increase from NotifyRunnable.
-    aWorkerPrivate->ModifyBusyCountFromWorker(false);
-
-    aWorkerPrivate->CloseHandlerFinished();
-  }
-};
-
 class MessageEventRunnable final : public WorkerRunnable
                                  , public StructuredCloneHolder
 {
   // This is only used for messages dispatched to a service worker.
   UniquePtr<ServiceWorkerClientInfo> mEventSource;
 
   RefPtr<PromiseNativeHandler> mHandler;
 
@@ -894,32 +836,38 @@ public:
                aStatus == Canceling || aStatus == Killing);
   }
 
 private:
   virtual bool
   PreDispatch(WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnParentThread();
-    // Modify here, but not in PostRun! This busy count addition will be matched
-    // by the CloseEventRunnable.
     return aWorkerPrivate->ModifyBusyCount(true);
   }
 
   virtual void
   PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
   {
     aWorkerPrivate->AssertIsOnParentThread();
     if (!aDispatchResult) {
       // We couldn't dispatch to the worker, which means it's already dead.
       // Undo the busy count modification.
       aWorkerPrivate->ModifyBusyCount(false);
     }
   }
 
+  virtual void
+  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
+          override
+  {
+    aWorkerPrivate->ModifyBusyCountFromWorker(false);
+    return;
+  }
+
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     bool ok = aWorkerPrivate->NotifyInternal(aCx, mStatus);
     MOZ_ASSERT(!JS_IsExceptionPending(aCx));
     return ok;
   }
 };
@@ -930,19 +878,17 @@ public:
   explicit CloseRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
   { }
 
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-    // This busy count will be matched by the CloseEventRunnable.
-    return aWorkerPrivate->ModifyBusyCount(true) &&
-           aWorkerPrivate->Close();
+    return aWorkerPrivate->Close();
   }
 };
 
 class FreezeRunnable final : public WorkerControlRunnable
 {
 public:
   explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
@@ -1351,121 +1297,16 @@ private:
 };
 
 void
 DummyCallback(nsITimer* aTimer, void* aClosure)
 {
   // Nothing!
 }
 
-class KillCloseEventRunnable final : public WorkerRunnable
-{
-  nsCOMPtr<nsITimer> mTimer;
-
-  class KillScriptRunnable final : public WorkerControlRunnable
-  {
-  public:
-    explicit KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
-    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-    { }
-
-  private:
-    virtual bool
-    PreDispatch(WorkerPrivate* aWorkerPrivate) override
-    {
-      // Silence bad assertions, this is dispatched from the timer thread.
-      return true;
-    }
-
-    virtual void
-    PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
-    {
-      // Silence bad assertions, this is dispatched from the timer thread.
-    }
-
-    virtual bool
-    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-    {
-      // Kill running script.
-      return false;
-    }
-  };
-
-public:
-  explicit KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate)
-  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-  { }
-
-  bool
-  SetTimeout(uint32_t aDelayMS)
-  {
-    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
-    if (!timer) {
-      return false;
-    }
-
-    RefPtr<KillScriptRunnable> runnable =
-      new KillScriptRunnable(mWorkerPrivate);
-
-    RefPtr<TimerThreadEventTarget> target =
-      new TimerThreadEventTarget(mWorkerPrivate, runnable);
-
-    if (NS_FAILED(timer->SetTarget(target))) {
-      return false;
-    }
-
-    if (NS_FAILED(timer->InitWithNamedFuncCallback(
-          DummyCallback, nullptr, aDelayMS, nsITimer::TYPE_ONE_SHOT,
-          "dom::workers::DummyCallback(1)"))) {
-      return false;
-    }
-
-    mTimer.swap(timer);
-    return true;
-  }
-
-private:
-  ~KillCloseEventRunnable()
-  {
-    if (mTimer) {
-      mTimer->Cancel();
-    }
-  }
-
-  virtual bool
-  PreDispatch(WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_CRASH("Don't call Dispatch() on KillCloseEventRunnable!");
-  }
-
-  virtual void
-  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
-  {
-    MOZ_CRASH("Don't call Dispatch() on KillCloseEventRunnable!");
-  }
-
-  virtual bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    if (mTimer) {
-      mTimer->Cancel();
-      mTimer = nullptr;
-    }
-
-    return true;
-  }
-
-  nsresult Cancel() override
-  {
-    // We need to run regardless.
-    Run();
-    return WorkerRunnable::Cancel();
-  }
-};
-
 class UpdateContextOptionsRunnable final : public WorkerControlRunnable
 {
   JS::ContextOptions mContextOptions;
 
 public:
   UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
                                const JS::ContextOptions& aContextOptions)
   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
@@ -4030,18 +3871,16 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
   , mPRThread(nullptr)
   , mDebuggerEventLoopLevel(0)
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
   , mFrozen(false)
   , mTimerRunning(false)
   , mRunningExpiredTimeouts(false)
-  , mCloseHandlerStarted(false)
-  , mCloseHandlerFinished(false)
   , mPendingEventQueueClearing(false)
   , mMemoryReporterRunning(false)
   , mBlockedForMemoryReporter(false)
   , mCancelAllPendingRunnables(false)
   , mPeriodicGCTimerRunning(false)
   , mIdleGCTimerRunning(false)
   , mWorkerScriptExecutedSuccessfully(false)
   , mOnLine(false)
@@ -4253,17 +4092,16 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
     // If the parent is going away give up now.
     Status parentStatus;
     {
       MutexAutoLock lock(aParent->mMutex);
       parentStatus = aParent->mStatus;
     }
 
     if (parentStatus > Running) {
-      NS_WARNING("Cannot create child workers from the close handler!");
       return NS_ERROR_FAILURE;
     }
 
     // StartAssignment() is used instead getter_AddRefs because, getter_AddRefs
     // does QI in debug build and, if this worker runs in a child process,
     // HttpChannelChild will crash because it's not thread-safe.
     rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
                                           loadInfo.mChannel.StartAssignment());
@@ -4531,22 +4369,23 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
 
   EnableMemoryReporter();
 
   InitializeGCTimers();
 
   Maybe<JSAutoCompartment> workerCompartment;
 
   for (;;) {
-    Status currentStatus;
+    Status currentStatus, previousStatus;
     bool debuggerRunnablesPending = false;
     bool normalRunnablesPending = false;
 
     {
       MutexAutoLock lock(mMutex);
+      previousStatus = mStatus;
 
       while (mControlQueue.IsEmpty() &&
              !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
              !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
         WaitForWorkerEvents();
       }
 
       auto result = ProcessAllControlRunnablesLocked();
@@ -4557,20 +4396,21 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
         // The state of the world may have changed, recheck it.
         normalRunnablesPending = NS_HasPendingEvents(mThread);
         // The debugger queue doesn't get cleared, so we can ignore that.
       }
 
       currentStatus = mStatus;
     }
 
-    // If the close handler has finished and all holders are done then we can
-    // kill this thread.
+    // if all holders are done then we can kill this thread.
     if (currentStatus != Running && !HasActiveHolders()) {
-      if (mCloseHandlerFinished && currentStatus != Killing) {
+
+      // If we just changed status, we must schedule the current runnables.
+      if (previousStatus != Running && currentStatus != Killing) {
         NotifyInternal(aCx, Killing);
         MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
 #ifdef DEBUG
         {
           MutexAutoLock lock(mMutex);
           currentStatus = mStatus;
         }
@@ -4641,20 +4481,18 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
         WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
         MOZ_ASSERT(globalScope);
 
         // Now *might* be a good time to GC. Let the JS engine make the decision.
         JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
         JS_MaybeGC(aCx);
       }
     } else if (normalRunnablesPending) {
-      MOZ_ASSERT(NS_HasPendingEvents(mThread));
-
       // Process a single runnable from the main queue.
-      MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
+      NS_ProcessNextEvent(mThread, false);
 
       normalRunnablesPending = NS_HasPendingEvents(mThread);
       if (normalRunnablesPending && GlobalScope()) {
         // Now *might* be a good time to GC. Let the JS engine make the decision.
         JSAutoCompartment ac(aCx, GlobalScope()->GetGlobalJSObject());
         JS_MaybeGC(aCx);
       }
     }
@@ -4846,17 +4684,17 @@ WorkerPrivate::InterruptCallback(JSConte
     }
 
     while ((mayContinue = MayContinueRunning())) {
       MutexAutoLock lock(mMutex);
       if (!mControlQueue.IsEmpty()) {
         break;
       }
 
-      WaitForWorkerEvents(PR_MillisecondsToInterval(RemainingRunTimeMS()));
+      WaitForWorkerEvents(PR_MillisecondsToInterval(UINT32_MAX));
     }
   }
 
   if (!mayContinue) {
     // We want only uncatchable exceptions here.
     NS_ASSERTION(!JS_IsExceptionPending(aCx),
                  "Should not have an exception set here!");
     return false;
@@ -5148,27 +4986,16 @@ WorkerPrivate::ClearDebuggerEventQueue()
   while (!mDebuggerQueue.IsEmpty()) {
     WorkerRunnable* runnable;
     mDebuggerQueue.Pop(runnable);
     // It should be ok to simply release the runnable, without running it.
     runnable->Release();
   }
 }
 
-uint32_t
-WorkerPrivate::RemainingRunTimeMS() const
-{
-  if (mKillTime.IsNull()) {
-    return UINT32_MAX;
-  }
-  TimeDuration runtime = mKillTime - TimeStamp::Now();
-  double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0;
-  return ms > double(UINT32_MAX) ? UINT32_MAX : uint32_t(ms);
-}
-
 bool
 WorkerPrivate::FreezeInternal()
 {
   AssertIsOnWorkerThread();
 
   NS_ASSERTION(!mFrozen, "Already frozen!");
 
   mFrozen = true;
@@ -5797,121 +5624,57 @@ WorkerPrivate::NotifyInternal(JSContext*
     // mutex must be acquired *after* mCrossThreadDispatcher's mutex when
     // they're both held.
     mCrossThreadDispatcher->Forget();
     mCrossThreadDispatcher = nullptr;
   }
 
   MOZ_ASSERT(previousStatus != Pending);
 
-  MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull());
-
   // Let all our holders know the new status.
   NotifyHolders(aCx, aStatus);
   MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
   // If this is the first time our status has changed then we need to clear the
   // main event queue.
   if (previousStatus == Running) {
     // NB: If we're in a sync loop, we can't clear the queue immediately,
     // because this is the wrong queue. So we have to defer it until later.
     if (!mSyncLoopStack.IsEmpty()) {
       mPendingEventQueueClearing = true;
     } else {
       ClearMainEventQueue(WorkerRan);
     }
   }
 
-  // If we've run the close handler, we don't need to do anything else.
-  if (mCloseHandlerFinished) {
-    return true;
-  }
-
   // If the worker script never ran, or failed to compile, we don't need to do
-  // anything else, except pretend that we ran the close handler.
+  // anything else.
   if (!GlobalScope()) {
-    mCloseHandlerStarted = true;
-    mCloseHandlerFinished = true;
     return true;
   }
 
-  // If this is the first time our status has changed we also need to schedule
-  // the close handler unless we're being shut down.
-  if (previousStatus == Running && aStatus != Killing) {
-    MOZ_ASSERT(!mCloseHandlerStarted && !mCloseHandlerFinished);
-
-    RefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(closeRunnable));
-  }
-
   if (aStatus == Closing) {
     // Notify parent to stop sending us messages and balance our busy count.
     RefPtr<CloseRunnable> runnable = new CloseRunnable(this);
     if (!runnable->Dispatch()) {
       return false;
     }
 
     // Don't abort the script.
     return true;
   }
 
-  if (aStatus == Terminating) {
-    // Only abort the script if we're not yet running the close handler.
-    return mCloseHandlerStarted;
-  }
-
-  if (aStatus == Canceling) {
-    // We need to enforce a timeout on the close handler.
-    MOZ_ASSERT(previousStatus >= Running && previousStatus <= Terminating);
-
-    uint32_t killSeconds = IsChromeWorker() ?
-      RuntimeService::GetChromeCloseHandlerTimeoutSeconds() :
-      RuntimeService::GetContentCloseHandlerTimeoutSeconds();
-
-    if (killSeconds) {
-      mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds);
-
-      if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable()) {
-        return false;
-      }
-    }
-
-    // Only abort the script if we're not yet running the close handler.
-    return mCloseHandlerStarted;
-  }
-
-  MOZ_ASSERT(aStatus == Killing);
-
-  mKillTime = TimeStamp::Now();
-
-  if (mCloseHandlerStarted && !mCloseHandlerFinished) {
-    ScheduleKillCloseEventRunnable();
-  }
+  MOZ_ASSERT(aStatus == Terminating ||
+             aStatus == Canceling ||
+             aStatus == Killing);
 
   // Always abort the script.
   return false;
 }
 
-bool
-WorkerPrivate::ScheduleKillCloseEventRunnable()
-{
-  AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mKillTime.IsNull());
-
-  RefPtr<KillCloseEventRunnable> killCloseEventRunnable =
-    new KillCloseEventRunnable(this);
-  if (!killCloseEventRunnable->SetTimeout(RemainingRunTimeMS())) {
-    return false;
-  }
-
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(killCloseEventRunnable));
-
-  return true;
-}
-
 void
 WorkerPrivate::ReportError(JSContext* aCx, const char* aFallbackMessage,
                            JSErrorReport* aReport)
 {
   AssertIsOnWorkerThread();
 
   if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
     return;
@@ -5960,19 +5723,18 @@ WorkerPrivate::ReportError(JSContext* aC
       nsDependentCString truncatedFallbackMessage(aFallbackMessage, 1024);
       AppendUTF8toUTF16(truncatedFallbackMessage, message);
     }
   }
 
   mErrorHandlerRecursionCount++;
 
   // Don't want to run the scope's error handler if this is a recursive error or
-  // if there was an error in the close handler or if we ran out of memory.
+  // if we ran out of memory.
   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
-                     !mCloseHandlerStarted &&
                      errorNumber != JSMSG_OUT_OF_MEMORY &&
                      JS::CurrentGlobalOrNull(aCx);
 
   ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
                                    filename, line, lineNumber,
                                    columnNumber, flags, errorNumber, exnType,
                                    mutedError, 0, exn);
 
@@ -6003,23 +5765,16 @@ WorkerPrivate::SetTimeout(JSContext* aCx
   const int32_t timerId = mNextTimeoutId++;
 
   Status currentStatus;
   {
     MutexAutoLock lock(mMutex);
     currentStatus = mStatus;
   }
 
-  // It's a script bug if setTimeout/setInterval are called from a close handler
-  // so throw an exception.
-  if (currentStatus == Closing) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return 0;
-  }
-
   // If the worker is trying to call setTimeout/setInterval and the parent
   // thread has initiated the close process then just silently fail.
   if (currentStatus >= Closing) {
     aRv.Throw(NS_ERROR_FAILURE);
     return 0;
   }
 
   nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -945,18 +945,16 @@ class WorkerPrivate : public WorkerPriva
   JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
   TimeStamp mKillTime;
   uint32_t mErrorHandlerRecursionCount;
   uint32_t mNextTimeoutId;
   Status mStatus;
   bool mFrozen;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
-  bool mCloseHandlerStarted;
-  bool mCloseHandlerFinished;
   bool mPendingEventQueueClearing;
   bool mMemoryReporterRunning;
   bool mBlockedForMemoryReporter;
   bool mCancelAllPendingRunnables;
   bool mPeriodicGCTimerRunning;
   bool mIdleGCTimerRunning;
   bool mWorkerScriptExecutedSuccessfully;
   bool mPreferences[WORKERPREF_COUNT];
@@ -1151,30 +1149,16 @@ public:
 
   bool
   RunExpiredTimeouts(JSContext* aCx);
 
   bool
   RescheduleTimeoutTimer(JSContext* aCx);
 
   void
-  CloseHandlerStarted()
-  {
-    AssertIsOnWorkerThread();
-    mCloseHandlerStarted = true;
-  }
-
-  void
-  CloseHandlerFinished()
-  {
-    AssertIsOnWorkerThread();
-    mCloseHandlerFinished = true;
-  }
-
-  void
   UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions);
 
   void
   UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
 
   void
   UpdatePreferenceInternal(WorkerPreference aPref, bool aValue);
 
@@ -1364,34 +1348,26 @@ private:
     AssertIsOnWorkerThread();
 
     Status status;
     {
       MutexAutoLock lock(mMutex);
       status = mStatus;
     }
 
-    if (status >= Killing) {
-      return false;
-    }
-    if (status >= Running) {
-      return mKillTime.IsNull() || RemainingRunTimeMS() > 0;
+    if (status < Terminating) {
+      return true;
     }
-    return true;
+
+    return false;
   }
 
-  uint32_t
-  RemainingRunTimeMS() const;
-
   void
   CancelAllTimeouts();
 
-  bool
-  ScheduleKillCloseEventRunnable();
-
   enum class ProcessAllControlRunnablesResult
   {
     // We did not process anything.
     Nothing,
     // We did process something, states may have changed, but we can keep
     // executing script.
     MayContinue,
     // We did process something, and should not continue executing script.
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -143,17 +143,16 @@ public:
 
   void
   Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const;
   void
   Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const;
 
   IMPL_EVENT_HANDLER(online)
   IMPL_EVENT_HANDLER(offline)
-  IMPL_EVENT_HANDLER(close)
 
   void
   Dump(const Optional<nsAString>& aString) const;
 
   Performance* GetPerformance();
 
   already_AddRefed<Promise>
   Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit, ErrorResult& aRv);
deleted file mode 100644
--- a/dom/workers/test/closeOnGC_server.sjs
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-function handleRequest(request, response)
-{
-  response.setHeader("Content-Type", "text/plain", false);
-  response.setHeader("Cache-Control", "no-cache", false);
-
-  if (request.method == "POST") {
-    setState("seenPost" + request.queryString, "1");
-    return;
-  }
-
-  if (request.method == "GET") {
-    if (getState("seenPost" + request.queryString) == "1") {
-      response.write("closed");
-    }
-    return;
-  }
-
-  response.setStatusLine(request.httpVersion, 404, "Not found");
-}
deleted file mode 100644
--- a/dom/workers/test/closeOnGC_worker.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-onclose = function() {
-  var xhr = new XMLHttpRequest();
-  xhr.open("POST", "closeOnGC_server.sjs" + location.search, false);
-  xhr.send();
-};
-
-postMessage("ready");
deleted file mode 100644
--- a/dom/workers/test/close_worker.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-onclose = function() {
-  postMessage("closed");
-  // Try to open a new worker.
-  try {
-    var worker = new Worker("close_worker.js");
-    throw new Error("We shouldn't get here!");
-  } catch (e) {
-    // pass
-  }
-};
-
-setTimeout(function () {
-  setTimeout(function () {
-    throw new Error("I should never run!");
-  }, 1000);
-  close();
-}, 1000);
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -6,19 +6,16 @@ support-files =
   bug1014466_data1.txt
   bug1014466_data2.txt
   bug1014466_worker.js
   bug1020226_worker.js
   bug1020226_frame.html
   bug998474_worker.js
   bug1063538_worker.js
   clearTimeouts_worker.js
-  closeOnGC_server.sjs
-  closeOnGC_worker.js
-  close_worker.js
   content_worker.js
   console_worker.js
   consoleReplaceable_worker.js
   csp_worker.js
   csp_worker.js^headers^
   404_server.sjs
   errorPropagation_iframe.html
   errorPropagation_worker.js
@@ -141,18 +138,16 @@ support-files =
 [test_bug1062920.html]
 [test_bug1063538.html]
 [test_bug1104064.html]
 [test_bug1132395.html]
 skip-if = true # bug 1176225
 [test_bug1132924.html]
 [test_chromeWorker.html]
 [test_clearTimeouts.html]
-[test_close.html]
-[test_closeOnGC.html]
 [test_console.html]
 [test_consoleAndBlobs.html]
 [test_consoleReplaceable.html]
 [test_consoleSharedWorkers.html]
 [test_contentWorker.html]
 [test_csp.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_dataURLWorker.html]
--- a/dom/workers/test/terminate_worker.js
+++ b/dom/workers/test/terminate_worker.js
@@ -1,13 +1,9 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
-onclose = function() {
-  postMessage("Closed!");
-}
-
 onmessage = function(event) {
   throw "No messages should reach me!";
 }
 
 setInterval(function() { postMessage("Still alive!"); }, 100);
deleted file mode 100644
--- a/dom/workers/test/test_close.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for DOM Worker Threads</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-  var worker = new Worker("close_worker.js");
-  worker.onmessage = function(event) {
-    is(event.data, "closed");
-    SimpleTest.finish();
-  }
-
-  SimpleTest.waitForExplicitFinish();
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/workers/test/test_closeOnGC.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for DOM Worker Threads</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-  var count = 0;
-
-  function testWorker(queryString) {
-    ++count;
-    var worker = new Worker("closeOnGC_worker.js?" + queryString);
-    worker.onmessage = function(event) {
-      is(event.data, "ready");
-      worker = null;
-    }
-
-    var interval = setInterval(function() {
-      var xhr = new XMLHttpRequest();
-      xhr.open("GET", "closeOnGC_server.sjs?" + queryString, false);
-      xhr.send();
-      if (xhr.responseText != "closed") {
-        SpecialPowers.gc();
-        return;
-      }
-      clearInterval(interval);
-      ok(true, "xhr correctly closed");
-      if (--count == 0) {
-        SimpleTest.finish();
-      }
-    }, 500);
-
-    return worker;
-  }
-
-  testWorker("white");
-  var worker = testWorker("gray");
-  worker.onerror = function() {};
-  worker.onerror.foo = worker;
-  worker = null;
-
-  SimpleTest.waitForExplicitFinish();
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/workers/test/test_terminate.html
+++ b/dom/workers/test/test_terminate.html
@@ -25,17 +25,17 @@ Tests of DOM Worker terminate feature
   var intervalCount = 0;
 
   var interval;
 
   var worker;
 
   function messageListener(event) {
     is(event.data, "Still alive!", "Correct message!");
-    if (messageCount++ == 20) {
+    if (++messageCount == 20) {
       ok(worker.onmessage === messageListener,
          "Correct listener before terminate");
 
       worker.terminate();
 
       var exception = false;
       try {
         worker.addEventListener("message", messageListener, false);
@@ -77,17 +77,17 @@ Tests of DOM Worker terminate feature
 
       worker.onmessage = function(event) { }
 
       interval = setInterval(testCount, 1000);
     }
   }
 
   function testCount() {
-    is(messageCount, 21, "Received another message after terminated!");
+    is(messageCount, 20, "Received another message after terminated!");
     if (intervalCount++ == 5) {
       clearInterval(interval);
       SimpleTest.finish();
     }
   }
 
   worker = new Worker("terminate_worker.js");
   worker.onmessage = messageListener;
--- a/toolkit/modules/subprocess/subprocess_worker_common.js
+++ b/toolkit/modules/subprocess/subprocess_worker_common.js
@@ -222,14 +222,8 @@ onmessage = event => {
 
     self.postMessage({
       msg: "failure",
       msgId,
       error: {},
     });
   });
 };
-
-onclose = event => {
-  io.shutdown();
-
-  self.postMessage({msg: "close"});
-};
--- a/toolkit/modules/subprocess/subprocess_worker_unix.js
+++ b/toolkit/modules/subprocess/subprocess_worker_unix.js
@@ -495,16 +495,17 @@ io = {
 
   shutdown() {
     if (this.running) {
       this.running = false;
 
       this.signal.cleanup();
       this.signal = null;
 
+      self.postMessage({msg: "close"});
       self.close();
     }
   },
 
   getPipe(pipeId) {
     let pipe = this.pipes.get(pipeId);
 
     if (!pipe) {
--- a/toolkit/modules/subprocess/subprocess_worker_win.js
+++ b/toolkit/modules/subprocess/subprocess_worker_win.js
@@ -594,16 +594,17 @@ io = {
 
   shutdown() {
     if (this.running) {
       this.running = false;
 
       this.signal.cleanup();
       this.signal = null;
 
+      self.postMessage({msg: "close"});
       self.close();
     }
   },
 
   getPipe(pipeId) {
     let pipe = this.pipes.get(pipeId);
 
     if (!pipe) {