commit 1928932c32104ec5a4171f7d4b19f3b333834540
parent adb38904a5a5714d61cc62c4cc4bb394e1a9c553
Author: Hubert Boma Manilla <hmanilla@mozilla.com>
Date: Mon, 10 Nov 2025 12:13:02 +0000
Bug 1999236 - Add the WebTransport Event Service and API's for managing listeners r=necko-reviewers,layout-reviewers,kershaw,emilio
Differential Revision: https://phabricator.services.mozilla.com/D266659
Diffstat:
8 files changed, 304 insertions(+), 0 deletions(-)
diff --git a/dom/webtransport/test/chrome.toml b/dom/webtransport/test/chrome.toml
@@ -0,0 +1,14 @@
+# 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/.
+
+[DEFAULT]
+run-if = ["http3"]
+prefs = [
+ "network.dns.localDomains=foo.example.com",
+ "network.webtransport.enabled=true",
+ "network.http.http3.enabled=true"
+]
+
+["test_webtransporteventservice_basic.html"]
+scheme = ["https"]
diff --git a/dom/webtransport/test/moz.build b/dom/webtransport/test/moz.build
@@ -7,3 +7,7 @@
TEST_DIRS += [
"xpcshell",
]
+
+MOCHITEST_CHROME_MANIFESTS += [
+ "chrome.toml",
+]
diff --git a/dom/webtransport/test/test_webtransporteventservice_basic.html b/dom/webtransport/test/test_webtransporteventservice_basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>WebTransportevent service basic test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+ var service = Cc["@mozilla.org/webtransportevent/service;1"]
+ .getService(Ci.nsIWebTransportEventService);
+ ok(!!service, "We have the nsIWebTransportEventService");
+
+ var innerWindowId = window.windowGlobalChild.innerWindowId;
+ ok(innerWindowId, "We have a valid innerWindowID: " + innerWindowId);
+
+ var listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIWebTransportEventListener"]),
+ };
+
+ service.addListener(innerWindowId, listener);
+ ok(true, "Listener added");
+ ok(service.hasListenerFor(innerWindowId), "hasListenerFor(innerId) should be true");
+
+ service.removeListener(innerWindowId, listener);
+ ok(true, "Listener removed");
+ ok(!service.hasListenerFor(innerWindowId), "hasListenerFor(innerId) should be false");
+</script>
+</body>
+</html>
diff --git a/layout/build/components.conf b/layout/build/components.conf
@@ -441,6 +441,14 @@ Classes = [
'headers': ['mozilla/dom/EventSourceEventService.h'],
'constructor': 'mozilla::dom::EventSourceEventService::GetOrCreate',
},
+ {
+ 'cid': '{54e6b9d6-248c-4eb0-81b8-e72f04067f3c}',
+ 'contract_ids': ['@mozilla.org/webtransportevent/service;1'],
+ 'singleton': True,
+ 'type': 'mozilla::net::WebTransportEventService',
+ 'headers': ['mozilla/net/WebTransportEventService.h'],
+ 'constructor': 'mozilla::net::WebTransportEventService::GetOrCreate',
+ },
]
if defined('MOZ_WEBSPEECH'):
diff --git a/netwerk/protocol/webtransport/WebTransportEventService.cpp b/netwerk/protocol/webtransport/WebTransportEventService.cpp
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "WebTransportEventService.h"
+#include "nsThreadUtils.h"
+#include "nsIObserverService.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Services.h"
+#include "nsISupportsPrimitives.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+StaticRefPtr<WebTransportEventService> gWebTransportEventService;
+
+} // anonymous namespace
+
+/* static */
+already_AddRefed<WebTransportEventService>
+WebTransportEventService::GetOrCreate() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!gWebTransportEventService) {
+ gWebTransportEventService = new WebTransportEventService();
+ }
+
+ RefPtr<WebTransportEventService> service = gWebTransportEventService.get();
+ return service.forget();
+}
+
+NS_INTERFACE_MAP_BEGIN(WebTransportEventService)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebTransportEventService)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIWebTransportEventService)
+NS_INTERFACE_MAP_END
+
+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);
+ obs->AddObserver(this, "inner-window-destroyed", false);
+ }
+}
+
+WebTransportEventService::~WebTransportEventService() {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+NS_IMETHODIMP
+WebTransportEventService::AddListener(uint64_t aInnerWindowID,
+ nsIWebTransportEventListener* aListener) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aListener) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ ++mCountListeners;
+
+ WindowListener* listener = mWindows.GetOrInsertNew(aInnerWindowID);
+
+ listener->mListeners.AppendElement(aListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportEventService::RemoveListener(
+ uint64_t aInnerWindowID, nsIWebTransportEventListener* aListener) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aListener) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ WindowListener* listener = mWindows.Get(aInnerWindowID);
+ if (!listener) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!listener->mListeners.RemoveElement(aListener)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // The last listener for this window.
+ if (listener->mListeners.IsEmpty()) {
+ mWindows.Remove(aInnerWindowID);
+ }
+
+ MOZ_ASSERT(mCountListeners);
+ --mCountListeners;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportEventService::HasListenerFor(uint64_t aInnerWindowID,
+ bool* aResult) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ *aResult = mWindows.Get(aInnerWindowID);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportEventService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!strcmp(aTopic, "xpcom-shutdown")) {
+ Shutdown();
+ return NS_OK;
+ }
+
+ if (!strcmp(aTopic, "inner-window-destroyed") && HasListeners()) {
+ nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+
+ uint64_t innerWindowID;
+ nsresult rv = wrapper->GetData(&innerWindowID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ WindowListener* listener = mWindows.Get(innerWindowID);
+ if (!listener) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mCountListeners >= listener->mListeners.Length());
+ mCountListeners -= listener->mListeners.Length();
+ mWindows.Remove(innerWindowID);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+bool WebTransportEventService::HasListeners() const {
+ return !!mCountListeners;
+}
+
+void WebTransportEventService::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (gWebTransportEventService) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(gWebTransportEventService, "xpcom-shutdown");
+ obs->RemoveObserver(gWebTransportEventService, "inner-window-destroyed");
+ }
+
+ mWindows.Clear();
+ gWebTransportEventService = nullptr;
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/protocol/webtransport/WebTransportEventService.h b/netwerk/protocol/webtransport/WebTransportEventService.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_net_WebTransportEventService_h
+#define mozilla_net_WebTransportEventService_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "nsIWebTransportEventService.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsIObserver.h"
+#include "nsISupportsImpl.h"
+#include "nsTHashMap.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+
+namespace mozilla {
+namespace net {
+
+class WebTransportEventService final : public nsIWebTransportEventService,
+ public nsIObserver {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIWEBTRANSPORTEVENTSERVICE
+
+ static already_AddRefed<WebTransportEventService> GetOrCreate();
+
+ private:
+ WebTransportEventService();
+ ~WebTransportEventService();
+
+ bool HasListeners() const;
+
+ using WebTransportEventListeners =
+ nsTArray<nsCOMPtr<nsIWebTransportEventListener>>;
+
+ struct WindowListener {
+ WebTransportEventListeners mListeners;
+ };
+
+ void Shutdown();
+
+ nsClassHashtable<nsUint64HashKey, WindowListener> mWindows;
+
+ uint64_t mCountListeners;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_WebTransportEventService_h
diff --git a/netwerk/protocol/webtransport/moz.build b/netwerk/protocol/webtransport/moz.build
@@ -9,18 +9,21 @@ with Files("**"):
XPIDL_SOURCES += [
"nsIWebTransport.idl",
+ "nsIWebTransportEventService.idl",
"nsIWebTransportStream.idl",
]
XPIDL_MODULE = "necko_webtransport"
EXPORTS.mozilla.net += [
+ "WebTransportEventService.h",
"WebTransportHash.h",
"WebTransportSessionProxy.h",
"WebTransportStreamProxy.h",
]
UNIFIED_SOURCES += [
+ "WebTransportEventService.cpp",
"WebTransportHash.cpp",
"WebTransportSessionProxy.cpp",
"WebTransportStreamProxy.cpp",
diff --git a/netwerk/protocol/webtransport/nsIWebTransportEventService.idl b/netwerk/protocol/webtransport/nsIWebTransportEventService.idl
@@ -0,0 +1,24 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(5f34ad48-92ec-4684-ab08-76f6ead9008d)]
+interface nsIWebTransportEventListener : nsISupports
+{
+ // TODO: The functions for this interface are still TBD
+};
+
+[scriptable, builtinclass, uuid(0907d7d4-351b-4513-a2d9-f6e6467d59ec)]
+interface nsIWebTransportEventService : nsISupports
+{
+ [must_use] void addListener(in unsigned long long aInnerWindowID,
+ in nsIWebTransportEventListener aListener);
+
+ [must_use] void removeListener(in unsigned long long aInnerWindowID,
+ in nsIWebTransportEventListener aListener);
+
+ [must_use] boolean hasListenerFor(in unsigned long long aInnerWindowID);
+};