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
--- 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__