summaryrefslogtreecommitdiffstats
path: root/dom/webtransport
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/webtransport/api/WebTransport.cpp306
-rw-r--r--dom/webtransport/api/WebTransport.h102
-rw-r--r--dom/webtransport/api/WebTransportBidirectionalStream.cpp34
-rw-r--r--dom/webtransport/api/WebTransportBidirectionalStream.h52
-rw-r--r--dom/webtransport/api/WebTransportDatagramDuplexStream.cpp35
-rw-r--r--dom/webtransport/api/WebTransportDatagramDuplexStream.h78
-rw-r--r--dom/webtransport/api/WebTransportError.cpp30
-rw-r--r--dom/webtransport/api/WebTransportError.h34
-rw-r--r--dom/webtransport/api/WebTransportReceiveStream.h39
-rw-r--r--dom/webtransport/api/WebTransportSendStream.h39
-rw-r--r--dom/webtransport/api/moz.build25
-rw-r--r--dom/webtransport/child/WebTransportChild.h43
-rw-r--r--dom/webtransport/child/moz.build13
-rw-r--r--dom/webtransport/moz.build13
-rw-r--r--dom/webtransport/parent/WebTransportParent.cpp266
-rw-r--r--dom/webtransport/parent/WebTransportParent.h57
-rw-r--r--dom/webtransport/parent/moz.build22
-rw-r--r--dom/webtransport/shared/PWebTransport.ipdl26
-rw-r--r--dom/webtransport/shared/WebTransportLog.cpp13
-rw-r--r--dom/webtransport/shared/WebTransportLog.h25
-rw-r--r--dom/webtransport/shared/moz.build21
-rw-r--r--dom/webtransport/test/moz.build9
-rw-r--r--dom/webtransport/test/xpcshell/moz.build9
-rw-r--r--dom/webtransport/test/xpcshell/test_close.js64
-rw-r--r--dom/webtransport/test/xpcshell/xpcshell.ini11
25 files changed, 1366 insertions, 0 deletions
diff --git a/dom/webtransport/api/WebTransport.cpp b/dom/webtransport/api/WebTransport.cpp
new file mode 100644
index 0000000000..5915827f62
--- /dev/null
+++ b/dom/webtransport/api/WebTransport.cpp
@@ -0,0 +1,306 @@
+/* -*- 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 "WebTransport.h"
+
+#include "nsUTF8Utils.h"
+#include "nsIURL.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PWebTransport.h"
+#include "mozilla/dom/ReadableStream.h"
+#include "mozilla/dom/WebTransportDatagramDuplexStream.h"
+#include "mozilla/dom/WebTransportLog.h"
+#include "mozilla/dom/WritableStream.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebTransport, mGlobal,
+ mIncomingUnidirectionalStreams,
+ mIncomingBidirectionalStreams,
+ mSendStreams, mReceiveStreams, mDatagrams,
+ mReady, mClosed)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebTransport)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebTransport)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebTransport)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+WebTransport::WebTransport(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal),
+ mState(WebTransportState::CONNECTING),
+ mReliability(WebTransportReliabilityMode::Pending) {
+ LOG(("Creating WebTransport %p", this));
+}
+
+// WebIDL Boilerplate
+
+nsIGlobalObject* WebTransport::GetParentObject() const { return mGlobal; }
+
+JSObject* WebTransport::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return WebTransport_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+// WebIDL Interface
+
+/* static */
+already_AddRefed<WebTransport> WebTransport::Constructor(
+ const GlobalObject& aGlobal, const nsAString& aURL,
+ const WebTransportOptions& aOptions, ErrorResult& aError) {
+ LOG(("Creating WebTransport for %s", NS_ConvertUTF16toUTF8(aURL).get()));
+
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ RefPtr<WebTransport> result = new WebTransport(global);
+ if (!result->Init(aGlobal, aURL, aOptions, aError)) {
+ return nullptr;
+ }
+
+ return result.forget();
+}
+
+bool WebTransport::Init(const GlobalObject& aGlobal, const nsAString& aURL,
+ const WebTransportOptions& aOptions,
+ ErrorResult& aError) {
+ // Initiate connection with parent
+ using mozilla::ipc::BackgroundChild;
+ using mozilla::ipc::Endpoint;
+ using mozilla::ipc::PBackgroundChild;
+
+ mReady = Promise::Create(mGlobal, aError);
+ if (NS_WARN_IF(aError.Failed())) {
+ return false;
+ }
+
+ mClosed = Promise::Create(mGlobal, aError);
+ if (NS_WARN_IF(aError.Failed())) {
+ return false;
+ }
+
+ QueuingStrategy strategy;
+ Optional<JS::Handle<JSObject*>> underlying;
+ mIncomingUnidirectionalStreams =
+ ReadableStream::Constructor(aGlobal, underlying, strategy, aError);
+ if (aError.Failed()) {
+ return false;
+ }
+ mIncomingBidirectionalStreams =
+ ReadableStream::Constructor(aGlobal, underlying, strategy, aError);
+ if (aError.Failed()) {
+ return false;
+ }
+
+ PBackgroundChild* backgroundChild =
+ BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!backgroundChild)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSystemPrincipal();
+ // Create a new IPC connection
+ Endpoint<PWebTransportParent> parentEndpoint;
+ Endpoint<PWebTransportChild> childEndpoint;
+ MOZ_ALWAYS_SUCCEEDS(
+ PWebTransport::CreateEndpoints(&parentEndpoint, &childEndpoint));
+
+ RefPtr<WebTransportChild> child = new WebTransportChild();
+ if (!childEndpoint.Bind(child)) {
+ return false;
+ }
+
+ mState = WebTransportState::CONNECTING;
+ LOG(("Connecting WebTransport to parent for %s",
+ NS_ConvertUTF16toUTF8(aURL).get()));
+
+ // Parse string for validity and Throw a SyntaxError if it isn't
+ if (!ParseURL(aURL)) {
+ aError.ThrowSyntaxError("Invalid WebTransport URL");
+ return false;
+ }
+ bool dedicated =
+ !aOptions.mAllowPooling; // spec language, optimizer will eliminate this
+ bool requireUnreliable = aOptions.mRequireUnreliable;
+ WebTransportCongestionControl congestionControl = aOptions.mCongestionControl;
+ if (aOptions.mServerCertificateHashes.WasPassed()) {
+ // XXX bug 1806693
+ aError.ThrowNotSupportedError("No support for serverCertificateHashes yet");
+ // XXX if dedicated is false and serverCertificateHashes is non-null, then
+ // throw a TypeError. Also should enforce in parent
+ return false;
+ }
+
+ // https://w3c.github.io/webtransport/#webtransport-constructor Spec 5.2
+ backgroundChild
+ ->SendCreateWebTransportParent(aURL, principal, dedicated,
+ requireUnreliable,
+ (uint32_t)congestionControl,
+ // XXX serverCertHashes,
+ std::move(parentEndpoint))
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this},
+ child](PBackgroundChild::CreateWebTransportParentPromise::
+ ResolveOrRejectValue&& aResult) {
+ // aResult is a Tuple<nsresult, uint8_t>
+ // TODO: is there a better/more-spec-compliant error in the
+ // reject case? Which begs the question, why would we get a
+ // reject?
+ nsresult rv = aResult.IsReject()
+ ? NS_ERROR_FAILURE
+ : Get<0>(aResult.ResolveValue());
+ if (NS_FAILED(rv)) {
+ self->RejectWaitingConnection(rv);
+ } else {
+ // This will process anything waiting for the connection to
+ // complete;
+ self->ResolveWaitingConnection(
+ static_cast<WebTransportReliabilityMode>(
+ Get<1>(aResult.ResolveValue())),
+ child);
+ }
+ });
+
+ return true;
+}
+
+void WebTransport::ResolveWaitingConnection(
+ WebTransportReliabilityMode aReliability, WebTransportChild* aChild) {
+ LOG(("Resolved Connection %p, reliability = %u", this,
+ (unsigned)aReliability));
+
+ MOZ_ASSERT(mState == WebTransportState::CONNECTING);
+ mChild = aChild;
+ mState = WebTransportState::CONNECTED;
+ mReliability = aReliability;
+
+ mReady->MaybeResolve(true);
+}
+
+void WebTransport::RejectWaitingConnection(nsresult aRv) {
+ LOG(("Reject Connection %p", this));
+ MOZ_ASSERT(mState == WebTransportState::CONNECTING);
+ mState = WebTransportState::FAILED;
+ LOG(("Rejected connection %x", (uint32_t)aRv));
+
+ // https://w3c.github.io/webtransport/#webtransport-internal-slots
+ // "Reliability returns "pending" until a connection is established" so
+ // we leave it pending
+ mReady->MaybeReject(aRv);
+}
+
+bool WebTransport::ParseURL(const nsAString& aURL) const {
+ NS_ENSURE_TRUE(!aURL.IsEmpty(), false);
+
+ // 5.4 = https://w3c.github.io/webtransport/#webtransport-constructor
+ // 5.4 #1 and #2
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // 5.4 #3
+ if (!uri->SchemeIs("https")) {
+ return false;
+ }
+
+ // 5.4 #4 no fragments
+ bool hasRef;
+ rv = uri->GetHasRef(&hasRef);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !hasRef, false);
+
+ return true;
+}
+
+already_AddRefed<Promise> WebTransport::GetStats(ErrorResult& aError) {
+ aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return nullptr;
+}
+
+already_AddRefed<Promise> WebTransport::Ready() { return do_AddRef(mReady); }
+
+WebTransportReliabilityMode WebTransport::Reliability() { return mReliability; }
+
+WebTransportCongestionControl WebTransport::CongestionControl() {
+ // XXX not implemented
+ return WebTransportCongestionControl::Default;
+}
+
+already_AddRefed<Promise> WebTransport::Closed() {
+ ErrorResult error;
+ RefPtr<Promise> promise = Promise::Create(GetParentObject(), error);
+ if (error.Failed()) {
+ return nullptr;
+ }
+ promise->MaybeResolve(mState == WebTransportState::CLOSED);
+ return promise.forget();
+}
+
+void WebTransport::Close(const WebTransportCloseInfo& aOptions) {
+ LOG(("Close() called"));
+ if (mState == WebTransportState::CONNECTED ||
+ mState == WebTransportState::CONNECTING) {
+ MOZ_ASSERT(mChild);
+ LOG(("Sending Close"));
+ // "Let reasonString be the maximal code unit prefix of
+ // closeInfo.reason where the length of the UTF-8 encoded prefix
+ // doesn’t exceed 1024."
+ // Take the maximal "code unit prefix" of mReason and limit to 1024 bytes
+ // https://w3c.github.io/webtransport/#webtransport-methods 5.4
+
+ if (aOptions.mReason.Length() > 1024u) {
+ // We want to start looking for the previous code point at one past the
+ // limit, since if a code point ends exactly at the specified length, the
+ // next byte will be the start of a new code point. Note
+ // RewindToPriorUTF8Codepoint doesn't reduce the index if it points to the
+ // start of a code point. We know reason[1024] is accessible since
+ // Length() > 1024
+ mChild->SendClose(
+ aOptions.mCloseCode,
+ Substring(aOptions.mReason, 0,
+ RewindToPriorUTF8Codepoint(aOptions.mReason.get(), 1024u)));
+ } else {
+ mChild->SendClose(aOptions.mCloseCode, aOptions.mReason);
+ }
+ mState = WebTransportState::CLOSED;
+
+ // The other side will call `Close()` for us now, make sure we don't call it
+ // in our destructor.
+ mChild = nullptr;
+ }
+}
+
+already_AddRefed<WebTransportDatagramDuplexStream> WebTransport::Datagrams() {
+ LOG(("Datagrams() called"));
+ // XXX not implemented
+ return nullptr;
+}
+
+already_AddRefed<Promise> WebTransport::CreateBidirectionalStream(
+ ErrorResult& aError) {
+ LOG(("CreateBidirectionalStream() called"));
+ aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return nullptr;
+}
+
+already_AddRefed<ReadableStream> WebTransport::IncomingBidirectionalStreams() {
+ return do_AddRef(mIncomingBidirectionalStreams);
+}
+
+already_AddRefed<Promise> WebTransport::CreateUnidirectionalStream(
+ ErrorResult& aError) {
+ LOG(("CreateUnidirectionalStream() called"));
+ aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return nullptr;
+}
+
+already_AddRefed<ReadableStream> WebTransport::IncomingUnidirectionalStreams() {
+ return do_AddRef(mIncomingUnidirectionalStreams);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/webtransport/api/WebTransport.h b/dom/webtransport/api/WebTransport.h
new file mode 100644
index 0000000000..803a370676
--- /dev/null
+++ b/dom/webtransport/api/WebTransport.h
@@ -0,0 +1,102 @@
+/* -*- 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 DOM_WEBTRANSPORT_API_WEBTRANSPORT__H_
+#define DOM_WEBTRANSPORT_API_WEBTRANSPORT__H_
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/WebTransportBinding.h"
+#include "mozilla/dom/WebTransportChild.h"
+
+namespace mozilla::dom {
+
+class WebTransportDatagramDuplexStream;
+class ReadableStream;
+class WritableStream;
+
+class WebTransport final : public nsISupports, public nsWrapperCache {
+ public:
+ explicit WebTransport(nsIGlobalObject* aGlobal);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WebTransport)
+
+ enum class WebTransportState { CONNECTING, CONNECTED, CLOSED, FAILED };
+
+ bool Init(const GlobalObject& aGlobal, const nsAString& aUrl,
+ const WebTransportOptions& aOptions, ErrorResult& aError);
+ void ResolveWaitingConnection(WebTransportReliabilityMode aReliability,
+ WebTransportChild* aChild);
+ void RejectWaitingConnection(nsresult aRv);
+ bool ParseURL(const nsAString& aURL) const;
+
+ // WebIDL Boilerplate
+ nsIGlobalObject* GetParentObject() const;
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ // WebIDL Interface
+ static already_AddRefed<WebTransport> Constructor(
+ const GlobalObject& aGlobal, const nsAString& aUrl,
+ const WebTransportOptions& aOptions, ErrorResult& aRv);
+
+ already_AddRefed<Promise> GetStats(ErrorResult& aError);
+
+ already_AddRefed<Promise> Ready();
+ WebTransportReliabilityMode Reliability();
+ WebTransportCongestionControl CongestionControl();
+ already_AddRefed<Promise> Closed();
+ void Close(const WebTransportCloseInfo& aOptions);
+ already_AddRefed<WebTransportDatagramDuplexStream> Datagrams();
+ already_AddRefed<Promise> CreateBidirectionalStream(ErrorResult& aError);
+ already_AddRefed<ReadableStream> IncomingBidirectionalStreams();
+ already_AddRefed<Promise> CreateUnidirectionalStream(ErrorResult& aError);
+ already_AddRefed<ReadableStream> IncomingUnidirectionalStreams();
+
+ void Shutdown() {}
+
+ private:
+ ~WebTransport() {
+ // Should be empty by this point, because we should always have run cleanup:
+ // https://w3c.github.io/webtransport/#webtransport-procedures
+ MOZ_ASSERT(mSendStreams.IsEmpty());
+ MOZ_ASSERT(mReceiveStreams.IsEmpty());
+ // If this WebTransport was destroyed without being closed properly, make
+ // sure to clean up the channel.
+ if (mChild) {
+ mChild->Shutdown();
+ }
+ }
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ RefPtr<WebTransportChild> mChild;
+
+ // Spec-defined slots:
+ // ordered sets, but we can't have duplicates, and this spec only appends.
+ // Order is visible due to
+ // https://w3c.github.io/webtransport/#webtransport-procedures step 10: "For
+ // each sendStream in sendStreams, error sendStream with error."
+ // XXX Use nsTArray.h for now, but switch to OrderHashSet/Table for release to
+ // improve remove performance (if needed)
+ nsTArray<RefPtr<WritableStream>> mSendStreams;
+ nsTArray<RefPtr<ReadableStream>> mReceiveStreams;
+
+ WebTransportState mState;
+ RefPtr<Promise> mReady;
+ WebTransportReliabilityMode mReliability;
+ // These are created in the constructor
+ RefPtr<ReadableStream> mIncomingUnidirectionalStreams;
+ RefPtr<ReadableStream> mIncomingBidirectionalStreams;
+ RefPtr<WebTransportDatagramDuplexStream> mDatagrams;
+ RefPtr<Promise> mClosed;
+};
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/webtransport/api/WebTransportBidirectionalStream.cpp b/dom/webtransport/api/WebTransportBidirectionalStream.cpp
new file mode 100644
index 0000000000..60f202cd03
--- /dev/null
+++ b/dom/webtransport/api/WebTransportBidirectionalStream.cpp
@@ -0,0 +1,34 @@
+/* -*- 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 "WebTransportBidirectionalStream.h"
+#include "mozilla/dom/Promise.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebTransportBidirectionalStream, mGlobal)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebTransportBidirectionalStream)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebTransportBidirectionalStream)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebTransportBidirectionalStream)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+// WebIDL Boilerplate
+
+nsIGlobalObject* WebTransportBidirectionalStream::GetParentObject() const {
+ return mGlobal;
+}
+
+JSObject* WebTransportBidirectionalStream::WrapObject(
+ JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return WebTransportBidirectionalStream_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+// WebIDL Interface
+
+} // namespace mozilla::dom
diff --git a/dom/webtransport/api/WebTransportBidirectionalStream.h b/dom/webtransport/api/WebTransportBidirectionalStream.h
new file mode 100644
index 0000000000..0f9e90f767
--- /dev/null
+++ b/dom/webtransport/api/WebTransportBidirectionalStream.h
@@ -0,0 +1,52 @@
+/* -*- 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 DOM_WEBTRANSPORT_API_WEBTRANSPORTBIDIRECTIONALSTREAM__H_
+#define DOM_WEBTRANSPORT_API_WEBTRANSPORTBIDIRECTIONALSTREAM__H_
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ReadableStream.h"
+#include "mozilla/dom/WritableStream.h"
+#include "mozilla/dom/WebTransportSendReceiveStreamBinding.h"
+
+// #include "mozilla/dom/WebTransportReceiveStream.h"
+// #include "mozilla/dom/WebTransportSendStream.h"
+
+namespace mozilla::dom {
+class WebTransportBidirectionalStream final : public nsISupports,
+ public nsWrapperCache {
+ public:
+ explicit WebTransportBidirectionalStream(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal) {}
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WebTransportBidirectionalStream)
+
+ // WebIDL Boilerplate
+ nsIGlobalObject* GetParentObject() const;
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ // WebIDL Interface
+ // XXX spec says these should be WebTransportReceiveStream and
+ // WebTransportSendStream
+ // XXX Not implemented
+ already_AddRefed<ReadableStream> Readable() { return nullptr; }
+ already_AddRefed<WritableStream> Writable() { return nullptr; }
+
+ private:
+ ~WebTransportBidirectionalStream() = default;
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+};
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/webtransport/api/WebTransportDatagramDuplexStream.cpp b/dom/webtransport/api/WebTransportDatagramDuplexStream.cpp
new file mode 100644
index 0000000000..e4fac822e5
--- /dev/null
+++ b/dom/webtransport/api/WebTransportDatagramDuplexStream.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 "WebTransportDatagramDuplexStream.h"
+#include "mozilla/dom/Promise.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebTransportDatagramDuplexStream, mGlobal,
+ mReadable, mWritable)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebTransportDatagramDuplexStream)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebTransportDatagramDuplexStream)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebTransportDatagramDuplexStream)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+// WebIDL Boilerplate
+
+nsIGlobalObject* WebTransportDatagramDuplexStream::GetParentObject() const {
+ return mGlobal;
+}
+
+JSObject* WebTransportDatagramDuplexStream::WrapObject(
+ JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return WebTransportDatagramDuplexStream_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+// WebIDL Interface
+
+} // namespace mozilla::dom
diff --git a/dom/webtransport/api/WebTransportDatagramDuplexStream.h b/dom/webtransport/api/WebTransportDatagramDuplexStream.h
new file mode 100644
index 0000000000..1aa895cd53
--- /dev/null
+++ b/dom/webtransport/api/WebTransportDatagramDuplexStream.h
@@ -0,0 +1,78 @@
+/* -*- 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 DOM_WEBTRANSPORT_API_WEBTRANSPORTDATAGRAMDUPLEXSTREAM__H_
+#define DOM_WEBTRANSPORT_API_WEBTRANSPORTDATAGRAMDUPLEXSTREAM__H_
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ReadableStream.h"
+#include "mozilla/dom/WritableStream.h"
+#include "mozilla/dom/WebTransportDatagramDuplexStreamBinding.h"
+
+namespace mozilla::dom {
+
+class WebTransportDatagramDuplexStream final : public nsISupports,
+ public nsWrapperCache {
+ public:
+ explicit WebTransportDatagramDuplexStream(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal) {}
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WebTransportDatagramDuplexStream)
+
+ // WebIDL Boilerplate
+ nsIGlobalObject* GetParentObject() const;
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ // WebIDL Interface
+ already_AddRefed<ReadableStream> Readable() const {
+ RefPtr<ReadableStream> result(mReadable);
+ return result.forget();
+ }
+ already_AddRefed<WritableStream> Writable() {
+ RefPtr<WritableStream> result(mWritable);
+ return result.forget();
+ }
+ // XXX not implemented
+ uint64_t MaxDatagramSize() const { return 1500; }
+ Nullable<double> GetIncomingMaxAge() const { return mIncomingMaxAge; }
+ void SetIncomingMaxAge(const Nullable<double>& aMaxAge) {
+ mIncomingMaxAge = aMaxAge;
+ }
+ Nullable<double> GetOutgoingMaxAge() const { return mOutgoingMaxAge; }
+ void SetOutgoingMaxAge(const Nullable<double>& aMaxAge) {
+ mOutgoingMaxAge = aMaxAge;
+ }
+ int64_t IncomingHighWaterMark() const { return mIncomingHighWaterMark; }
+ void SetIncomingHighWaterMark(int64_t aWaterMark) {
+ mIncomingHighWaterMark = aWaterMark;
+ }
+ int64_t OutgoingHighWaterMark() const { return mOutgoingHighWaterMark; }
+ void SetOutgoingHighWaterMark(int64_t aWaterMark) {
+ mOutgoingHighWaterMark = aWaterMark;
+ }
+
+ private:
+ ~WebTransportDatagramDuplexStream() = default;
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ RefPtr<ReadableStream> mReadable;
+ RefPtr<WritableStream> mWritable;
+
+ Nullable<double> mIncomingMaxAge;
+ Nullable<double> mOutgoingMaxAge;
+ int64_t mIncomingHighWaterMark = 0;
+ int64_t mOutgoingHighWaterMark = 0;
+};
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/webtransport/api/WebTransportError.cpp b/dom/webtransport/api/WebTransportError.cpp
new file mode 100644
index 0000000000..142d98d2b9
--- /dev/null
+++ b/dom/webtransport/api/WebTransportError.cpp
@@ -0,0 +1,30 @@
+/* -*- 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 "WebTransportError.h"
+
+namespace mozilla::dom {
+
+/* static */
+already_AddRefed<WebTransportError> WebTransportError::Constructor(
+ const GlobalObject& aGlobal, const WebTransportErrorInit& aInit) {
+ nsCString message(""_ns);
+ if (aInit.mMessage.WasPassed()) {
+ CopyUTF16toUTF8(aInit.mMessage.Value(), message);
+ }
+ RefPtr<WebTransportError> error(new WebTransportError(message));
+ if (aInit.mStreamErrorCode.WasPassed()) {
+ error->mStreamErrorCode = aInit.mStreamErrorCode.Value();
+ }
+ return error.forget();
+}
+
+WebTransportErrorSource WebTransportError::Source() {
+ // XXX Not implemented
+ return WebTransportErrorSource::Stream;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/webtransport/api/WebTransportError.h b/dom/webtransport/api/WebTransportError.h
new file mode 100644
index 0000000000..96b195ca0e
--- /dev/null
+++ b/dom/webtransport/api/WebTransportError.h
@@ -0,0 +1,34 @@
+/* -*- 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 DOM_WEBTRANSPORT_API_WEBTRANSPORTERROR__H_
+#define DOM_WEBTRANSPORT_API_WEBTRANSPORTERROR__H_
+
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/WebTransportErrorBinding.h"
+
+namespace mozilla::dom {
+class WebTransportError final : public DOMException {
+ protected:
+ explicit WebTransportError(const nsACString& aMessage)
+ : DOMException(NS_OK, aMessage, "stream"_ns, 0) {}
+
+ public:
+ static already_AddRefed<WebTransportError> Constructor(
+ const GlobalObject& aGlobal, const WebTransportErrorInit& aInit);
+
+ WebTransportErrorSource Source();
+ Nullable<uint8_t> GetStreamErrorCode() const {
+ return Nullable<uint8_t>(mStreamErrorCode);
+ }
+
+ private:
+ uint8_t mStreamErrorCode = 0;
+};
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/webtransport/api/WebTransportReceiveStream.h b/dom/webtransport/api/WebTransportReceiveStream.h
new file mode 100644
index 0000000000..013af43424
--- /dev/null
+++ b/dom/webtransport/api/WebTransportReceiveStream.h
@@ -0,0 +1,39 @@
+/* -*- 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 DOM_WEBTRANSPORT_API_WEBTRANSPORTSENDSTREAM__H_
+#define DOM_WEBTRANSPORT_API_WEBTRANSPORTSENDSTREAM__H_
+
+#include "mozilla/dom/ReadableStream.h"
+
+#if WEBTRANSPORT_STREAM_IMPLEMENTED
+namespace mozilla::dom {
+class WebTransportReceiveStream final : public ReadableStream {
+ protected:
+ WebTransportReceiveStream();
+
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WebTransportReceiveStream,
+ ReadableStream)
+
+ already_AddRefed<Promise> GetStats();
+};
+#else
+namespace mozilla::dom {
+class WebTransportReceiveStream final : public nsISupports {
+ protected:
+ WebTransportReceiveStream();
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ already_AddRefed<Promise> GetStats();
+};
+#endif
+}
+
+#endif
diff --git a/dom/webtransport/api/WebTransportSendStream.h b/dom/webtransport/api/WebTransportSendStream.h
new file mode 100644
index 0000000000..0de62208b4
--- /dev/null
+++ b/dom/webtransport/api/WebTransportSendStream.h
@@ -0,0 +1,39 @@
+/* -*- 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 DOM_WEBTRANSPORT_API_WEBTRANSPORTSENDSTREAM__H_
+#define DOM_WEBTRANSPORT_API_WEBTRANSPORTSENDSTREAM__H_
+
+#include "mozilla/dom/WritableStream.h"
+
+#if WEBTRANSPORT_STREAM_IMPLEMENTED
+namespace mozilla::dom {
+class WebTransportSendStream final : public WritableStream {
+ protected:
+ WebTransportSendStream();
+
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WebTransportSendStream,
+ WritableStream)
+
+ already_AddRefed<Promise> GetStats();
+};
+#else
+namespace mozilla::dom {
+class WebTransportSendStream final : public nsISupports {
+ protected:
+ WebTransportSendStream();
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ already_AddRefed<Promise> GetStats();
+};
+#endif
+}
+
+#endif
diff --git a/dom/webtransport/api/moz.build b/dom/webtransport/api/moz.build
new file mode 100644
index 0000000000..c456fb16d0
--- /dev/null
+++ b/dom/webtransport/api/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+ "WebTransport.h",
+ "WebTransportBidirectionalStream.h",
+ "WebTransportDatagramDuplexStream.h",
+ "WebTransportError.h",
+ "WebTransportReceiveStream.h",
+ "WebTransportSendStream.h",
+]
+
+UNIFIED_SOURCES += [
+ "WebTransport.cpp",
+ "WebTransportBidirectionalStream.cpp",
+ "WebTransportDatagramDuplexStream.cpp",
+ "WebTransportError.cpp",
+]
+
+FINAL_LIBRARY = "xul"
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/dom/webtransport/child/WebTransportChild.h b/dom/webtransport/child/WebTransportChild.h
new file mode 100644
index 0000000000..3aa7c8d8e2
--- /dev/null
+++ b/dom/webtransport/child/WebTransportChild.h
@@ -0,0 +1,43 @@
+/* -*- 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 DOM_WEBTRANSPORT_WEBTRANSPORTCHILD_H_
+#define DOM_WEBTRANSPORT_WEBTRANSPORTCHILD_H_
+
+#include "mozilla/dom/PWebTransportChild.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla::dom {
+
+class WebTransportChild : public PWebTransportChild {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(WebTransportChild)
+
+ virtual void CloseAll() {
+ // XXX need impl
+ }
+
+ virtual void Shutdown() {
+ if (!CanSend()) {
+ return;
+ }
+
+ Close();
+ }
+
+ ::mozilla::ipc::IPCResult RecvCloseAll(CloseAllResolver&& aResolver) {
+ CloseAll();
+ aResolver(NS_OK);
+ return IPC_OK();
+ }
+
+ protected:
+ virtual ~WebTransportChild() = default;
+};
+
+} // namespace mozilla::dom
+
+#endif // DOM_WEBTRANSPORT_WEBTRANSPORTCHILD_H_
diff --git a/dom/webtransport/child/moz.build b/dom/webtransport/child/moz.build
new file mode 100644
index 0000000000..dd86788857
--- /dev/null
+++ b/dom/webtransport/child/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+ "WebTransportChild.h",
+]
+
+FINAL_LIBRARY = "xul"
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/dom/webtransport/moz.build b/dom/webtransport/moz.build
new file mode 100644
index 0000000000..971f041ddb
--- /dev/null
+++ b/dom/webtransport/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "api",
+ "child",
+ "parent",
+ "shared",
+ "test",
+]
diff --git a/dom/webtransport/parent/WebTransportParent.cpp b/dom/webtransport/parent/WebTransportParent.cpp
new file mode 100644
index 0000000000..fa84d49803
--- /dev/null
+++ b/dom/webtransport/parent/WebTransportParent.cpp
@@ -0,0 +1,266 @@
+/* -*- 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 "WebTransportParent.h"
+#include "Http3WebTransportSession.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/dom/WebTransportBinding.h"
+#include "mozilla/dom/WebTransportLog.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "nsIEventTarget.h"
+#include "nsIOService.h"
+#include "nsIPrincipal.h"
+#include "nsIWebTransport.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_ISUPPORTS(WebTransportParent, WebTransportSessionEventListener);
+
+using IPCResult = mozilla::ipc::IPCResult;
+using CreateWebTransportPromise =
+ MozPromise<WebTransportReliabilityMode, nsresult, true>;
+WebTransportParent::~WebTransportParent() {
+ LOG(("Destroying WebTransportParent %p", this));
+}
+
+void WebTransportParent::Create(
+ const nsAString& aURL, nsIPrincipal* aPrincipal, const bool& aDedicated,
+ const bool& aRequireUnreliable, const uint32_t& aCongestionControl,
+ // Sequence<WebTransportHash>* aServerCertHashes,
+ Endpoint<PWebTransportParent>&& aParentEndpoint,
+ std::function<void(Tuple<const nsresult&, const uint8_t&>)>&& aResolver) {
+ LOG(("Created WebTransportParent %s %s %s congestion=%s",
+ NS_ConvertUTF16toUTF8(aURL).get(),
+ aDedicated ? "Dedicated" : "AllowPooling",
+ aRequireUnreliable ? "RequireUnreliable" : "",
+ aCongestionControl ==
+ (uint32_t)dom::WebTransportCongestionControl::Throughput
+ ? "ThroughPut"
+ : (aCongestionControl ==
+ (uint32_t)dom::WebTransportCongestionControl::Low_latency
+ ? "Low-Latency"
+ : "Default")));
+
+ if (!StaticPrefs::network_webtransport_enabled()) {
+ aResolver(ResolveType(
+ NS_ERROR_DOM_NOT_ALLOWED_ERR,
+ static_cast<uint8_t>(WebTransportReliabilityMode::Pending)));
+ return;
+ }
+
+ if (!aParentEndpoint.IsValid()) {
+ aResolver(ResolveType(
+ NS_ERROR_INVALID_ARG,
+ static_cast<uint8_t>(WebTransportReliabilityMode::Pending)));
+ return;
+ }
+
+ RefPtr<WebTransportParent> parent = new WebTransportParent();
+ MOZ_ASSERT(parent);
+
+ MOZ_DIAGNOSTIC_ASSERT(mozilla::net::gIOService);
+ nsresult rv = mozilla::net::gIOService->NewWebTransport(
+ getter_AddRefs(parent->mWebTransport));
+ if (NS_FAILED(rv)) {
+ aResolver(ResolveType(
+ rv, static_cast<uint8_t>(WebTransportReliabilityMode::Pending)));
+ return;
+ }
+
+ parent->mOwningEventTarget = GetCurrentEventTarget();
+
+ MOZ_ASSERT(aPrincipal);
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), aURL);
+ if (NS_FAILED(rv)) {
+ aResolver(ResolveType(
+ NS_ERROR_INVALID_ARG,
+ static_cast<uint8_t>(WebTransportReliabilityMode::Pending)));
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "WebTransport AsyncConnect",
+ [self = RefPtr{parent}, uri = std::move(uri),
+ principal = RefPtr{aPrincipal},
+ flags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL] {
+ self->mWebTransport->AsyncConnect(uri, principal, flags, self);
+ });
+
+ // Bind to SocketThread for IPC - connection creation/destruction must
+ // hit MainThread, but keep all other traffic on SocketThread. Note that
+ // we must call aResolver() on this (PBackground) thread.
+ nsCOMPtr<nsISerialEventTarget> sts =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ InvokeAsync(sts, __func__,
+ [parentEndpoint = std::move(aParentEndpoint), runnable = r,
+ resolver = std::move(aResolver), p = RefPtr{parent}]() mutable {
+ p->mResolver = resolver;
+
+ LOG(("Binding parent endpoint"));
+ if (!parentEndpoint.Bind(p)) {
+ return CreateWebTransportPromise::CreateAndReject(
+ NS_ERROR_FAILURE, __func__);
+ }
+ // IPC now holds a ref to parent
+ // Send connection to the server via MainThread
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+ return CreateWebTransportPromise::CreateAndResolve(
+ WebTransportReliabilityMode::Supports_unreliable, __func__);
+ })
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [p = RefPtr{parent}](
+ const CreateWebTransportPromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsReject()) {
+ p->mResolver(ResolveType(
+ aValue.RejectValue(),
+ static_cast<uint8_t>(WebTransportReliabilityMode::Pending)));
+ }
+ });
+}
+
+void WebTransportParent::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("ActorDestroy WebTransportParent %d", aWhy));
+}
+
+// We may not receive this response if the child side is destroyed without
+// `Close` or `Shutdown` being explicitly called.
+mozilla::ipc::IPCResult WebTransportParent::RecvClose(
+ const uint32_t& aCode, const nsACString& aReason) {
+ LOG(("Close received, code = %u, reason = %s", aCode,
+ PromiseFlatCString(aReason).get()));
+ MOZ_ASSERT(!mClosed);
+ mClosed.Flip();
+ nsAutoCString reason(aReason);
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WebTransport Close", [self = RefPtr{this}, aCode, reason] {
+ if (self->mWebTransport) {
+ self->mWebTransport->CloseSession(aCode, reason);
+ }
+ }));
+ Close();
+ return IPC_OK();
+}
+
+// We recieve this notification from the WebTransportSessionProxy if session was
+// successfully created at the end of
+// WebTransportSessionProxy::OnStopRequest
+NS_IMETHODIMP
+WebTransportParent::OnSessionReady(uint64_t aSessionId) {
+ MOZ_ASSERT(mOwningEventTarget);
+ MOZ_ASSERT(!mOwningEventTarget->IsOnCurrentThread());
+
+ LOG(("Created web transport session, sessionID = %" PRIu64 "", aSessionId));
+
+ mOwningEventTarget->Dispatch(NS_NewRunnableFunction(
+ "WebTransportParent::OnSessionReady", [self = RefPtr{this}] {
+ if (!self->IsClosed() && self->mResolver) {
+ self->mResolver(ResolveType(
+ NS_OK, static_cast<uint8_t>(
+ WebTransportReliabilityMode::Supports_unreliable)));
+ self->mResolver = nullptr;
+ }
+ }));
+
+ return NS_OK;
+}
+
+// We recieve this notification from the WebTransportSessionProxy if session
+// creation was unsuccessful at the end of
+// WebTransportSessionProxy::OnStopRequest
+NS_IMETHODIMP
+WebTransportParent::OnSessionClosed(const uint32_t aErrorCode,
+ const nsACString& aReason) {
+ LOG(("Creating web transport session failed code= %u, reason= %s", aErrorCode,
+ PromiseFlatCString(aReason).get()));
+ nsresult rv = NS_OK;
+
+ if (aErrorCode != 0) {
+ // currently we just know if session was closed gracefully or not.
+ // we need better error propagation from lower-levels of http3
+ // webtransport session and it's subsequent error mapping to DOM.
+ // XXX See Bug 1806834
+ rv = NS_ERROR_FAILURE;
+ }
+ MOZ_ASSERT(mOwningEventTarget);
+ MOZ_ASSERT(!mOwningEventTarget->IsOnCurrentThread());
+
+ mOwningEventTarget->Dispatch(NS_NewRunnableFunction(
+ "WebTransportParent::OnSessionClosed",
+ [self = RefPtr{this}, result = rv] {
+ if (!self->IsClosed() && self->mResolver) {
+ self->mResolver(ResolveType(
+ result, static_cast<uint8_t>(
+ WebTransportReliabilityMode::Supports_unreliable)));
+ self->mResolver = nullptr;
+ }
+ }));
+
+ return NS_OK;
+}
+
+// This method is currently not used by WebTransportSessionProxy to inform of
+// any session related events. All notification is recieved via
+// WebTransportSessionProxy::OnSessionReady and
+// WebTransportSessionProxy::OnSessionClosed methods
+NS_IMETHODIMP
+WebTransportParent::OnSessionReadyInternal(
+ mozilla::net::Http3WebTransportSession* aSession) {
+ Unused << aSession;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportParent::OnIncomingStreamAvailableInternal(
+ mozilla::net::Http3WebTransportStream* aStream) {
+ // XXX implement once DOM WebAPI supports creation of streams
+ Unused << aStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportParent::OnIncomingUnidirectionalStreamAvailable(
+ nsIWebTransportReceiveStream* aStream) {
+ // XXX implement once DOM WebAPI supports creation of streams
+ Unused << aStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportParent::OnIncomingBidirectionalStreamAvailable(
+ nsIWebTransportBidirectionalStream* aStream) {
+ // XXX implement once DOM WebAPI supports creation of streams
+ Unused << aStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportParent::OnDatagramReceived(
+ const nsTArray<uint8_t>& aData) {
+ // XXX revisit this function once DOM WebAPI supports datagrams
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportParent::OnDatagramReceivedInternal(
+ nsTArray<uint8_t>&& aData) {
+ // XXX revisit this function once DOM WebAPI supports datagrams
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportParent::OnOutgoingDatagramOutCome(
+ uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) {
+ // XXX revisit this function once DOM WebAPI supports datagrams
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportParent::OnMaxDatagramSize(uint64_t aSize) {
+ // XXX revisit this function once DOM WebAPI supports datagrams
+ return NS_OK;
+}
+} // namespace mozilla::dom
diff --git a/dom/webtransport/parent/WebTransportParent.h b/dom/webtransport/parent/WebTransportParent.h
new file mode 100644
index 0000000000..6ce822e17c
--- /dev/null
+++ b/dom/webtransport/parent/WebTransportParent.h
@@ -0,0 +1,57 @@
+/* -*- 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 DOM_WEBTRANSPORT_PARENT_WEBTRANSPORTPARENT_H_
+#define DOM_WEBTRANSPORT_PARENT_WEBTRANSPORTPARENT_H_
+
+#include "ErrorList.h"
+#include "mozilla/dom/FlippedOnce.h"
+#include "mozilla/dom/PWebTransportParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "nsISupports.h"
+#include "nsIPrincipal.h"
+#include "nsIWebTransport.h"
+
+namespace mozilla::dom {
+
+enum class WebTransportReliabilityMode : uint8_t;
+
+class WebTransportParent : public PWebTransportParent,
+ public WebTransportSessionEventListener {
+ public:
+ WebTransportParent() = default;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENER
+
+ void Create(
+ const nsAString& aURL, nsIPrincipal* aPrincipal, const bool& aDedicated,
+ const bool& aRequireUnreliable, const uint32_t& aCongestionControl,
+ // Sequence<WebTransportHash>* aServerCertHashes,
+ Endpoint<PWebTransportParent>&& aParentEndpoint,
+ std::function<void(Tuple<const nsresult&, const uint8_t&>)>&& aResolver);
+
+ mozilla::ipc::IPCResult RecvClose(const uint32_t& aCode,
+ const nsACString& aReason);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ bool IsClosed() const { return mClosed; }
+
+ protected:
+ virtual ~WebTransportParent();
+
+ private:
+ using ResolveType = Tuple<const nsresult&, const uint8_t&>;
+ std::function<void(ResolveType)> mResolver;
+ FlippedOnce<false> mClosed;
+ nsCOMPtr<nsIWebTransport> mWebTransport;
+ nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+};
+
+} // namespace mozilla::dom
+
+#endif // DOM_WEBTRANSPORT_PARENT_WEBTRANSPORTPARENT_H_
diff --git a/dom/webtransport/parent/moz.build b/dom/webtransport/parent/moz.build
new file mode 100644
index 0000000000..f3486cc8c7
--- /dev/null
+++ b/dom/webtransport/parent/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+ "WebTransportParent.h",
+]
+
+UNIFIED_SOURCES += [
+ "WebTransportParent.cpp",
+]
+
+FINAL_LIBRARY = "xul"
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ "/netwerk/base",
+ "/netwerk/protocol/http",
+]
diff --git a/dom/webtransport/shared/PWebTransport.ipdl b/dom/webtransport/shared/PWebTransport.ipdl
new file mode 100644
index 0000000000..9376e2c511
--- /dev/null
+++ b/dom/webtransport/shared/PWebTransport.ipdl
@@ -0,0 +1,26 @@
+/* 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/. */
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace dom {
+
+async protocol PWebTransport
+{
+ parent:
+ /**
+ * TODO: documentation
+ */
+ async Close(uint32_t code, nsCString reason);
+
+ child:
+
+ async CloseAll()
+ returns(nsresult rv);
+};
+
+} // namespace dom
+} // namespace mozilla
+
diff --git a/dom/webtransport/shared/WebTransportLog.cpp b/dom/webtransport/shared/WebTransportLog.cpp
new file mode 100644
index 0000000000..0193921fec
--- /dev/null
+++ b/dom/webtransport/shared/WebTransportLog.cpp
@@ -0,0 +1,13 @@
+/* -*- 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 "WebTransportLog.h"
+
+namespace mozilla {
+
+LazyLogModule gWebTransportLog("WebTransport");
+
+}
diff --git a/dom/webtransport/shared/WebTransportLog.h b/dom/webtransport/shared/WebTransportLog.h
new file mode 100644
index 0000000000..5bd6da29bd
--- /dev/null
+++ b/dom/webtransport/shared/WebTransportLog.h
@@ -0,0 +1,25 @@
+/* -*- 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 DOM_WEBTRANSPORT_SHARED_WEBTRANSPORTLOG_H_
+#define DOM_WEBTRANSPORT_SHARED_WEBTRANSPORTLOG_H_
+
+#include "mozilla/Logging.h"
+
+namespace mozilla {
+extern LazyLogModule gWebTransportLog;
+}
+
+#define LOG(args) \
+ MOZ_LOG(mozilla::gWebTransportLog, mozilla::LogLevel::Debug, args)
+
+#define LOG_VERBOSE(args) \
+ MOZ_LOG(mozilla::gWebTransportLog, mozilla::LogLevel::Verbose, args)
+
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(mozilla::gWebTransportLog, mozilla::LogLevel::Debug)
+
+#endif // DOM_WEBTRANSPORT_SHARED_WEBTRANSPORTLOG_H
diff --git a/dom/webtransport/shared/moz.build b/dom/webtransport/shared/moz.build
new file mode 100644
index 0000000000..2992b7dbeb
--- /dev/null
+++ b/dom/webtransport/shared/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+ "WebTransportLog.h",
+]
+
+UNIFIED_SOURCES += [
+ "WebTransportLog.cpp",
+]
+
+FINAL_LIBRARY = "xul"
+
+IPDL_SOURCES += [
+ "PWebTransport.ipdl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/dom/webtransport/test/moz.build b/dom/webtransport/test/moz.build
new file mode 100644
index 0000000000..ddf7e1f9a9
--- /dev/null
+++ b/dom/webtransport/test/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+TEST_DIRS += [
+ "xpcshell",
+]
diff --git a/dom/webtransport/test/xpcshell/moz.build b/dom/webtransport/test/xpcshell/moz.build
new file mode 100644
index 0000000000..3d85532034
--- /dev/null
+++ b/dom/webtransport/test/xpcshell/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPCSHELL_TESTS_MANIFESTS += [
+ "xpcshell.ini",
+]
diff --git a/dom/webtransport/test/xpcshell/test_close.js b/dom/webtransport/test/xpcshell/test_close.js
new file mode 100644
index 0000000000..458b933ff3
--- /dev/null
+++ b/dom/webtransport/test/xpcshell/test_close.js
@@ -0,0 +1,64 @@
+//
+// Simple WebTransport test
+//
+// keep eslint happy until it knows about WebTransport
+/* global WebTransport:false */
+
+"use strict";
+
+var h3Port;
+var host;
+
+registerCleanupFunction(async () => {
+ Services.prefs.clearUserPref("network.dns.localDomains");
+});
+
+var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+function readFile(file) {
+ let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ fstream.init(file, -1, 0, 0);
+ let data = NetUtil.readInputStreamToString(fstream, fstream.available());
+ fstream.close();
+ return data;
+}
+
+function addCertFromFile(certdb, filename, trustString) {
+ let certFile = do_get_file(filename, false);
+ let pem = readFile(certFile)
+ .replace(/-----BEGIN CERTIFICATE-----/, "")
+ .replace(/-----END CERTIFICATE-----/, "")
+ .replace(/[\r\n]/g, "");
+ certdb.addCertFromBase64(pem, trustString);
+}
+
+add_task(async function setup() {
+ Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com");
+
+ h3Port = Services.env.get("MOZHTTP3_PORT");
+ Assert.notEqual(h3Port, null);
+ Assert.notEqual(h3Port, "");
+ host = "foo.example.com:" + h3Port;
+ do_get_profile();
+
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ // `../unit/` so that unit_ipc tests can use as well
+ addCertFromFile(
+ certdb,
+ "../../../../netwerk/test/unit/http2-ca.pem",
+ "CTu,u,u"
+ );
+});
+
+add_task(async function test_webtransport_create() {
+ Services.prefs.setBoolPref("network.webtransport.enabled", true);
+
+ const wt = new WebTransport("https://" + host + "/success");
+ await wt.ready;
+
+ wt.close();
+});
diff --git a/dom/webtransport/test/xpcshell/xpcshell.ini b/dom/webtransport/test/xpcshell/xpcshell.ini
new file mode 100644
index 0000000000..816e096aa0
--- /dev/null
+++ b/dom/webtransport/test/xpcshell/xpcshell.ini
@@ -0,0 +1,11 @@
+# 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]
+
+# XXX figure out why android doesn't get MOZHTTP3_PORT
+[test_close.js]
+skip-if =
+ os == 'android' || socketprocess_networking
+ os == 'win' && msix # https://bugzilla.mozilla.org/show_bug.cgi?id=1807925