Bug 1899503 - P3 PRemoteWorkerDebugger binding implementation. r=asuth
This patch implements the PRemoteWorkerDebugger binding. We are trying to keep the same logic debugger registration/unregistration to minimize the difference between the local debugger(the debugger on the content process main thread) The registration logic is After constructing a Worker, we start the debugger registration and block the parent thread until the registration is complete. Blocking the parent thread because we don't want to dispatch the ComplieScriptRunnable() and any other DebuggeeRunnables before the debugger is ready. Blocking the parent thread also forbids the IPC that we might not be able to handle during the debugger registration, such as SharedWorkerTerminateOpArgs which can terminate a "Pending" worker. After RemoteWorkerDebuggerManagerChild::SendRegister() is called, we use mRemoteDebuggerBindingCondVar to block the parent thread. After registration is done in the parent process, RemoteWorkerDebugger receives the RegistrationDone() on the WorkerThread, then calling mRemoteWorkerDebuggerBindingCondVar.Notify to unblock the parent thread. However, To ensure the message can be sent immediately after RemoteWorkerDebuggerParent binding, RemoteWorkerDebuggerChild binding needs to be completed in the Worker thread before calling RemoteWorkerDebuggerManagerChild::SendRegister(), so we wait for the child binding to complete in EnableRemoteDebugger(). RemoteWorkerDebuggerChild binding happens on the Worker thread when needed. The time points are the start of WorkerThreadPrimaryRunnable::Run() and WorkerPrivate::ThawInternal(). So when the Worker starts to run the event loops, we create the RemoteWorkerDebuggerChild and bind it to the child Endpoint. We then notify the parent thread that the child binding is done to start remote debugger registration on the parent process. Through this implementation, the remote debugger mechanism has similar logic to the local debugger and can be used by all types of Workers. Depends on D231682 Differential Revision: https://phabricator.services.mozilla.com/D230260
This commit is contained in:
@@ -1195,6 +1195,8 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) {
|
||||
}
|
||||
}
|
||||
|
||||
aWorkerPrivate.SetIsQueued(queued);
|
||||
|
||||
// From here on out we must call UnregisterWorker if something fails!
|
||||
if (parent) {
|
||||
if (!parent->AddChildWorker(aWorkerPrivate)) {
|
||||
@@ -1362,6 +1364,7 @@ bool RuntimeService::ScheduleWorker(WorkerPrivate& aWorkerPrivate) {
|
||||
|
||||
aWorkerPrivate.SetThread(thread.unsafeGetRawPtr());
|
||||
JSContext* cx = CycleCollectedJSContext::Get()->Context();
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new WorkerThreadPrimaryRunnable(
|
||||
&aWorkerPrivate, thread.clonePtr(), JS_GetParentRuntime(cx));
|
||||
if (NS_FAILED(
|
||||
@@ -1370,6 +1373,12 @@ bool RuntimeService::ScheduleWorker(WorkerPrivate& aWorkerPrivate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The worker was queued when creating, so enable remote debugger now.
|
||||
if (aWorkerPrivate.IsQueued()) {
|
||||
aWorkerPrivate.SetIsQueued(false);
|
||||
aWorkerPrivate.EnableRemoteDebugger();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2162,6 +2171,11 @@ WorkerThreadPrimaryRunnable::Run() {
|
||||
}
|
||||
|
||||
failureCleanup.release();
|
||||
|
||||
// Binding the RemoteWorkerDebugger child endpoint after initailzation
|
||||
// successfully.
|
||||
// mWorkerPrivate->BindRemoteWorkerDebuggerChild();
|
||||
|
||||
runLoopRan = true;
|
||||
|
||||
{
|
||||
|
||||
@@ -208,6 +208,14 @@ WorkerDebugger::GetIsChrome(bool* aResult) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::GetIsRemote(bool* aResult) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::GetIsInitialized(bool* aResult) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@@ -77,7 +77,15 @@ class WorkerDebuggerEnumerator final : public nsSimpleEnumerator {
|
||||
public:
|
||||
explicit WorkerDebuggerEnumerator(
|
||||
const nsTArray<nsCOMPtr<nsIWorkerDebugger>>& aDebuggers)
|
||||
: mDebuggers(aDebuggers.Clone()), mIndex(0) {}
|
||||
: mIndex(0) {
|
||||
for (auto debugger : aDebuggers) {
|
||||
bool isRemote;
|
||||
Unused << debugger->GetIsRemote(&isRemote);
|
||||
if (!isRemote) {
|
||||
mDebuggers.AppendElement(debugger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_DECL_NSISIMPLEENUMERATOR
|
||||
|
||||
@@ -275,10 +283,9 @@ void WorkerDebuggerManager::RegisterDebugger(
|
||||
AssertIsOnMainThread();
|
||||
|
||||
mDebuggers.AppendElement(aRemoteWorkerDebugger);
|
||||
|
||||
for (const auto& listener : CloneListeners()) {
|
||||
listener->OnRegister(aRemoteWorkerDebugger);
|
||||
}
|
||||
// for (const auto& listener : CloneListeners()) {
|
||||
// listener->OnRegister(aRemoteWorkerDebugger);
|
||||
// }
|
||||
}
|
||||
|
||||
void WorkerDebuggerManager::UnregisterDebugger(
|
||||
@@ -287,10 +294,9 @@ void WorkerDebuggerManager::UnregisterDebugger(
|
||||
AssertIsOnMainThread();
|
||||
|
||||
mDebuggers.RemoveElement(aRemoteWorkerDebugger);
|
||||
|
||||
for (const auto& listener : CloneListeners()) {
|
||||
listener->OnUnregister(aRemoteWorkerDebugger);
|
||||
}
|
||||
// for (const auto& listener : CloneListeners()) {
|
||||
// listener->OnUnregister(aRemoteWorkerDebugger);
|
||||
// }
|
||||
}
|
||||
|
||||
void WorkerDebuggerManager::RegisterDebuggerMainThread(
|
||||
@@ -349,8 +355,10 @@ nsCOMPtr<nsIWorkerDebugger> WorkerDebuggerManager::GetDebuggerById(
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(!aWorkerId.IsEmpty());
|
||||
for (auto debugger : mDebuggers) {
|
||||
nsAutoString workerId;
|
||||
bool isRemote;
|
||||
debugger->GetId(workerId);
|
||||
if (workerId.Equals(aWorkerId)) {
|
||||
debugger->GetIsRemote(&isRemote);
|
||||
if (workerId.Equals(aWorkerId) && isRemote) {
|
||||
return debugger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,48 @@ class WrappedControlRunnable final : public WorkerControlRunnable {
|
||||
#endif
|
||||
};
|
||||
|
||||
class WrappedDebuggerRunnable final : public WorkerDebuggerRunnable {
|
||||
nsCOMPtr<nsIRunnable> mInner;
|
||||
|
||||
~WrappedDebuggerRunnable() = default;
|
||||
|
||||
public:
|
||||
WrappedDebuggerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsCOMPtr<nsIRunnable>&& aInner)
|
||||
: WorkerDebuggerRunnable("WrappedDebuggerRunnable"),
|
||||
mInner(std::move(aInner)) {}
|
||||
|
||||
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
|
||||
// Silence bad assertions, this can be dispatched from any thread.
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) override {
|
||||
// Silence bad assertions, this can be dispatched from any thread.
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
||||
mInner->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
|
||||
NS_IMETHOD GetName(nsACString& aName) override {
|
||||
aName.AssignLiteral("WrappedDebuggerRunnable(");
|
||||
if (nsCOMPtr<nsINamed> named = do_QueryInterface(mInner)) {
|
||||
nsAutoCString containedName;
|
||||
named->GetName(containedName);
|
||||
aName.Append(containedName);
|
||||
} else {
|
||||
aName.AppendLiteral("?");
|
||||
}
|
||||
aName.AppendLiteral(")");
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(WorkerEventTarget, nsIEventTarget, nsISerialEventTarget)
|
||||
@@ -127,6 +169,23 @@ WorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mBehavior == Behavior::DebuggerOnly) {
|
||||
RefPtr<WorkerDebuggerRunnable> r =
|
||||
new WrappedDebuggerRunnable(mWorkerPrivate, std::move(runnable));
|
||||
LOGV(
|
||||
("WorkerEventTarget::Dispatch [%p] Wrapped runnable as debugger "
|
||||
"runnable(%p)",
|
||||
this, r.get()));
|
||||
if (!r->Dispatch(mWorkerPrivate)) {
|
||||
LOGV(
|
||||
("WorkerEventTarget::Dispatch [%p] Dispatch as debugger runnable(%p) "
|
||||
"fail",
|
||||
this, r.get()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mBehavior == Behavior::Hybrid) {
|
||||
LOGV(("WorkerEventTarget::Dispatch [%p] Dispatch as normal runnable(%p)",
|
||||
this, runnable.get()));
|
||||
|
||||
@@ -24,7 +24,9 @@ class WorkerEventTarget final : public nsISerialEventTarget {
|
||||
// with a holder in place.)
|
||||
//
|
||||
// * ControlOnly targets will simply dispatch a control runnable.
|
||||
enum class Behavior : uint8_t { Hybrid, ControlOnly };
|
||||
//
|
||||
// * DebuggerOnly targets will simply dispatch a debugger runnable.
|
||||
enum class Behavior : uint8_t { Hybrid, ControlOnly, DebuggerOnly };
|
||||
|
||||
private:
|
||||
mozilla::Mutex mMutex;
|
||||
|
||||
@@ -49,8 +49,10 @@
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/PerformanceStorageWorker.h"
|
||||
#include "mozilla/dom/PromiseDebugging.h"
|
||||
#include "mozilla/dom/PRemoteWorkerDebuggerParent.h"
|
||||
#include "mozilla/dom/ReferrerInfo.h"
|
||||
#include "mozilla/dom/RemoteWorkerChild.h"
|
||||
#include "mozilla/dom/RemoteWorkerDebuggerChild.h"
|
||||
#include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h"
|
||||
#include "mozilla/dom/RemoteWorkerService.h"
|
||||
#include "mozilla/dom/RootedDictionary.h"
|
||||
@@ -884,6 +886,17 @@ class MemoryPressureRunnable final : public WorkerControlRunnable {
|
||||
}
|
||||
};
|
||||
|
||||
class DisableRemoteDebuggerRunnable final : public WorkerControlRunnable {
|
||||
public:
|
||||
explicit DisableRemoteDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerControlRunnable("DisableRemoteDebuggerRunnable") {}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
|
||||
aWorkerPrivate->DisableRemoteDebuggerOnWorkerThread();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool StartsWithExplicit(nsACString& s) {
|
||||
return StringBeginsWith(s, "explicit/"_ns);
|
||||
@@ -1666,7 +1679,11 @@ nsresult WorkerPrivate::DispatchLockHeld(
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
|
||||
// Postpone the debuggee runnable dispatching while remote debugger
|
||||
// registration
|
||||
if (runnable->IsDebuggeeRunnable() && !mDebuggerReady &&
|
||||
!mRemoteDebuggerReady &&
|
||||
(!mRemoteDebuggerRegistered && XRE_IsParentProcess())) {
|
||||
MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
|
||||
mDelayedDebuggeeRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
@@ -1755,6 +1772,233 @@ void WorkerPrivate::DisableDebugger() {
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPrivate::BindRemoteWorkerDebuggerChild() {
|
||||
AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<RemoteWorkerDebuggerChild> debugger =
|
||||
MakeRefPtr<RemoteWorkerDebuggerChild>(this);
|
||||
mDebuggerChildEp.Bind(debugger);
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger);
|
||||
mRemoteDebugger = std::move(debugger);
|
||||
mDebuggerBindingCondVar.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPrivate::CreateRemoteDebuggerEndpoints() {
|
||||
AssertIsOnParentThread();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger &&
|
||||
!mDebuggerParentEp.IsValid() &&
|
||||
!mDebuggerChildEp.IsValid());
|
||||
|
||||
Unused << NS_WARN_IF(NS_FAILED(PRemoteWorkerDebugger::CreateEndpoints(
|
||||
&mDebuggerParentEp, &mDebuggerChildEp)));
|
||||
}
|
||||
|
||||
void WorkerPrivate::SetIsRemoteDebuggerRegistered(const bool& aRegistered) {
|
||||
AssertIsOnWorkerThread();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRegistered) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mRemoteDebuggerRegistered != aRegistered);
|
||||
|
||||
mRemoteDebuggerRegistered = aRegistered;
|
||||
bool debuggerRegistered = mDebuggerRegistered && mRemoteDebuggerRegistered;
|
||||
if (mRemoteDebuggerReady && mDebuggerReady && debuggerRegistered) {
|
||||
LOGV(
|
||||
("WorkerPrivate::SetIsRemoteDebuggerRegistered [%p] dispatching "
|
||||
"the delayed debuggee runnables",
|
||||
this));
|
||||
// Dispatch all the delayed runnables without releasing the lock, to
|
||||
// ensure that the order in which debuggee runnables execute is the same
|
||||
// as the order in which they were originally dispatched.
|
||||
auto pending = std::move(mDelayedDebuggeeRunnables);
|
||||
for (uint32_t i = 0; i < pending.Length(); i++) {
|
||||
RefPtr<WorkerRunnable> runnable = std::move(pending[i]);
|
||||
Unused << NS_WARN_IF(
|
||||
NS_FAILED(DispatchLockHeld(runnable.forget(), nullptr, lock)));
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
|
||||
}
|
||||
mDebuggerBindingCondVar.Notify();
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<RemoteWorkerDebuggerChild> unregisteredDebugger;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
// Can not call RemoteWorkerDebuggerChild::Close() with lock. It causes
|
||||
// deadlock between mMutex and MessageChannel::mMonitor.
|
||||
unregisteredDebugger = std::move(mRemoteDebugger);
|
||||
// Force to set as unregistered, mRemoteDebuggerRegistered could be false
|
||||
// here since Worker quickly shutdown or initialization fails in
|
||||
// WorkerThreadPrimaryRunnable::Run().
|
||||
mRemoteDebuggerRegistered = aRegistered;
|
||||
}
|
||||
if (unregisteredDebugger) {
|
||||
unregisteredDebugger->Close();
|
||||
unregisteredDebugger = nullptr;
|
||||
}
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mDebuggerBindingCondVar.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPrivate::SetIsRemoteDebuggerReady(const bool& aReady) {
|
||||
AssertIsOnWorkerThread();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRemoteDebuggerReady == aReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool debuggerRegistered = mDebuggerRegistered && mRemoteDebuggerRegistered;
|
||||
|
||||
if (!aReady && debuggerRegistered) {
|
||||
// The debugger can only be marked as not ready during registration.
|
||||
return;
|
||||
}
|
||||
|
||||
mRemoteDebuggerReady = aReady;
|
||||
|
||||
if (mRemoteDebuggerReady && mDebuggerReady && debuggerRegistered) {
|
||||
LOGV(
|
||||
("WorkerPrivate::SetIsRemoteDebuggerReady [%p] dispatching "
|
||||
"the delayed debuggee runnables",
|
||||
this));
|
||||
// Dispatch all the delayed runnables without releasing the lock, to ensure
|
||||
// that the order in which debuggee runnables execute is the same as the
|
||||
// order in which they were originally dispatched.
|
||||
auto pending = std::move(mDelayedDebuggeeRunnables);
|
||||
for (uint32_t i = 0; i < pending.Length(); i++) {
|
||||
RefPtr<WorkerRunnable> runnable = std::move(pending[i]);
|
||||
Unused << NS_WARN_IF(
|
||||
NS_FAILED(DispatchLockHeld(runnable.forget(), nullptr, lock)));
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPrivate::SetIsQueued(const bool& aQueued) {
|
||||
AssertIsOnParentThread();
|
||||
mIsQueued = aQueued;
|
||||
}
|
||||
|
||||
bool WorkerPrivate::IsQueued() const {
|
||||
AssertIsOnParentThread();
|
||||
return mIsQueued;
|
||||
}
|
||||
|
||||
void WorkerPrivate::EnableRemoteDebugger() {
|
||||
AssertIsOnParentThread();
|
||||
|
||||
// XXX Skip for ChromeWorker now, this should be removed after Devtool codes
|
||||
// adapt to RemoteWorkerDebugger mechanism.
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for RemoteWorkerDebuggerChild binding done in the worker thread.
|
||||
mozilla::ipc::Endpoint<PRemoteWorkerDebuggerParent> parentEp;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mRemoteDebugger) {
|
||||
mDebuggerBindingCondVar.Wait();
|
||||
}
|
||||
// If Worker Thread never run the event loop, i.e. JSContext initilaization
|
||||
// fails, directly return for the cases. Because mRemoteDebugger is only
|
||||
// created after initialization successfully, but mDebuggerBindingCondVar
|
||||
// can get notified if the initialization fails.
|
||||
if (!mRemoteDebugger) {
|
||||
return;
|
||||
}
|
||||
parentEp = std::move(mDebuggerParentEp);
|
||||
}
|
||||
|
||||
// Call IPC for RemoteWorkerDebuggerParent binding and registration.
|
||||
RemoteWorkerDebuggerInfo info(
|
||||
mIsChromeWorker, mWorkerKind, mScriptURL, WindowID(),
|
||||
WrapNotNull(GetPrincipal()), IsServiceWorker() ? ServiceWorkerID() : 0,
|
||||
Id(), mWorkerName,
|
||||
GetParent() ? nsAutoString(GetParent()->Id()) : EmptyString());
|
||||
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(parentEp.IsValid());
|
||||
RemoteWorkerService::RegisterRemoteDebugger(std::move(info),
|
||||
std::move(parentEp));
|
||||
// Wait for register done
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mRemoteDebuggerRegistered) {
|
||||
mDebuggerBindingCondVar.Wait();
|
||||
}
|
||||
// Warning the case if the Worker shutdown before remote debugger
|
||||
// registration down.
|
||||
Unused << NS_WARN_IF(!mRemoteDebuggerRegistered);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPrivate::DisableRemoteDebugger() {
|
||||
AssertIsOnParentThread();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<DisableRemoteDebuggerRunnable> r =
|
||||
new DisableRemoteDebuggerRunnable(this);
|
||||
|
||||
if (r->Dispatch(this)) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mRemoteDebuggerRegistered) {
|
||||
mDebuggerBindingCondVar.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPrivate::DisableRemoteDebuggerOnWorkerThread(
|
||||
const bool& aForShutdown) {
|
||||
AssertIsOnWorkerThread();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
remoteDebugger = mRemoteDebugger;
|
||||
}
|
||||
if (remoteDebugger) {
|
||||
remoteDebugger->SendUnregister();
|
||||
}
|
||||
|
||||
// Now notify the parent thread if it is blocked by waiting
|
||||
// RemoteWorkerDebugger registration or unregsiteration.
|
||||
if (aForShutdown) {
|
||||
SetIsRemoteDebuggerRegistered(false);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult WorkerPrivate::DispatchControlRunnable(
|
||||
already_AddRefed<WorkerRunnable> aWorkerRunnable) {
|
||||
// May be called on any thread!
|
||||
@@ -1980,6 +2224,8 @@ bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DisableRemoteDebugger();
|
||||
|
||||
DisableDebugger();
|
||||
|
||||
RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
|
||||
@@ -2022,10 +2268,15 @@ bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) {
|
||||
}
|
||||
}
|
||||
|
||||
// Create remote debugger endpoints here for child binding in ThawRunnable;
|
||||
// CreateRemoteDebuggerEndpoints();
|
||||
RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
|
||||
bool rv = runnable->Dispatch(this);
|
||||
// EnableRemoteDebugger();
|
||||
|
||||
EnableDebugger();
|
||||
|
||||
RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
|
||||
return runnable->Dispatch(this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void WorkerPrivate::ParentWindowPaused() {
|
||||
@@ -2603,6 +2854,13 @@ WorkerPrivate::WorkerPrivate(
|
||||
mWorkerHybridEventTarget(
|
||||
new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)),
|
||||
mChildEp(std::move(aChildEp)),
|
||||
mRemoteDebuggerRegistered(false),
|
||||
mRemoteDebuggerReady(true),
|
||||
mIsQueued(false),
|
||||
mDebuggerBindingCondVar(mMutex,
|
||||
"WorkerPrivate RemoteDebuggerBindingCondVar"),
|
||||
mWorkerDebuggerEventTarget(new WorkerEventTarget(
|
||||
this, WorkerEventTarget::Behavior::DebuggerOnly)),
|
||||
mParentStatus(Pending),
|
||||
mStatus(Pending),
|
||||
mCreationTimeStamp(TimeStamp::Now()),
|
||||
@@ -2773,6 +3031,8 @@ WorkerPrivate::~WorkerPrivate() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mTopLevelWorkerFinishedRunnableCount == 0);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mWorkerFinishedRunnableCount == 0);
|
||||
|
||||
mWorkerDebuggerEventTarget->ForgetWorkerPrivate(this);
|
||||
|
||||
mWorkerControlEventTarget->ForgetWorkerPrivate(this);
|
||||
|
||||
// We force the hybrid event target to forget the thread when we
|
||||
@@ -2925,6 +3185,10 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
|
||||
|
||||
worker->mDefaultLocale = std::move(defaultLocale);
|
||||
|
||||
// Create remote debugger endpoint here for child binding in
|
||||
// WorkerThreadPrimaryRunnable
|
||||
worker->CreateRemoteDebuggerEndpoints();
|
||||
|
||||
if (!runtimeService->RegisterWorker(*worker)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
@@ -2936,6 +3200,13 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
|
||||
worker->mSelfRef = worker;
|
||||
worker->mParentRef = MakeRefPtr<WorkerParentRef>(worker);
|
||||
|
||||
// Enable remote worker debugger when the worker is really scheduled.
|
||||
/*
|
||||
if (!worker->mIsQueued) {
|
||||
worker->EnableRemoteDebugger();
|
||||
}
|
||||
*/
|
||||
|
||||
worker->EnableDebugger();
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
|
||||
@@ -3000,7 +3271,10 @@ nsresult WorkerPrivate::SetIsDebuggerReady(bool aReady) {
|
||||
|
||||
mDebuggerReady = aReady;
|
||||
|
||||
if (aReady && mDebuggerRegistered) {
|
||||
bool debuggerRegistered = mDebuggerRegistered && (mRemoteDebuggerRegistered ||
|
||||
XRE_IsParentProcess());
|
||||
|
||||
if (aReady && debuggerRegistered) {
|
||||
// Dispatch all the delayed runnables without releasing the lock, to ensure
|
||||
// that the order in which debuggee runnables execute is the same as the
|
||||
// order in which they were originally dispatched.
|
||||
@@ -3646,6 +3920,8 @@ void WorkerPrivate::DoRunLoop(JSContext* aCx) {
|
||||
// waiting for a next tick.
|
||||
PromiseDebugging::FlushUncaughtRejections();
|
||||
|
||||
// DisableRemoteDebuggerOnWorkerThread(true /*aForShutdown*/);
|
||||
|
||||
ShutdownGCTimers();
|
||||
|
||||
DisableMemoryReporter();
|
||||
@@ -4250,6 +4526,10 @@ void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Force to set mRemoteDebuggerRegistered as false and notify if the Worker is
|
||||
// waiting for the registration done.
|
||||
SetIsRemoteDebuggerRegistered(false);
|
||||
|
||||
if (WorkerPrivate* parent = GetParent()) {
|
||||
RefPtr<WorkerFinishedRunnable> runnable =
|
||||
new WorkerFinishedRunnable(parent, this);
|
||||
@@ -4506,6 +4786,8 @@ bool WorkerPrivate::ThawInternal() {
|
||||
auto data = mWorkerThreadAccessible.Access();
|
||||
NS_ASSERTION(data->mFrozen, "Not yet frozen!");
|
||||
|
||||
// BindRemoteWorkerDebuggerChild();
|
||||
|
||||
for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
|
||||
data->mChildWorkers[index]->Thaw(nullptr);
|
||||
}
|
||||
@@ -5426,6 +5708,16 @@ void WorkerPrivate::LeaveDebuggerEventLoop() {
|
||||
|
||||
void WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage) {
|
||||
mDebugger->PostMessageToDebugger(aMessage);
|
||||
RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mRemoteDebugger) {
|
||||
return;
|
||||
}
|
||||
remoteDebugger = mRemoteDebugger;
|
||||
}
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger);
|
||||
Unused << remoteDebugger->SendPostMessageToDebugger(nsAutoString(aMessage));
|
||||
}
|
||||
|
||||
void WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler,
|
||||
@@ -5443,6 +5735,18 @@ void WorkerPrivate::ReportErrorToDebugger(const nsACString& aFilename,
|
||||
uint32_t aLineno,
|
||||
const nsAString& aMessage) {
|
||||
mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
|
||||
RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mRemoteDebugger) {
|
||||
return;
|
||||
}
|
||||
remoteDebugger = mRemoteDebugger;
|
||||
}
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger);
|
||||
Unused << remoteDebugger->SendReportErrorToDebugger(
|
||||
RemoteWorkerDebuggerErrorInfo(nsAutoCString(aFilename), aLineno,
|
||||
nsAutoString(aMessage)));
|
||||
}
|
||||
|
||||
bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
|
||||
@@ -6473,9 +6777,7 @@ RefPtr<GenericPromise> WorkerPrivate::SetServiceWorkerSkipWaitingFlag() {
|
||||
return promise;
|
||||
}
|
||||
|
||||
const nsAString& WorkerPrivate::Id() {
|
||||
AssertIsOnParentThread();
|
||||
|
||||
const nsString& WorkerPrivate::Id() {
|
||||
if (mId.IsEmpty()) {
|
||||
mId = ComputeWorkerPrivateId();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,10 @@ namespace mozilla {
|
||||
class ThrottledEventQueue;
|
||||
namespace dom {
|
||||
|
||||
class PRemoteWorkerDebuggerChild;
|
||||
class PRemoteWorkerDebuggerParent;
|
||||
class RemoteWorkerChild;
|
||||
class RemoteWorkerDebuggerChild;
|
||||
class RemoteWorkerNonLifeCycleOpControllerChild;
|
||||
|
||||
// If you change this, the corresponding list in nsIWorkerDebugger.idl needs
|
||||
@@ -1061,6 +1064,24 @@ class WorkerPrivate final
|
||||
|
||||
void DisableDebugger();
|
||||
|
||||
void BindRemoteWorkerDebuggerChild();
|
||||
|
||||
void CreateRemoteDebuggerEndpoints();
|
||||
|
||||
void SetIsRemoteDebuggerRegistered(const bool& aRegistered);
|
||||
|
||||
void SetIsRemoteDebuggerReady(const bool& aReady);
|
||||
|
||||
void EnableRemoteDebugger();
|
||||
|
||||
void DisableRemoteDebugger();
|
||||
|
||||
void DisableRemoteDebuggerOnWorkerThread(const bool& aForShutdown = false);
|
||||
|
||||
void SetIsQueued(const bool& aQueued);
|
||||
|
||||
bool IsQueued() const;
|
||||
|
||||
already_AddRefed<WorkerRunnable> MaybeWrapAsWorkerRunnable(
|
||||
already_AddRefed<nsIRunnable> aRunnable);
|
||||
|
||||
@@ -1135,7 +1156,7 @@ class WorkerPrivate final
|
||||
|
||||
void StartCancelingTimer();
|
||||
|
||||
const nsAString& Id();
|
||||
const nsString& Id();
|
||||
|
||||
const nsID& AgentClusterId() const { return mAgentClusterId; }
|
||||
|
||||
@@ -1497,6 +1518,15 @@ class WorkerPrivate final
|
||||
|
||||
mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild> mChildEp;
|
||||
|
||||
RefPtr<RemoteWorkerDebuggerChild> mRemoteDebugger;
|
||||
mozilla::ipc::Endpoint<PRemoteWorkerDebuggerChild> mDebuggerChildEp;
|
||||
mozilla::ipc::Endpoint<PRemoteWorkerDebuggerParent> mDebuggerParentEp;
|
||||
bool mRemoteDebuggerRegistered MOZ_GUARDED_BY(mMutex);
|
||||
bool mRemoteDebuggerReady MOZ_GUARDED_BY(mMutex);
|
||||
bool mIsQueued; // Should only touched on parent thread.
|
||||
mozilla::CondVar mDebuggerBindingCondVar MOZ_GUARDED_BY(mMutex);
|
||||
RefPtr<WorkerEventTarget> mWorkerDebuggerEventTarget;
|
||||
|
||||
JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
|
||||
TimeStamp mKillTime;
|
||||
WorkerStatus mParentStatus MOZ_GUARDED_BY(mMutex);
|
||||
|
||||
@@ -278,7 +278,7 @@ class WorkerDebuggerRunnable : public WorkerThreadRunnable {
|
||||
private:
|
||||
virtual bool IsDebuggerRunnable() const override { return true; }
|
||||
|
||||
bool PreDispatch(WorkerPrivate* aWorkerPrivate) final {
|
||||
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -29,6 +29,8 @@ interface nsIWorkerDebugger : nsISupports
|
||||
|
||||
readonly attribute boolean isChrome;
|
||||
|
||||
readonly attribute boolean isRemote;
|
||||
|
||||
readonly attribute boolean isInitialized;
|
||||
|
||||
readonly attribute nsIWorkerDebugger parent;
|
||||
|
||||
@@ -24,6 +24,9 @@ parent:
|
||||
async RemoveWindowID(uint64_t aWindowID);
|
||||
|
||||
child:
|
||||
async RegisterDone();
|
||||
async UnregisterDone();
|
||||
|
||||
async Initialize(nsString aURL);
|
||||
async PostMessage(nsString aMessage);
|
||||
async SetDebuggerReady(bool aReady);
|
||||
|
||||
@@ -139,7 +139,8 @@ RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData)
|
||||
: mState(VariantType<remoteworker::Pending>(), "RemoteWorkerState"),
|
||||
mServiceKeepAlive(RemoteWorkerService::MaybeGetKeepAlive()),
|
||||
mIsServiceWorker(aData.serviceWorkerData().type() ==
|
||||
OptionalServiceWorkerData::TServiceWorkerData) {
|
||||
OptionalServiceWorkerData::TServiceWorkerData),
|
||||
mPendingOps("PendingRemoteWorkerOps") {
|
||||
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
|
||||
}
|
||||
|
||||
@@ -813,9 +814,34 @@ void RemoteWorkerChild::CancelAllPendingOps(RemoteWorkerState& aState) {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteWorkerChild::PendRemoteWorkerOp(RefPtr<RemoteWorkerOp> aOp) {
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(mIsThawing);
|
||||
|
||||
auto pendingOps = mPendingOps.Lock();
|
||||
|
||||
pendingOps->AppendElement(std::move(aOp));
|
||||
}
|
||||
|
||||
void RemoteWorkerChild::RunAllPendingOpsOnMainThread() {
|
||||
RefPtr<RemoteWorkerChild> self = this;
|
||||
|
||||
auto pendingOps = mPendingOps.Lock();
|
||||
|
||||
for (auto& op : *pendingOps) {
|
||||
op->StartOnMainThread(self);
|
||||
}
|
||||
|
||||
pendingOps->Clear();
|
||||
}
|
||||
|
||||
void RemoteWorkerChild::MaybeStartOp(RefPtr<RemoteWorkerOp>&& aOp) {
|
||||
MOZ_ASSERT(aOp);
|
||||
|
||||
if (mIsThawing) {
|
||||
PendRemoteWorkerOp(std::move(aOp));
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = mState.Lock();
|
||||
|
||||
if (!aOp->MaybeStart(this, lock.ref())) {
|
||||
|
||||
@@ -84,6 +84,11 @@ class RemoteWorkerChild final : public PRemoteWorkerChild {
|
||||
|
||||
const nsTArray<uint64_t>& WindowIDs() const { return mWindowIDs; }
|
||||
|
||||
void SetIsThawing(const bool aIsThawing) { mIsThawing = aIsThawing; }
|
||||
bool IsThawing() const { return mIsThawing; }
|
||||
void PendRemoteWorkerOp(RefPtr<RemoteWorkerOp> aOp);
|
||||
void RunAllPendingOpsOnMainThread();
|
||||
|
||||
private:
|
||||
class InitializeWorkerRunnable;
|
||||
|
||||
@@ -173,6 +178,23 @@ class RemoteWorkerChild final : public PRemoteWorkerChild {
|
||||
};
|
||||
|
||||
ThreadBound<LauncherBoundData> mLauncherData;
|
||||
|
||||
// Thaw operation holds mState.lock. It means other operations will be blocked
|
||||
// until mState.lock is released. However, Thaw operation is blocked by
|
||||
// RemoteWorkerDebugger registration that needs WorkerLauncher thread to send
|
||||
// IPC to continue the registration on the parent process. If a RemoteWorkerOp
|
||||
// is received on WorkerLauncher thread when the RemoteWorker is thawing, a
|
||||
// deadlock could be happen between WorkerLauncher thread and RemoteWorker's
|
||||
// parent thread. So mIsThawing and mPendingOps are introduced to avoid the
|
||||
// deadlock by pending the operations when RemoteWorker is thawing.
|
||||
//
|
||||
// Note that these could be removed once RemoteWorkerChild off-main-thread
|
||||
// done since the RemoteWorker's parent thread will be WorkerLauncher thread.
|
||||
// And it means when executing WorkerPrivate::Thaw on WorkerLauncher thread,
|
||||
// it is impossible to handle the IPC callback on WorkerLauncher thread at the
|
||||
// same time.
|
||||
Atomic<bool> mIsThawing{false};
|
||||
DataMutex<nsTArray<RefPtr<RemoteWorkerOp>>> mPendingOps;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
@@ -3,38 +3,177 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RemoteWorkerDebuggerChild.h"
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
#include "mozilla/dom/MessageEventBinding.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/workerinternals/ScriptLoader.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
namespace {
|
||||
|
||||
class RemoteDebuggerMessageEventRunnable final : public WorkerDebuggerRunnable {
|
||||
nsString mMessage;
|
||||
|
||||
public:
|
||||
explicit RemoteDebuggerMessageEventRunnable(const nsAString& aMessage)
|
||||
: WorkerDebuggerRunnable("RemoteDebuggerMessageEventRunnable"),
|
||||
mMessage(aMessage) {}
|
||||
|
||||
private:
|
||||
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) override {}
|
||||
|
||||
virtual bool WorkerRun(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate) override {
|
||||
WorkerDebuggerGlobalScope* globalScope =
|
||||
aWorkerPrivate->DebuggerGlobalScope();
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
JS::Rooted<JSString*> message(
|
||||
aCx, JS_NewUCStringCopyN(aCx, mMessage.get(), mMessage.Length()));
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
|
||||
|
||||
RefPtr<MessageEvent> event =
|
||||
new MessageEvent(globalScope, nullptr, nullptr);
|
||||
event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
|
||||
Cancelable::eYes, data, u""_ns, u""_ns, nullptr,
|
||||
Sequence<OwningNonNull<MessagePort>>());
|
||||
event->SetTrusted(true);
|
||||
|
||||
globalScope->DispatchEvent(*event);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CompileRemoteDebuggerScriptRunnable final
|
||||
: public WorkerDebuggerRunnable {
|
||||
nsString mScriptURL;
|
||||
const mozilla::Encoding* mDocumentEncoding;
|
||||
|
||||
public:
|
||||
CompileRemoteDebuggerScriptRunnable(
|
||||
WorkerPrivate* aWorkerPrivate, const nsAString& aScriptURL,
|
||||
const mozilla::Encoding* aDocumentEncoding)
|
||||
: WorkerDebuggerRunnable("CompileDebuggerScriptRunnable"),
|
||||
mScriptURL(aScriptURL),
|
||||
mDocumentEncoding(aDocumentEncoding) {}
|
||||
|
||||
private:
|
||||
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) override {}
|
||||
|
||||
virtual bool WorkerRun(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate) override {
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
WorkerDebuggerGlobalScope* globalScope =
|
||||
aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
|
||||
if (!globalScope) {
|
||||
NS_WARNING("Failed to make global!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
|
||||
|
||||
ErrorResult rv;
|
||||
JSAutoRealm ar(aCx, global);
|
||||
workerinternals::LoadMainScript(aWorkerPrivate, nullptr, mScriptURL,
|
||||
DebuggerScript, rv, mDocumentEncoding);
|
||||
rv.WouldReportJSException();
|
||||
// Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
|
||||
// return false and don't SetWorkerScriptExecutedSuccessfully() in that
|
||||
// case, but don't throw anything on aCx. The idea is to not dispatch error
|
||||
// events if our load is canceled with that error code.
|
||||
if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
|
||||
rv.SuppressException();
|
||||
return false;
|
||||
}
|
||||
// Make sure to propagate exceptions from rv onto aCx, so that they will get
|
||||
// reported after we return. We do this for all failures on rv, because now
|
||||
// we're using rv to track all the state we care about.
|
||||
if (rv.MaybeSetPendingException(aCx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
RemoteWorkerDebuggerChild::RemoteWorkerDebuggerChild(
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate) {
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
WorkerPrivate* aWorkerPrivate) {
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
RemoteWorkerDebuggerChild::~RemoteWorkerDebuggerChild() {
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(!mWorkerPrivate);
|
||||
RemoteWorkerDebuggerChild::~RemoteWorkerDebuggerChild() {}
|
||||
|
||||
mozilla::ipc::IPCResult RemoteWorkerDebuggerChild::RecvRegisterDone() {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
|
||||
|
||||
workerPrivate->SetIsRemoteDebuggerRegistered(true);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult RemoteWorkerDebuggerChild::RecvUnregisterDone() {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
|
||||
|
||||
workerPrivate->SetIsRemoteDebuggerRegistered(false);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult RemoteWorkerDebuggerChild::RecvInitialize(
|
||||
const nsString& aURL) {
|
||||
// Send a WorkerDebuggerRunnable to compile debugger script.
|
||||
Unused << SendSetAsInitialized();
|
||||
if (!mIsInitialized) {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
|
||||
RefPtr<CompileRemoteDebuggerScriptRunnable> runnable =
|
||||
new CompileRemoteDebuggerScriptRunnable(workerPrivate, aURL, nullptr);
|
||||
Unused << NS_WARN_IF(!runnable->Dispatch(workerPrivate));
|
||||
Unused << SendSetAsInitialized();
|
||||
}
|
||||
mIsInitialized = true;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult RemoteWorkerDebuggerChild::RecvPostMessage(
|
||||
const nsString& aMessage) {
|
||||
// Send a WorkerDebuggerRunnable to fire a message event on DebuggerGlobal.
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
|
||||
RefPtr<RemoteDebuggerMessageEventRunnable> runnable =
|
||||
new RemoteDebuggerMessageEventRunnable(aMessage);
|
||||
Unused << NS_WARN_IF(!runnable->Dispatch(workerPrivate));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult RemoteWorkerDebuggerChild::RecvSetDebuggerReady(
|
||||
const bool& aReady) {
|
||||
// Should call mWorkerPrivate->SetIsDebuggerReady();
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
|
||||
workerPrivate->SetIsRemoteDebuggerReady(aReady);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,16 @@ class WorkerPrivate;
|
||||
|
||||
class RemoteWorkerDebuggerChild final : public PRemoteWorkerDebuggerChild {
|
||||
friend class PRemoteWorkerDebuggerChild;
|
||||
friend class WorkerPrivate;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteWorkerDebuggerChild, final)
|
||||
|
||||
explicit RemoteWorkerDebuggerChild(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRegisterDone();
|
||||
mozilla::ipc::IPCResult RecvUnregisterDone();
|
||||
|
||||
mozilla::ipc::IPCResult RecvInitialize(const nsString& aURL);
|
||||
mozilla::ipc::IPCResult RecvPostMessage(const nsString& aMessage);
|
||||
mozilla::ipc::IPCResult RecvSetDebuggerReady(const bool& aReady);
|
||||
@@ -29,7 +33,7 @@ class RemoteWorkerDebuggerChild final : public PRemoteWorkerDebuggerChild {
|
||||
private:
|
||||
~RemoteWorkerDebuggerChild();
|
||||
|
||||
RefPtr<WorkerPrivate> mWorkerPrivate;
|
||||
bool mIsInitialized{false};
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
@@ -13,10 +13,6 @@ RemoteWorkerDebuggerManagerChild::RemoteWorkerDebuggerManagerChild() {
|
||||
RemoteWorkerService::Thread()->IsOnCurrentThread());
|
||||
}
|
||||
|
||||
RemoteWorkerDebuggerManagerChild::~RemoteWorkerDebuggerManagerChild() {
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(
|
||||
RemoteWorkerService::Thread() &&
|
||||
RemoteWorkerService::Thread()->IsOnCurrentThread());
|
||||
}
|
||||
RemoteWorkerDebuggerManagerChild::~RemoteWorkerDebuggerManagerChild() {}
|
||||
|
||||
} // end of namespace mozilla::dom
|
||||
|
||||
@@ -40,18 +40,18 @@ mozilla::ipc::IPCResult RemoteWorkerDebuggerManagerParent::RecvRegister(
|
||||
const RemoteWorkerDebuggerInfo& aDebuggerInfo,
|
||||
mozilla::ipc::Endpoint<PRemoteWorkerDebuggerParent>&& aParentEp) {
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
|
||||
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::Get();
|
||||
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(manager);
|
||||
|
||||
nsCOMPtr<nsIWorkerDebugger> debugger =
|
||||
manager->GetDebuggerById(aDebuggerInfo.Id());
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(!debugger);
|
||||
|
||||
debugger = MakeRefPtr<RemoteWorkerDebuggerParent>(aDebuggerInfo,
|
||||
std::move(aParentEp));
|
||||
RefPtr<RemoteWorkerDebuggerParent> debugger =
|
||||
MakeRefPtr<RemoteWorkerDebuggerParent>(aDebuggerInfo,
|
||||
std::move(aParentEp));
|
||||
|
||||
manager->RegisterDebugger(debugger);
|
||||
|
||||
MOZ_ASSERT(debugger->CanSend());
|
||||
Unused << debugger->SendRegisterDone();
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "RemoteWorkerDebuggerParent.h"
|
||||
#include "mozilla/dom/WorkerDebuggerManager.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
@@ -35,6 +36,10 @@ mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvUnregister() {
|
||||
listener->OnClose();
|
||||
}
|
||||
|
||||
if (CanSend()) {
|
||||
Unused << SendUnregisterDone();
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
@@ -105,6 +110,15 @@ RemoteWorkerDebuggerParent::GetIsChrome(bool* aResult) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemoteWorkerDebuggerParent::GetIsRemote(bool* aResult) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
|
||||
|
||||
*aResult = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemoteWorkerDebuggerParent::GetIsInitialized(bool* aResult) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@@ -190,12 +190,26 @@ void RemoteWorkerService::RegisterRemoteDebugger(
|
||||
StaticMutexAutoLock lock(sRemoteWorkerServiceMutex);
|
||||
MOZ_ASSERT(sRemoteWorkerService);
|
||||
MOZ_ASSERT(sRemoteWorkerService->mThread);
|
||||
|
||||
// If we are on WorkerLauncher thread, direcly call
|
||||
// RemoteWorkerDebuggerManager::SendRegister.
|
||||
if (sRemoteWorkerService->mThread->IsOnCurrentThread()) {
|
||||
MOZ_ASSERT(sRemoteWorkerService->mDebuggerManagerActor);
|
||||
Unused << sRemoteWorkerService->mDebuggerManagerActor->SendRegister(
|
||||
MOZ_ASSERT(sRemoteWorkerService->mDebuggerManagerChild);
|
||||
Unused << sRemoteWorkerService->mDebuggerManagerChild->SendRegister(
|
||||
std::move(aDebuggerInfo), std::move(aDebuggerParentEp));
|
||||
return;
|
||||
}
|
||||
|
||||
// For top-level workers in parent process, directly call RecvRegister().
|
||||
if (XRE_IsParentProcess() && NS_IsMainThread()) {
|
||||
MOZ_ASSERT(sRemoteWorkerService->mDebuggerManagerParent);
|
||||
Unused << sRemoteWorkerService->mDebuggerManagerParent->RecvRegister(
|
||||
std::move(aDebuggerInfo), std::move(aDebuggerParentEp));
|
||||
return;
|
||||
}
|
||||
|
||||
// We are on other thread in the case of this is a Child worker. Dispatch this
|
||||
// method to WorkerLauncher thread.
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"RemoteWorkerService::RegisterRemoteDebugger",
|
||||
[debuggerInfo = std::move(aDebuggerInfo),
|
||||
@@ -263,9 +277,9 @@ nsresult RemoteWorkerService::InitializeOnMainThread(
|
||||
debuggerChildEp = std::move(aDebuggerChildEp)]() mutable {
|
||||
self->InitializeOnTargetThread(std::move(endpoint));
|
||||
|
||||
self->mDebuggerManagerActor =
|
||||
self->mDebuggerManagerChild =
|
||||
MakeRefPtr<RemoteWorkerDebuggerManagerChild>();
|
||||
debuggerChildEp.Bind(self->mDebuggerManagerActor);
|
||||
debuggerChildEp.Bind(self->mDebuggerManagerChild);
|
||||
});
|
||||
|
||||
rv = mThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
@@ -353,9 +367,9 @@ RemoteWorkerService::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
NS_ENSURE_TRUE(parentActor, NS_ERROR_FAILURE);
|
||||
|
||||
Endpoint<PRemoteWorkerDebuggerManagerChild> debuggerChildEp;
|
||||
RefPtr<RemoteWorkerDebuggerManagerParent> debuggerParentActor =
|
||||
mDebuggerManagerParent =
|
||||
RemoteWorkerDebuggerManagerParent::CreateForProcess(&debuggerChildEp);
|
||||
NS_ENSURE_TRUE(debuggerParentActor, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(mDebuggerManagerParent, NS_ERROR_FAILURE);
|
||||
|
||||
return InitializeOnMainThread(std::move(childEp), std::move(debuggerChildEp));
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/dom/RemoteWorkerTypes.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class RemoteWorkerDebuggerManagerChild;
|
||||
class RemoteWorkerDebuggerManagerParent;
|
||||
class RemoteWorkerService;
|
||||
class RemoteWorkerServiceChild;
|
||||
class RemoteWorkerServiceShutdownBlocker;
|
||||
@@ -123,7 +125,8 @@ class RemoteWorkerService final : public nsIObserver {
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
RefPtr<RemoteWorkerServiceChild> mActor;
|
||||
RefPtr<RemoteWorkerDebuggerManagerChild> mDebuggerManagerActor;
|
||||
RefPtr<RemoteWorkerDebuggerManagerChild> mDebuggerManagerChild;
|
||||
RefPtr<RemoteWorkerDebuggerManagerParent> mDebuggerManagerParent;
|
||||
// The keep-alive is set and cleared on the main thread but we will hand out
|
||||
// additional references to it from the "Worker Launcher" thread, so it's
|
||||
// appropriate to use a mutex. (Alternately we could have used a ThreadBound
|
||||
|
||||
@@ -82,6 +82,10 @@ bool SharedWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
|
||||
|
||||
MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
|
||||
|
||||
if (mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerThawOpArgs) {
|
||||
aOwner->SetIsThawing(true);
|
||||
}
|
||||
|
||||
RefPtr<SharedWorkerOp> self = this;
|
||||
RefPtr<RemoteWorkerChild> owner = aOwner;
|
||||
|
||||
@@ -97,6 +101,11 @@ bool SharedWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
|
||||
}
|
||||
|
||||
self->StartOnMainThread(owner);
|
||||
if (self->mOpArgs.type() ==
|
||||
SharedWorkerOpArgs::TSharedWorkerThawOpArgs) {
|
||||
owner->SetIsThawing(false);
|
||||
owner->RunAllPendingOpsOnMainThread();
|
||||
}
|
||||
});
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
|
||||
@@ -151,6 +160,12 @@ void SharedWorkerOp::StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) {
|
||||
} else {
|
||||
MOZ_CRASH("Unknown SharedWorkerOpArgs type!");
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!mStarted) {
|
||||
mStarted = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SharedWorkerOp::Start(RemoteWorkerNonLifeCycleOpControllerChild* aOwner,
|
||||
|
||||
Reference in New Issue
Block a user