Files
sousa-gecko/dom/workers/remoteworkers/RemoteWorkerDebuggerParent.cpp
T
Eden Chuang 7f2f7985dc Bug 1944240 - Expose parent-process remote worker debuggers to nsIWorkerDebuggerManager consumers. r=asuth,dom-worker-reviewers
Notify nsIWorkerDebuggerManagerListener when a RemoteWorkerDebugger registers or
unregisters in the parent process, and stop filtering remote debuggers out of
the worker debugger enumerator, so DevTools (and other consumers) can observe
workers running in content processes. RemoteWorkerDebuggerParent::GetWindow now
returns null instead of throwing, since a remote worker has no window in the
parent process; consumers use windowIDs instead.

Tighten the RemoteWorkerDebuggerParent lifecycle to match the local
WorkerDebugger: RecvUnregister marks the actor closed before notifying onClose
listeners, and the nsIWorkerDebugger data getters, Initialize and PostMessageMoz
throw NS_ERROR_UNEXPECTED once the actor is closed rather than touching a
torn-down actor.

Differential Revision: https://phabricator.services.mozilla.com/D306649
2026-07-07 20:20:05 +00:00

330 lines
8.2 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RemoteWorkerDebuggerParent.h"
#include "mozilla/dom/WorkerDebuggerManager.h"
#include "mozilla/dom/WorkerPrivate.h"
namespace mozilla::dom {
RemoteWorkerDebuggerParent::RemoteWorkerDebuggerParent(
const RemoteWorkerDebuggerInfo& aWorkerDebuggerInfo,
Endpoint<PRemoteWorkerDebuggerParent>&& aParentEp)
: mWorkerDebuggerInfo(std::move(aWorkerDebuggerInfo)) {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
MOZ_ASSERT_DEBUG_OR_FUZZING(aParentEp.IsValid());
aParentEp.Bind(this);
if (mWorkerDebuggerInfo.type() == WorkerKindDedicated) {
mWindowIDs.AppendElement(mWorkerDebuggerInfo.windowID());
}
}
RemoteWorkerDebuggerParent::~RemoteWorkerDebuggerParent() {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
}
// PRemoteWorkerDebugger IPC interface
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvUnregister() {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::Get();
MOZ_ASSERT_DEBUG_OR_FUZZING(manager);
manager->UnregisterDebugger(this);
// Unregistering means the worker is gone, so the debugger is now closed.
// This mirrors the local WorkerDebugger, whose Close() (called on unregister)
// makes nsIWorkerDebugger.isClosed return true and property accesses throw.
mIsClosed = true;
for (const auto& listener : mListeners.Clone()) {
listener->OnClose();
}
if (CanSend()) {
(void)SendUnregisterDone();
}
return IPC_OK();
}
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvReportErrorToDebugger(
const RemoteWorkerDebuggerErrorInfo& aErrorInfo) {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
for (const auto& listener : mListeners.Clone()) {
listener->OnError(aErrorInfo.fileName(), aErrorInfo.lineNo(),
aErrorInfo.message());
}
return IPC_OK();
}
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvPostMessageToDebugger(
const nsString& aMessage) {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
for (const auto& listener : mListeners.Clone()) {
listener->OnMessage(aMessage);
}
return IPC_OK();
}
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvSetAsInitialized() {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
mIsInitialized = true;
return IPC_OK();
}
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvSetAsClosed() {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
mIsClosed = true;
return IPC_OK();
}
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvAddWindowID(
const uint64_t& aWindowID) {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
mWindowIDs.AppendElement(aWindowID);
return IPC_OK();
}
mozilla::ipc::IPCResult RemoteWorkerDebuggerParent::RecvRemoveWindowID(
const uint64_t& aWindowID) {
MOZ_ASSERT_DEBUG_OR_FUZZING(XRE_IsParentProcess() && NS_IsMainThread());
mWindowIDs.RemoveElement(aWindowID);
return IPC_OK();
}
// nsIWorkerDebugger interface
NS_IMPL_ISUPPORTS(RemoteWorkerDebuggerParent, nsIWorkerDebugger)
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetIsClosed(bool* aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
*aResult = mIsClosed;
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetIsChrome(bool* aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
*aResult = mWorkerDebuggerInfo.isChrome();
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();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
*aResult = mIsInitialized;
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetParent(nsIWorkerDebugger** aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIWorkerDebugger> parent;
if (!mWorkerDebuggerInfo.parentId().IsEmpty()) {
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::Get();
MOZ_ASSERT_DEBUG_OR_FUZZING(manager);
parent = manager->GetDebuggerById(mWorkerDebuggerInfo.parentId());
}
parent.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetType(uint32_t* aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
*aResult = mWorkerDebuggerInfo.type();
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetUrl(nsAString& aResult) {
AssertIsOnMainThread();
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
aResult = mWorkerDebuggerInfo.url();
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetWindow(mozIDOMWindow** aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
// A remote worker runs in a content process, so there is no associated
// window object in this (parent) process. Consumers that need the owning
// document should use the inner window id(s) exposed through windowIDs
// instead. Return null rather than throwing so that `debugger.window`
// behaves like an absent optional value.
*aResult = nullptr;
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetWindowIDs(nsTArray<uint64_t>& aResult) {
AssertIsOnMainThread();
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
aResult = mWindowIDs.Clone();
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetPrincipal(nsIPrincipal** aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIPrincipal> principal = mWorkerDebuggerInfo.principal();
principal.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetServiceWorkerID(uint32_t* aResult) {
AssertIsOnMainThread();
MOZ_ASSERT_DEBUG_OR_FUZZING(aResult);
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
*aResult = mWorkerDebuggerInfo.serviceWorkerID();
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetId(nsAString& aResult) {
AssertIsOnMainThread();
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
aResult = mWorkerDebuggerInfo.Id();
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::GetName(nsAString& aResult) {
AssertIsOnMainThread();
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
aResult = mWorkerDebuggerInfo.name();
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::Initialize(const nsAString& aURL) {
AssertIsOnMainThread();
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
if (CanSend()) {
nsAutoString url(aURL);
(void)SendInitialize(url);
}
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::PostMessageMoz(const nsAString& aMessage) {
AssertIsOnMainThread();
if (mIsClosed) {
return NS_ERROR_UNEXPECTED;
}
if (CanSend()) {
nsAutoString message(aMessage);
(void)SendPostMessage(message);
}
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::AddListener(nsIWorkerDebuggerListener* aListener) {
AssertIsOnMainThread();
if (mListeners.Contains(aListener)) {
return NS_ERROR_INVALID_ARG;
}
mListeners.AppendElement(aListener);
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::RemoveListener(
nsIWorkerDebuggerListener* aListener) {
AssertIsOnMainThread();
if (!mListeners.Contains(aListener)) {
return NS_ERROR_INVALID_ARG;
}
mListeners.RemoveElement(aListener);
return NS_OK;
}
NS_IMETHODIMP
RemoteWorkerDebuggerParent::SetDebuggerReady(bool aReady) {
AssertIsOnMainThread();
if (CanSend()) {
(void)SendSetDebuggerReady(aReady);
}
return NS_OK;
}
} // namespace mozilla::dom