tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 21b5e322a3bd2525101c9fd2c333f3e810b1d592
parent 9cfe831685694267ced2344018359b0f042d3cb8
Author: Hubert Boma Manilla <hmanilla@mozilla.com>
Date:   Fri, 19 Dec 2025 11:22:33 +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:
Mdom/webtransport/api/WebTransport.cpp | 39+++++++++++++++++++++++++++++++++++++++
Mdom/webtransport/api/WebTransport.h | 4++++
Mdom/webtransport/parent/WebTransportParent.cpp | 10++++++++++
Mdom/webtransport/parent/WebTransportParent.h | 3+++
Mdom/webtransport/shared/PWebTransport.ipdl | 6++++++
Mnetwerk/protocol/webtransport/WebTransportEventService.cpp | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mnetwerk/protocol/webtransport/WebTransportEventService.h | 18++++++++++++++----
Mnetwerk/protocol/webtransport/WebTransportSessionProxy.cpp | 14++++++++++++--
Mnetwerk/protocol/webtransport/WebTransportSessionProxy.h | 4++++
Mnetwerk/protocol/webtransport/nsIWebTransport.idl | 2++
Mnetwerk/protocol/webtransport/nsIWebTransportEventService.idl | 7++++++-
11 files changed, 225 insertions(+), 13 deletions(-)

diff --git a/dom/webtransport/api/WebTransport.cpp b/dom/webtransport/api/WebTransport.cpp @@ -264,6 +264,12 @@ void WebTransport::Init(const GlobalObject& aGlobal, const nsAString& aURL, return; } + // TODO: Fix the shared and service worker use cases + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + mService = workerPrivate && (workerPrivate->IsSharedWorker() || + workerPrivate->IsServiceWorker()) + ? nullptr + : net::WebTransportEventService::GetOrCreate(); // XXX TODO // Step 15 Let transport be a newly constructed WebTransport object, with: @@ -414,6 +420,17 @@ 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()) { + mInnerWindowID = workerPrivate->WindowID(); + } + } mChild->SendGetMaxDatagramSize()->Then( GetCurrentSerialEventTarget(), __func__, @@ -432,6 +449,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 +871,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 (aCloseInfo && 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,13 +115,45 @@ NS_IMPL_ADDREF(WebTransportEventService) NS_IMPL_RELEASE(WebTransportEventService) WebTransportEventService::WebTransportEventService() : mCountListeners(0) { - MOZ_ASSERT(NS_IsMainThread()); + if (NS_IsMainThread()) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, "xpcom-shutdown", false); + obs->AddObserver(this, "inner-window-destroyed", false); + } + } else { + gWebTransportEventService = nullptr; + } +} + +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"); +} - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->AddObserver(this, "xpcom-shutdown", false); - obs->AddObserver(this, "inner-window-destroyed", false); +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() { @@ -148,6 +252,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 +276,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)]