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
330 lines
8.2 KiB
C++
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
|