Bug 1426467: Part 3: Create a WorkerRunnable subclass, WorkerDebuggeeRunnable, for runnables that could run debuggee JS. r=baku
authorJim Blandy <jimb@mozilla.com>
Mon, 22 Oct 2018 15:46:04 +0000
changeset 491007 9acfa4df8ac9b091bc9d5204d0bf4f36ccd7c485
parent 491006 d826438ea26dc82c57ab98156b7ed84eb2354362
child 491008 9ca97be22a381d0eb8d9a53931f18e37dd50baaf
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersbaku
bugs1426467
milestone65.0a1
Bug 1426467: Part 3: Create a WorkerRunnable subclass, WorkerDebuggeeRunnable, for runnables that could run debuggee JS. r=baku Separating these runnables out under a separate subclass will let us delay their delivery while the content window is paused in the debugger. CancelingOnParentRunnable, used when the worker calls self.close(), to close the worker from the parent thread, must also be a WorkerDebuggeeRunnable, since it must be processed only after all prior messages/errors from the worker. Depends on D9218 Differential Revision: https://phabricator.services.mozilla.com/D9219
dom/workers/MessageEventRunnable.cpp
dom/workers/MessageEventRunnable.h
dom/workers/WorkerError.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerRunnable.h
--- a/dom/workers/MessageEventRunnable.cpp
+++ b/dom/workers/MessageEventRunnable.cpp
@@ -14,17 +14,17 @@
 #include "WorkerPrivate.h"
 #include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
 
 MessageEventRunnable::MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                                            TargetAndBusyBehavior aBehavior)
-  : WorkerRunnable(aWorkerPrivate, aBehavior)
+  : WorkerDebuggeeRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHolder(CloningSupported, TransferringSupported,
                           StructuredCloneScope::SameProcessDifferentThread)
 {
 }
 
 bool
 MessageEventRunnable::DispatchDOMEvent(JSContext* aCx,
                                        WorkerPrivate* aWorkerPrivate,
--- a/dom/workers/MessageEventRunnable.h
+++ b/dom/workers/MessageEventRunnable.h
@@ -12,17 +12,17 @@
 #include "mozilla/dom/StructuredCloneHolder.h"
 
 namespace mozilla {
 
 class DOMEventTargetHelper;
 
 namespace dom {
 
-class MessageEventRunnable final : public WorkerRunnable
+class MessageEventRunnable final : public WorkerDebuggeeRunnable
                                  , public StructuredCloneHolder
 {
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior);
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
--- a/dom/workers/WorkerError.cpp
+++ b/dom/workers/WorkerError.cpp
@@ -21,17 +21,17 @@
 #include "WorkerPrivate.h"
 #include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class ReportErrorRunnable final : public WorkerRunnable
+class ReportErrorRunnable final : public WorkerDebuggeeRunnable
 {
   WorkerErrorReport mReport;
 
 public:
   // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
   // aTarget is the worker object that we are going to fire an error at
   // (if any).
   static void
@@ -154,17 +154,17 @@ public:
     }
 
     // Otherwise log an error to the error console.
     WorkerErrorReport::LogErrorToConsole(aReport, aInnerWindowId);
   }
 
   ReportErrorRunnable(WorkerPrivate* aWorkerPrivate,
                       const WorkerErrorReport& aReport)
-  : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
+  : WorkerDebuggeeRunnable(aWorkerPrivate),
     mReport(aReport)
   { }
 
 private:
   virtual void
   PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
@@ -235,33 +235,33 @@ private:
 
     ReportError(aCx, parent, fireAtScope,
                 aWorkerPrivate->ParentEventTargetRef(),
                 mReport, innerWindowId);
     return true;
   }
 };
 
-class ReportGenericErrorRunnable final : public WorkerRunnable
+class ReportGenericErrorRunnable final : public WorkerDebuggeeRunnable
 {
 public:
   static void
   CreateAndDispatch(WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     RefPtr<ReportGenericErrorRunnable> runnable =
       new ReportGenericErrorRunnable(aWorkerPrivate);
     runnable->Dispatch();
   }
 
 private:
   explicit ReportGenericErrorRunnable(WorkerPrivate* aWorkerPrivate)
-    : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
+    : WorkerDebuggeeRunnable(aWorkerPrivate)
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   void
   PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -928,23 +928,23 @@ PRThreadFromThread(nsIThread* aThread)
   MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
   MOZ_ASSERT(result);
 
   return result;
 }
 
 // A runnable to cancel the worker from the parent thread when self.close() is
 // called. This runnable is executed on the parent process in order to cancel
-// the current runnable. It uses a normal WorkerRunnable in order to be sure
-// that all the pending WorkerRunnables are executed before this.
-class CancelingOnParentRunnable final : public WorkerRunnable
+// the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be sure
+// that all the pending WorkerDebuggeeRunnables are executed before this.
+class CancelingOnParentRunnable final : public WorkerDebuggeeRunnable
 {
 public:
   explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
-    : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
+    : WorkerDebuggeeRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
   {}
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->Cancel();
     return true;
   }
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -83,16 +83,31 @@ public:
 
   // See above note about Cancel().
   virtual bool
   IsCanceled() const
   {
     return mCanceled != 0;
   }
 
+  // True if this runnable is handled by running JavaScript in some global that
+  // could possibly be a debuggee, and thus needs to be deferred when the target
+  // is paused in the debugger, until the JavaScript invocation in progress has
+  // run to completion. Examples are MessageEventRunnable and
+  // ReportErrorRunnable. These runnables are segregated into separate
+  // ThrottledEventQueues, which the debugger pauses.
+  //
+  // Note that debugger runnables do not fall in this category, since we don't
+  // support debugging the debugger server at the moment.
+  virtual bool
+  IsDebuggeeRunnable() const
+  {
+    return false;
+  }
+
   static WorkerRunnable*
   FromRunnable(nsIRunnable* aRunnable);
 
 protected:
   WorkerRunnable(WorkerPrivate* aWorkerPrivate,
                  TargetAndBusyBehavior aBehavior = WorkerThreadModifyBusyCount)
 #ifdef DEBUG
   ;
@@ -493,12 +508,50 @@ private:
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
 
   bool
   DispatchInternal() final;
 };
 
+// Runnables handled by content JavaScript (MessageEventRunnable, JavaScript
+// error reports, and so on) must not be delivered while that content is in the
+// midst of being debugged; the debuggee must be allowed to complete its current
+// JavaScript invocation and return to its own event loop. Only then is it
+// prepared for messages sent from the worker.
+//
+// Runnables that need to be deferred in this way should inherit from this
+// class. They will be routed to mMainThreadDebuggeeEventTarget, which is paused
+// while the window is suspended, as it is whenever the debugger spins its
+// nested event loop. When the debugger leaves its nested event loop, it resumes
+// the window, so that mMainThreadDebuggeeEventTarget will resume delivering
+// runnables from the worker when control returns to the main event loop.
+//
+// When a page enters the bfcache, it freezes all its workers. Since a frozen
+// worker processes only control runnables, it doesn't take any special
+// consideration to prevent WorkerDebuggeeRunnables sent from child to parent
+// workers from running; they'll never run anyway. But WorkerDebuggeeRunnables
+// from a top-level frozen worker to its parent window must not be delivered
+// either, even as the main thread event loop continues to spin. Thus, freezing
+// a top-level worker also pauses mMainThreadDebuggeeEventTarget.
+class WorkerDebuggeeRunnable : public WorkerRunnable
+{
+ protected:
+  WorkerDebuggeeRunnable(WorkerPrivate* aWorkerPrivate,
+                         TargetAndBusyBehavior aBehavior = ParentThreadUnchangedBusyCount)
+    : WorkerRunnable(aWorkerPrivate, aBehavior)
+  { }
+
+ private:
+  // This override is deliberately private: it doesn't make sense to call it if
+  // we know statically that we are a WorkerDebuggeeRunnable.
+  bool
+  IsDebuggeeRunnable() const override
+  {
+    return true;
+  }
+};
+
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_workers_workerrunnable_h__