commit 18a576b4f53928e46cc2efe71091ad65432f4d56
parent d47d53b6f23c510e9c0a7219fe4b99f9e063644a
Author: Hubert Boma Manilla <hmanilla@mozilla.com>
Date: Wed, 17 Dec 2025 14:11:37 +0000
Bug 1999240 - Add API's for monitoring Webtransport sessions creations and destruction r=necko-reviewers,kershaw,jesup
Differential Revision: https://phabricator.services.mozilla.com/D265607
Diffstat:
11 files changed, 220 insertions(+), 10 deletions(-)
diff --git a/dom/webtransport/api/WebTransport.cpp b/dom/webtransport/api/WebTransport.cpp
@@ -264,6 +264,11 @@ void WebTransport::Init(const GlobalObject& aGlobal, const nsAString& aURL,
return;
}
+ // TODO: Fix the shared worker case
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ mService = workerPrivate && workerPrivate->IsSharedWorker()
+ ? nullptr
+ : net::WebTransportEventService::GetOrCreate();
// XXX TODO
// Step 15 Let transport be a newly constructed WebTransport object, with:
@@ -414,6 +419,20 @@ void WebTransport::ResolveWaitingConnection(
// Step 17.3: Set transport.[[Session]] to session.
// Step 17.4: Set transport’s [[Reliability]] to "supports-unreliable".
mReliability = aReliability;
+ if (NS_IsMainThread()) {
+ nsPIDOMWindowInner* innerWindow = GetParentObject()->GetAsInnerWindow();
+ if (innerWindow) {
+ mInnerWindowID = innerWindow->WindowID();
+ }
+ } else {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ if (workerPrivate->IsDedicatedWorker()) {
+ nsPIDOMWindowInner* window = workerPrivate->GetAncestorWindow();
+ if (window) {
+ mInnerWindowID = window->WindowID();
+ }
+ }
+ }
mChild->SendGetMaxDatagramSize()->Then(
GetCurrentSerialEventTarget(), __func__,
@@ -432,6 +451,21 @@ void WebTransport::ResolveWaitingConnection(
// We can now release any queued datagrams
mDatagrams->SetChild(mChild);
+
+ if (mInnerWindowID != 0) {
+ // Get the http chanel created for the web transport session;
+ mChild->SendGetHttpChannelID()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this}](uint64_t&& aHttpChannelId) {
+ MOZ_ASSERT(self->mService);
+ self->mHttpChannelID = aHttpChannelId;
+ self->mService->WebTransportSessionCreated(self->mInnerWindowID,
+ aHttpChannelId);
+ },
+ [](const mozilla::ipc::ResponseRejectReason& aReason) {
+ LOG(("WebTransport fetching the channel information failed "));
+ });
+ }
}
void WebTransport::RejectWaitingConnection(nsresult aRv) {
@@ -839,6 +873,13 @@ void WebTransport::Cleanup(WebTransportError* aError,
// Otherwise, set transport.[[State]] to "failed".
mState = aCloseInfo ? WebTransportState::CLOSED : WebTransportState::FAILED;
+ // Notify all the listeners of the closed session
+ if (mInnerWindowID != 0) {
+ mService->WebTransportSessionClosed(
+ mInnerWindowID, mHttpChannelID, aCloseInfo->mCloseCode,
+ NS_ConvertUTF8toUTF16(aCloseInfo->mReason));
+ }
+
// Step 10: For each sendStream in sendStreams, error sendStream with error.
AutoJSAPI jsapi;
if (!jsapi.Init(mGlobal)) {
diff --git a/dom/webtransport/api/WebTransport.h b/dom/webtransport/api/WebTransport.h
@@ -14,6 +14,7 @@
#include "mozilla/dom/WebTransportSendStream.h"
#include "mozilla/dom/WebTransportStreams.h"
#include "mozilla/ipc/DataPipe.h"
+#include "mozilla/net/WebTransportEventService.h"
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "nsPIDOMWindow.h"
@@ -149,6 +150,9 @@ class WebTransport final : public nsISupports, public nsWrapperCache {
WebTransportState mState;
RefPtr<Promise> mReady;
+ uint64_t mInnerWindowID = 0;
+ uint64_t mHttpChannelID = 0;
+ RefPtr<mozilla::net::WebTransportEventService> mService;
// XXX may not need to be a RefPtr, since we own it through the Streams
RefPtr<WebTransportIncomingStreamsAlgorithms> mIncomingBidirectionalAlgorithm;
RefPtr<WebTransportIncomingStreamsAlgorithms>
diff --git a/dom/webtransport/parent/WebTransportParent.cpp b/dom/webtransport/parent/WebTransportParent.cpp
@@ -725,6 +725,16 @@ WebTransportParent::OnIncomingBidirectionalStreamAvailable(
return IPC_OK();
}
+::mozilla::ipc::IPCResult WebTransportParent::RecvGetHttpChannelID(
+ GetHttpChannelIDResolver&& aResolver) {
+ LOG(("WebTransportParent Channel ID"));
+ MOZ_ASSERT(mWebTransport);
+ uint64_t aHttpChannelId;
+ mWebTransport->GetHttpChannelID(&aHttpChannelId);
+ aResolver(aHttpChannelId);
+ return IPC_OK();
+}
+
// The promise sent in this request will be resolved
// in OnOutgoingDatagramOutCome which is called synchronously from
// WebTransportSessionProxy::SendDatagram
diff --git a/dom/webtransport/parent/WebTransportParent.h b/dom/webtransport/parent/WebTransportParent.h
@@ -59,6 +59,9 @@ class WebTransportParent : public PWebTransportParent,
::mozilla::ipc::IPCResult RecvGetMaxDatagramSize(
GetMaxDatagramSizeResolver&& aResolver);
+ ::mozilla::ipc::IPCResult RecvGetHttpChannelID(
+ GetHttpChannelIDResolver&& aResolver);
+
void ActorDestroy(ActorDestroyReason aWhy) override;
class OnResetOrStopSendingCallback final {
diff --git a/dom/webtransport/shared/PWebTransport.ipdl b/dom/webtransport/shared/PWebTransport.ipdl
@@ -73,6 +73,12 @@ async protocol PWebTransport
returns(uint64_t maxDatagramSize);
/**
+ * Get the http channel id
+ */
+ async GetHttpChannelID()
+ returns(uint64_t httpChannelID);
+
+ /**
* Set the sendOrder for an existing stream
*/
async SetSendOrder(uint64_t streamId, int64_t? sendOrder);
diff --git a/netwerk/protocol/webtransport/WebTransportEventService.cpp b/netwerk/protocol/webtransport/WebTransportEventService.cpp
@@ -10,6 +10,7 @@
#include "mozilla/StaticPtr.h"
#include "mozilla/Services.h"
#include "nsISupportsPrimitives.h"
+#include "mozilla/StaticMutex.h"
namespace mozilla {
namespace net {
@@ -17,13 +18,84 @@ namespace net {
namespace {
StaticRefPtr<WebTransportEventService> gWebTransportEventService;
+static StaticMutex sLock;
} // anonymous namespace
+class WebTransportBaseRunnable : public Runnable {
+ public:
+ WebTransportBaseRunnable(uint64_t aInnerWindowID, uint64_t aHttpChannelId)
+ : Runnable("net::WebTransportBaseRunnable"),
+ mInnerWindowID(aInnerWindowID),
+ mHttpChannelId(aHttpChannelId),
+ mService(WebTransportEventService::GetOrCreate()) {}
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mService);
+
+ WebTransportEventService::WebTransportEventListeners listeners;
+ mService->GetListeners(mInnerWindowID, listeners);
+
+ for (uint32_t i = 0; i < listeners.Length(); ++i) {
+ DoWork(listeners[i]);
+ }
+
+ return NS_OK;
+ }
+
+ protected:
+ ~WebTransportBaseRunnable() = default;
+
+ virtual void DoWork(nsIWebTransportEventListener* aListener) = 0;
+
+ uint64_t mInnerWindowID;
+ uint64_t mHttpChannelId;
+ RefPtr<WebTransportEventService> mService;
+};
+
+class WebTransportSessionCreatedRunnable final
+ : public WebTransportBaseRunnable {
+ public:
+ WebTransportSessionCreatedRunnable(uint64_t aInnerWindowID,
+ uint64_t aHttpChannelId)
+ : WebTransportBaseRunnable(aInnerWindowID, aHttpChannelId) {}
+
+ private:
+ virtual void DoWork(nsIWebTransportEventListener* aListener) override {
+ DebugOnly<nsresult> rv =
+ aListener->WebTransportSessionCreated(mHttpChannelId);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "WebTransport Session Created failed");
+ }
+};
+
+class WebTransportSessionClosedRunnable final
+ : public WebTransportBaseRunnable {
+ public:
+ WebTransportSessionClosedRunnable(uint64_t aInnerWindowID,
+ uint64_t aHttpChannelId, uint32_t aCode,
+ const nsAString& aReason)
+ : WebTransportBaseRunnable(aInnerWindowID, aHttpChannelId),
+ mCode(aCode),
+ mReason(aReason) {}
+
+ private:
+ virtual void DoWork(nsIWebTransportEventListener* aListener) override {
+ DebugOnly<nsresult> rv =
+ aListener->WebTransportSessionClosed(mHttpChannelId, mCode, mReason);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "WebTransport Session Closed failed");
+ }
+
+ uint32_t mCode;
+ const nsString mReason;
+};
+
/* static */
already_AddRefed<WebTransportEventService>
WebTransportEventService::GetOrCreate() {
- MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sLock);
if (!gWebTransportEventService) {
gWebTransportEventService = new WebTransportEventService();
@@ -43,8 +115,6 @@ NS_IMPL_ADDREF(WebTransportEventService)
NS_IMPL_RELEASE(WebTransportEventService)
WebTransportEventService::WebTransportEventService() : mCountListeners(0) {
- MOZ_ASSERT(NS_IsMainThread());
-
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "xpcom-shutdown", false);
@@ -52,6 +122,36 @@ WebTransportEventService::WebTransportEventService() : mCountListeners(0) {
}
}
+void WebTransportEventService::WebTransportSessionCreated(
+ uint64_t aInnerWindowID, uint64_t aHttpChannelId) {
+ // Let's continue only if we have some listeners.
+ if (!HasListeners()) {
+ return;
+ }
+
+ RefPtr<WebTransportSessionCreatedRunnable> runnable =
+ new WebTransportSessionCreatedRunnable(aInnerWindowID, aHttpChannelId);
+ DebugOnly<nsresult> rv =
+ NS_IsMainThread() ? runnable->Run() : NS_DispatchToMainThread(runnable);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
+}
+
+void WebTransportEventService::WebTransportSessionClosed(
+ uint64_t aInnerWindowID, uint64_t aHttpChannelId, uint32_t aCode,
+ const nsAString& aReason) {
+ // Let's continue only if we have some listeners.
+ if (!HasListeners()) {
+ return;
+ }
+
+ RefPtr<WebTransportSessionClosedRunnable> runnable =
+ new WebTransportSessionClosedRunnable(aInnerWindowID, aHttpChannelId,
+ aCode, aReason);
+ DebugOnly<nsresult> rv =
+ NS_IsMainThread() ? runnable->Run() : NS_DispatchToMainThread(runnable);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
+}
+
WebTransportEventService::~WebTransportEventService() {
MOZ_ASSERT(NS_IsMainThread());
}
@@ -148,6 +248,20 @@ bool WebTransportEventService::HasListeners() const {
return !!mCountListeners;
}
+void WebTransportEventService::GetListeners(
+ uint64_t aInnerWindowID,
+ WebTransportEventService::WebTransportEventListeners& aListeners) const {
+ MOZ_ASSERT(NS_IsMainThread());
+ aListeners.Clear();
+
+ WindowListener* listener = mWindows.Get(aInnerWindowID);
+ if (!listener) {
+ return;
+ }
+
+ aListeners.AppendElements(listener->mListeners);
+}
+
void WebTransportEventService::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
if (gWebTransportEventService) {
@@ -158,6 +272,7 @@ void WebTransportEventService::Shutdown() {
}
mWindows.Clear();
+ StaticMutexAutoLock lock(sLock);
gWebTransportEventService = nullptr;
}
}
diff --git a/netwerk/protocol/webtransport/WebTransportEventService.h b/netwerk/protocol/webtransport/WebTransportEventService.h
@@ -23,21 +23,31 @@ namespace net {
class WebTransportEventService final : public nsIWebTransportEventService,
public nsIObserver {
public:
- NS_DECL_ISUPPORTS
+ NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIWEBTRANSPORTEVENTSERVICE
static already_AddRefed<WebTransportEventService> GetOrCreate();
+ using WebTransportEventListeners =
+ nsTArray<nsCOMPtr<nsIWebTransportEventListener>>;
+
+ void GetListeners(uint64_t aInnerWindowID,
+ WebTransportEventListeners& aListeners) const;
+
+ void WebTransportSessionCreated(uint64_t aInnerWindowID,
+ uint64_t aHttpChannelId);
+
+ void WebTransportSessionClosed(uint64_t aInnerWindowID,
+ uint64_t aHttpChannelId, uint32_t aCode,
+ const nsAString& aReason);
+
private:
WebTransportEventService();
~WebTransportEventService();
bool HasListeners() const;
- using WebTransportEventListeners =
- nsTArray<nsCOMPtr<nsIWebTransportEventListener>>;
-
struct WindowListener {
WebTransportEventListeners mListeners;
};
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
@@ -9,6 +9,7 @@
#include "ScopedNSSTypes.h"
#include "WebTransportSessionProxy.h"
#include "WebTransportStreamProxy.h"
+#include "WebTransportEventService.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
@@ -52,7 +53,7 @@ WebTransportSessionProxy::~WebTransportSessionProxy() {
MOZ_ASSERT(mState != WebTransportSessionProxyState::SESSION_CLOSE_PENDING,
"We can not be in the SESSION_CLOSE_PENDING state in destructor, "
- "because should e an runnable that holds reference to this"
+ "because should be a runnable that holds reference to this"
"object.");
(void)gSocketTransportService->Dispatch(NS_NewRunnableFunction(
@@ -181,6 +182,9 @@ nsresult WebTransportSessionProxy::AsyncConnectWithClient(
if (NS_SUCCEEDED(rv)) {
cleanup.release();
}
+
+ mHttpChannelID = httpChannel->ChannelId();
+
return rv;
}
@@ -566,6 +570,12 @@ WebTransportSessionProxy::GetMaxDatagramSize() {
return NS_OK;
}
+NS_IMETHODIMP
+WebTransportSessionProxy::GetHttpChannelID(uint64_t* _retval) {
+ *_retval = mHttpChannelID;
+ return NS_OK;
+}
+
//-----------------------------------------------------------------------------
// WebTransportSessionProxy::nsIStreamListener
//-----------------------------------------------------------------------------
@@ -924,7 +934,7 @@ WebTransportSessionProxy::OnIncomingUnidirectionalStreamAvailable(
NS_IMETHODIMP
WebTransportSessionProxy::OnSessionReady(uint64_t ready) {
- MOZ_ASSERT(false, "Should not b called");
+ MOZ_ASSERT(false, "Should not be called");
return NS_OK;
}
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.h b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
@@ -117,6 +117,8 @@
namespace mozilla::net {
+class WebTransportEventService;
+
class WebTransportStreamCallbackWrapper;
class WebTransportSessionProxy final
@@ -177,7 +179,9 @@ class WebTransportSessionProxy final
uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome);
nsCOMPtr<nsIChannel> mChannel;
+ uint64_t mHttpChannelID = 0;
nsCOMPtr<nsIChannel> mRedirectChannel;
+ RefPtr<WebTransportEventService> mService;
nsCOMPtr<WebTransportSessionEventListener> mListener MOZ_GUARDED_BY(mMutex);
RefPtr<WebTransportSessionBase> mWebTransportSession MOZ_GUARDED_BY(mMutex);
uint64_t mSessionId MOZ_GUARDED_BY(mMutex) = UINT64_MAX;
diff --git a/netwerk/protocol/webtransport/nsIWebTransport.idl b/netwerk/protocol/webtransport/nsIWebTransport.idl
@@ -75,6 +75,8 @@ interface nsIWebTransport : nsISupports {
void getMaxDatagramSize();
+ uint64_t getHttpChannelID();
+
// This can be only called after onSessionReady().
// After this point, we can retarget the underlying WebTransportSessionProxy
// object off main thread.
diff --git a/netwerk/protocol/webtransport/nsIWebTransportEventService.idl b/netwerk/protocol/webtransport/nsIWebTransportEventService.idl
@@ -8,7 +8,12 @@
[scriptable, uuid(5f34ad48-92ec-4684-ab08-76f6ead9008d)]
interface nsIWebTransportEventListener : nsISupports
{
- // TODO: The functions for this interface are still TBD
+ [must_use] void webTransportSessionCreated(in uint64_t aHttpChannelId);
+
+ [must_use] void webTransportSessionClosed(in uint64_t aHttpChannelId, in unsigned short aCode,
+ in AString aReason);
+
+ // TODO: The other functions for this interface are still TBD. See Bug 1963028
};
[scriptable, builtinclass, uuid(0907d7d4-351b-4513-a2d9-f6e6467d59ec)]