tor-browser

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

WebTransport.cpp (40072B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "WebTransport.h"
      8 
      9 #include "WebTransportBidirectionalStream.h"
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/RefPtr.h"
     12 #include "mozilla/dom/DOMExceptionBinding.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "mozilla/dom/PWebTransport.h"
     15 #include "mozilla/dom/Promise.h"
     16 #include "mozilla/dom/ReadableStream.h"
     17 #include "mozilla/dom/ReadableStreamDefaultController.h"
     18 #include "mozilla/dom/RemoteWorkerChild.h"
     19 #include "mozilla/dom/WebTransportDatagramDuplexStream.h"
     20 #include "mozilla/dom/WebTransportError.h"
     21 #include "mozilla/dom/WebTransportLog.h"
     22 #include "mozilla/dom/WindowGlobalChild.h"
     23 #include "mozilla/dom/WorkerPrivate.h"
     24 #include "mozilla/dom/WorkerRunnable.h"
     25 #include "mozilla/dom/WritableStream.h"
     26 #include "mozilla/ipc/BackgroundChild.h"
     27 #include "mozilla/ipc/Endpoint.h"
     28 #include "mozilla/ipc/PBackgroundChild.h"
     29 #include "nsIURL.h"
     30 #include "nsIWebTransportStream.h"
     31 #include "nsUTF8Utils.h"
     32 
     33 using namespace mozilla::ipc;
     34 
     35 namespace mozilla::dom {
     36 
     37 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WebTransport)
     38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebTransport)
     39  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
     40  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingUnidirectionalStreams)
     41  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingBidirectionalStreams)
     42  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingUnidirectionalAlgorithm)
     43  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingBidirectionalAlgorithm)
     44  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatagrams)
     45  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady)
     46  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClosed)
     47  for (const auto& hashEntry : tmp->mSendStreams.Values()) {
     48    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSendStreams entry item");
     49    cb.NoteXPCOMChild(hashEntry);
     50  }
     51  for (const auto& hashEntry : tmp->mReceiveStreams.Values()) {
     52    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mReceiveStreams entry item");
     53    cb.NoteXPCOMChild(hashEntry);
     54  }
     55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     56 
     57 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebTransport)
     58  tmp->mSendStreams.Clear();
     59  tmp->mReceiveStreams.Clear();
     60  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
     61  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnidirectionalStreams)
     62  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBidirectionalStreams)
     63  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingUnidirectionalStreams)
     64  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingBidirectionalStreams)
     65  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingUnidirectionalAlgorithm)
     66  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingBidirectionalAlgorithm)
     67  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDatagrams)
     68  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady)
     69  NS_IMPL_CYCLE_COLLECTION_UNLINK(mClosed)
     70  if (tmp->mChild) {
     71    tmp->mChild->Shutdown(false);
     72    tmp->mChild = nullptr;
     73  }
     74  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     75 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     76 
     77 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebTransport)
     78 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebTransport)
     79 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebTransport)
     80  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     81  NS_INTERFACE_MAP_ENTRY(nsISupports)
     82 NS_INTERFACE_MAP_END
     83 
     84 WebTransport::WebTransport(nsIGlobalObject* aGlobal)
     85    : mGlobal(aGlobal),
     86      mState(WebTransportState::CONNECTING),
     87      mReliability(WebTransportReliabilityMode::Pending) {
     88  LOG(("Creating WebTransport %p", this));
     89 }
     90 
     91 WebTransport::~WebTransport() {
     92  // Should be empty by this point, because we should always have run cleanup:
     93  // https://w3c.github.io/webtransport/#webtransport-procedures
     94  LOG(("~WebTransport() for %p", this));
     95  MOZ_ASSERT(mSendStreams.IsEmpty());
     96  MOZ_ASSERT(mReceiveStreams.IsEmpty());
     97  // If this WebTransport was destroyed without being closed properly, make
     98  // sure to clean up the channel.
     99  // Since child has a raw ptr to us, we MUST call Shutdown() before we're
    100  // destroyed
    101  if (mChild) {
    102    mChild->Shutdown(true);
    103  }
    104 }
    105 
    106 // From parent
    107 void WebTransport::NewBidirectionalStream(
    108    uint64_t aStreamId, const RefPtr<DataPipeReceiver>& aIncoming,
    109    const RefPtr<DataPipeSender>& aOutgoing) {
    110  LOG_VERBOSE(("NewBidirectionalStream()"));
    111  // Create a Bidirectional stream and push it into the
    112  // IncomingBidirectionalStreams stream. Must be added to the ReceiveStreams
    113  // and SendStreams arrays
    114 
    115  UniquePtr<BidirectionalPair> streams(
    116      new BidirectionalPair(aIncoming, aOutgoing));
    117  auto tuple = std::tuple<uint64_t, UniquePtr<BidirectionalPair>>(
    118      aStreamId, std::move(streams));
    119  mBidirectionalStreams.AppendElement(std::move(tuple));
    120  // We need to delete them all!
    121 
    122  // Notify something to wake up readers of IncomingReceiveStreams
    123  // The callback is always set/used from the same thread (MainThread or a
    124  // Worker thread).
    125  if (mIncomingBidirectionalAlgorithm) {
    126    RefPtr<WebTransportIncomingStreamsAlgorithms> callback =
    127        mIncomingBidirectionalAlgorithm;
    128    LOG(("NotifyIncomingStream"));
    129    callback->NotifyIncomingStream();
    130  }
    131 }
    132 
    133 void WebTransport::NewUnidirectionalStream(
    134    uint64_t aStreamId, const RefPtr<mozilla::ipc::DataPipeReceiver>& aStream) {
    135  LOG_VERBOSE(("NewUnidirectionalStream()"));
    136  // Create a Unidirectional stream and push it into the
    137  // IncomingUnidirectionalStreams stream. Must be added to the ReceiveStreams
    138  // array
    139 
    140  mUnidirectionalStreams.AppendElement(
    141      std::tuple<uint64_t, RefPtr<mozilla::ipc::DataPipeReceiver>>(aStreamId,
    142                                                                   aStream));
    143  // Notify something to wake up readers of IncomingReceiveStreams
    144  // The callback is always set/used from the same thread (MainThread or a
    145  // Worker thread).
    146  if (mIncomingUnidirectionalAlgorithm) {
    147    RefPtr<WebTransportIncomingStreamsAlgorithms> callback =
    148        mIncomingUnidirectionalAlgorithm;
    149    LOG(("NotifyIncomingStream"));
    150    callback->NotifyIncomingStream();
    151  }
    152 }
    153 
    154 void WebTransport::NewDatagramReceived(nsTArray<uint8_t>&& aData,
    155                                       const mozilla::TimeStamp& aTimeStamp) {
    156  mDatagrams->NewDatagramReceived(std::move(aData), aTimeStamp);
    157 }
    158 
    159 // WebIDL Boilerplate
    160 
    161 nsIGlobalObject* WebTransport::GetParentObject() const { return mGlobal; }
    162 
    163 JSObject* WebTransport::WrapObject(JSContext* aCx,
    164                                   JS::Handle<JSObject*> aGivenProto) {
    165  return WebTransport_Binding::Wrap(aCx, this, aGivenProto);
    166 }
    167 
    168 // WebIDL Interface
    169 
    170 /* static */
    171 already_AddRefed<WebTransport> WebTransport::Constructor(
    172    const GlobalObject& aGlobal, const nsAString& aURL,
    173    const WebTransportOptions& aOptions, ErrorResult& aError) {
    174  LOG(("Creating WebTransport for %s", NS_ConvertUTF16toUTF8(aURL).get()));
    175  // https://w3c.github.io/webtransport/#webtransport-constructor
    176 
    177  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    178  RefPtr<WebTransport> result = new WebTransport(global);
    179  result->Init(aGlobal, aURL, aOptions, aError);
    180  if (aError.Failed()) {
    181    return nullptr;
    182  }
    183 
    184  // Don't let this document go into BFCache
    185  result->NotifyToWindow(true);
    186 
    187  // Step 25 Return transport
    188  return result.forget();
    189 }
    190 
    191 void WebTransport::Init(const GlobalObject& aGlobal, const nsAString& aURL,
    192                        const WebTransportOptions& aOptions,
    193                        ErrorResult& aError) {
    194  // https://w3c.github.io/webtransport/#webtransport-constructor
    195  // Initiate connection with parent
    196  using mozilla::ipc::BackgroundChild;
    197  using mozilla::ipc::Endpoint;
    198  using mozilla::ipc::PBackgroundChild;
    199 
    200  // https://w3c.github.io/webtransport/#webtransport-constructor
    201  // Steps 1-4: Parse string for validity and Throw a SyntaxError if it isn't
    202  // Let parsedURL be the URL record resulting from parsing url.
    203  // If parsedURL is a failure, throw a SyntaxError exception.
    204  // If parsedURL scheme is not https, throw a SyntaxError exception.
    205  // If parsedURL fragment is not null, throw a SyntaxError exception.
    206  if (!ParseURL(aURL)) {
    207    aError.ThrowSyntaxError("Invalid WebTransport URL");
    208    return;
    209  }
    210  // Step 5: Let allowPooling be options's allowPooling if it exists, and false
    211  // otherwise.
    212  // Step 6: Let dedicated be the negation of allowPooling.
    213  bool dedicated = !aOptions.mAllowPooling;
    214  // Step 7: Let serverCertificateHashes be options's serverCertificateHashes if
    215  // it exists, and null otherwise.
    216  // Step 8: If dedicated is false and serverCertificateHashes is non-null,
    217  // then throw a TypeError.
    218  nsTArray<mozilla::ipc::WebTransportHash> aServerCertHashes;
    219  if (aOptions.mServerCertificateHashes.WasPassed()) {
    220    if (!dedicated) {
    221      aError.ThrowNotSupportedError(
    222          "serverCertificateHashes not supported for non-dedicated "
    223          "connections");
    224      return;
    225    }
    226    for (const auto& hash : aOptions.mServerCertificateHashes.Value()) {
    227      if (!hash.mAlgorithm.WasPassed() || !hash.mValue.WasPassed()) continue;
    228 
    229      if (hash.mAlgorithm.Value() != u"sha-256") {
    230        LOG(("Algorithms other than SHA-256 are not supported"));
    231        continue;
    232      }
    233 
    234      nsTArray<uint8_t> data;
    235      if (!AppendTypedArrayDataTo(hash.mValue.Value(), data)) {
    236        aError.Throw(NS_ERROR_OUT_OF_MEMORY);
    237        return;
    238      }
    239 
    240      nsCString alg = NS_ConvertUTF16toUTF8(hash.mAlgorithm.Value());
    241      aServerCertHashes.EmplaceBack(alg, data);
    242    }
    243  }
    244  // Step 9: Let requireUnreliable be options's requireUnreliable.
    245  bool requireUnreliable = aOptions.mRequireUnreliable;
    246  // Step 10: Let congestionControl be options's congestionControl.
    247  // Step 11: If congestionControl is not "default", and the user agent
    248  // does not support any congestion control algorithms that optimize for
    249  // congestionControl, as allowed by [RFC9002] section 7, then set
    250  // congestionControl to "default".
    251  WebTransportCongestionControl congestionControl =
    252      WebTransportCongestionControl::Default;  // aOptions.mCongestionControl;
    253  // Set this to 'default' until we add congestion control setting
    254 
    255  // Setup up WebTransportDatagramDuplexStream
    256  // Step 12: Let incomingDatagrams be a new ReadableStream.
    257  // Step 13: Let outgoingDatagrams be a new WritableStream.
    258  // Step 14: Let datagrams be the result of creating a
    259  // WebTransportDatagramDuplexStream, its readable set to
    260  // incomingDatagrams and its writable set to outgoingDatagrams.
    261  mDatagrams = new WebTransportDatagramDuplexStream(mGlobal, this);
    262  mDatagrams->Init(aError);
    263  if (aError.Failed()) {
    264    return;
    265  }
    266 
    267  // TODO: Fix the shared and service worker use cases
    268  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    269  mService = workerPrivate && (workerPrivate->IsSharedWorker() ||
    270                               workerPrivate->IsServiceWorker())
    271                 ? nullptr
    272                 : net::WebTransportEventService::GetOrCreate();
    273  // XXX TODO
    274 
    275  // Step 15 Let transport be a newly constructed WebTransport object, with:
    276  // SendStreams: empty ordered set
    277  // ReceiveStreams: empty ordered set
    278  // Ready: new promise
    279  mReady = Promise::CreateInfallible(mGlobal);
    280 
    281  // Closed: new promise
    282  mClosed = Promise::CreateInfallible(mGlobal);
    283 
    284  PBackgroundChild* backgroundChild =
    285      BackgroundChild::GetOrCreateForCurrentThread();
    286  if (NS_WARN_IF(!backgroundChild)) {
    287    aError.Throw(NS_ERROR_FAILURE);
    288    return;
    289  }
    290 
    291  nsCOMPtr<nsIPrincipal> principal = mGlobal->PrincipalOrNull();
    292  mozilla::Maybe<IPCClientInfo> ipcClientInfo;
    293 
    294  if (mGlobal->GetClientInfo().isSome()) {
    295    ipcClientInfo = mozilla::Some(mGlobal->GetClientInfo().ref().ToIPC());
    296  }
    297 
    298  nsPIDOMWindowInner* window = mGlobal->GetAsInnerWindow();
    299  if (window) {
    300    mBrowsingContextID = window->GetBrowsingContext()->Id();
    301  }
    302  // Create a new IPC connection
    303  Endpoint<PWebTransportParent> parentEndpoint;
    304  Endpoint<PWebTransportChild> childEndpoint;
    305  MOZ_ALWAYS_SUCCEEDS(
    306      PWebTransport::CreateEndpoints(&parentEndpoint, &childEndpoint));
    307 
    308  RefPtr<WebTransportChild> child = new WebTransportChild(this);
    309  if (NS_IsMainThread()) {
    310    if (!childEndpoint.Bind(child)) {
    311      aError.Throw(NS_ERROR_FAILURE);
    312      return;
    313    }
    314  } else if (!childEndpoint.Bind(child, mGlobal->SerialEventTarget())) {
    315    aError.Throw(NS_ERROR_FAILURE);
    316    return;
    317  }
    318 
    319  mState = WebTransportState::CONNECTING;
    320 
    321  JSContext* cx = aGlobal.Context();
    322  // Set up Datagram streams
    323  // Step 16: Let pullDatagramsAlgorithm be an action that runs pullDatagrams
    324  // with transport.
    325  // Step 17: Let writeDatagramsAlgorithm be an action that runs writeDatagrams
    326  // with transport.
    327  // Step 18: Set up incomingDatagrams with pullAlgorithm set to
    328  // pullDatagramsAlgorithm, and highWaterMark set to 0.
    329  // Step 19: Set up outgoingDatagrams with writeAlgorithm set to
    330  // writeDatagramsAlgorithm.
    331 
    332  // XXX TODO
    333 
    334  // Step 20: Let pullBidirectionalStreamAlgorithm be an action that runs
    335  // pullBidirectionalStream with transport.
    336  // Step 21: Set up transport.[[IncomingBidirectionalStreams]] with
    337  // pullAlgorithm set to pullBidirectionalStreamAlgorithm, and highWaterMark
    338  // set to 0.
    339  Optional<JS::Handle<JSObject*>> underlying;
    340  // Suppress warnings about risk of mGlobal getting nulled during script.
    341  // We set the global from the aGlobalObject parameter of the constructor, so
    342  // it must still be set here.
    343  const nsCOMPtr<nsIGlobalObject> global(mGlobal);
    344 
    345  mIncomingBidirectionalAlgorithm = new WebTransportIncomingStreamsAlgorithms(
    346      WebTransportIncomingStreamsAlgorithms::StreamType::Bidirectional, this);
    347 
    348  RefPtr<WebTransportIncomingStreamsAlgorithms> algorithm =
    349      mIncomingBidirectionalAlgorithm;
    350  mIncomingBidirectionalStreams = ReadableStream::CreateNative(
    351      cx, global, *algorithm, Some(0.0), nullptr, aError);
    352  if (aError.Failed()) {
    353    return;
    354  }
    355  // Step 22: Let pullUnidirectionalStreamAlgorithm be an action that runs
    356  // pullUnidirectionalStream with transport.
    357  // Step 23: Set up transport.[[IncomingUnidirectionalStreams]] with
    358  // pullAlgorithm set to pullUnidirectionalStreamAlgorithm, and highWaterMark
    359  // set to 0.
    360 
    361  mIncomingUnidirectionalAlgorithm = new WebTransportIncomingStreamsAlgorithms(
    362      WebTransportIncomingStreamsAlgorithms::StreamType::Unidirectional, this);
    363 
    364  algorithm = mIncomingUnidirectionalAlgorithm;
    365  mIncomingUnidirectionalStreams = ReadableStream::CreateNative(
    366      cx, global, *algorithm, Some(0.0), nullptr, aError);
    367  if (aError.Failed()) {
    368    return;
    369  }
    370 
    371  // Step 24: Initialize WebTransport over HTTP with transport, parsedURL,
    372  // dedicated, requireUnreliable, and congestionControl.
    373  LOG(("Connecting WebTransport to parent for %s",
    374       NS_ConvertUTF16toUTF8(aURL).get()));
    375 
    376  // https://w3c.github.io/webtransport/#webtransport-constructor Spec 5.2
    377  mChild = child;
    378  backgroundChild
    379      ->SendCreateWebTransportParent(
    380          aURL, principal, mBrowsingContextID, ipcClientInfo, dedicated,
    381          requireUnreliable, (uint32_t)congestionControl,
    382          std::move(aServerCertHashes), std::move(parentEndpoint))
    383      ->Then(GetCurrentSerialEventTarget(), __func__,
    384             [self = RefPtr{this}](
    385                 PBackgroundChild::CreateWebTransportParentPromise::
    386                     ResolveOrRejectValue&& aResult) {
    387               // aResult is a std::tuple<nsresult, uint8_t>
    388               // TODO: is there a better/more-spec-compliant error in the
    389               // reject case? Which begs the question, why would we get a
    390               // reject?
    391               nsresult rv = aResult.IsReject()
    392                                 ? NS_ERROR_FAILURE
    393                                 : std::get<0>(aResult.ResolveValue());
    394               LOG(("isreject: %d nsresult 0x%x", aResult.IsReject(),
    395                    (uint32_t)rv));
    396               if (NS_FAILED(rv)) {
    397                 self->RejectWaitingConnection(rv);
    398               } else {
    399                 // This will process anything waiting for the connection to
    400                 // complete;
    401 
    402                 self->ResolveWaitingConnection(
    403                     static_cast<WebTransportReliabilityMode>(
    404                         std::get<1>(aResult.ResolveValue())));
    405               }
    406             });
    407 }
    408 
    409 void WebTransport::ResolveWaitingConnection(
    410    WebTransportReliabilityMode aReliability) {
    411  LOG(("Resolved Connection %p, reliability = %u", this,
    412       (unsigned)aReliability));
    413  // https://w3c.github.io/webtransport/#webtransport-constructor
    414  // Step 17 of  initialize WebTransport over HTTP
    415  // Step 17.1 If transport.[[State]] is not "connecting":
    416  if (mState != WebTransportState::CONNECTING) {
    417    // Step 17.1.1: In parallel, terminate session.
    418    // Step 17.1.2: abort these steps
    419    // Cleanup should have been called, which means Ready has been rejected
    420    return;
    421  }
    422 
    423  // Step 17.2: Set transport.[[State]] to "connected".
    424  mState = WebTransportState::CONNECTED;
    425  // Step 17.3: Set transport.[[Session]] to session.
    426  // Step 17.4: Set transport’s [[Reliability]] to "supports-unreliable".
    427  mReliability = aReliability;
    428  if (NS_IsMainThread()) {
    429    nsPIDOMWindowInner* innerWindow = GetParentObject()->GetAsInnerWindow();
    430    if (innerWindow) {
    431      mInnerWindowID = innerWindow->WindowID();
    432    }
    433  } else {
    434    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    435    if (workerPrivate->IsDedicatedWorker()) {
    436      mInnerWindowID = workerPrivate->WindowID();
    437    }
    438  }
    439 
    440  mChild->SendGetMaxDatagramSize()->Then(
    441      GetCurrentSerialEventTarget(), __func__,
    442      [self = RefPtr{this}](uint64_t&& aMaxDatagramSize) {
    443        MOZ_ASSERT(self->mDatagrams);
    444        self->mDatagrams->SetMaxDatagramSize(aMaxDatagramSize);
    445        LOG(("max datagram size for the session is %" PRIu64,
    446             aMaxDatagramSize));
    447      },
    448      [](const mozilla::ipc::ResponseRejectReason& aReason) {
    449        LOG(("WebTransport fetching maxDatagramSize failed"));
    450      });
    451 
    452  // Step 17.5: Resolve transport.[[Ready]] with undefined.
    453  mReady->MaybeResolveWithUndefined();
    454 
    455  // We can now release any queued datagrams
    456  mDatagrams->SetChild(mChild);
    457 
    458  if (mInnerWindowID != 0) {
    459    // Get the http chanel created for the web transport session;
    460    mChild->SendGetHttpChannelID()->Then(
    461        GetCurrentSerialEventTarget(), __func__,
    462        [self = RefPtr{this}](uint64_t&& aHttpChannelId) {
    463          MOZ_ASSERT(self->mService);
    464          self->mHttpChannelID = aHttpChannelId;
    465          self->mService->WebTransportSessionCreated(self->mInnerWindowID,
    466                                                     aHttpChannelId);
    467        },
    468        [](const mozilla::ipc::ResponseRejectReason& aReason) {
    469          LOG(("WebTransport fetching the channel information failed "));
    470        });
    471  }
    472 }
    473 
    474 void WebTransport::RejectWaitingConnection(nsresult aRv) {
    475  LOG(("Rejected connection %p %x", this, (uint32_t)aRv));
    476  // https://w3c.github.io/webtransport/#initialize-webtransport-over-http
    477 
    478  // Step 10: If connection is failure, then abort the remaining steps and
    479  // queue a network task with transport to run these steps:
    480  // Step 10.1: If transport.[[State]] is "closed" or "failed", then abort
    481  // these steps.
    482 
    483  // Step 14: If the previous step fails, abort the remaining steps and
    484  // queue a network task with transport to run these steps:
    485  // Step 14.1: If transport.[[State]] is "closed" or "failed", then abort
    486  // these steps.
    487  if (mState == WebTransportState::CLOSED ||
    488      mState == WebTransportState::FAILED) {
    489    if (mChild) {
    490      mChild->Shutdown(true);
    491      mChild = nullptr;
    492    }
    493    // Cleanup should have been called, which means Ready has been
    494    // rejected and pulls resolved
    495    return;
    496  }
    497 
    498  // Step 14.2: Let error be the result of creating a WebTransportError with
    499  // "session".
    500  RefPtr<WebTransportError> error = new WebTransportError(
    501      "WebTransport connection rejected"_ns, WebTransportErrorSource::Session);
    502  // Step 14.3: Cleanup transport with error.
    503  Cleanup(error, nullptr, IgnoreErrors());
    504 
    505  mChild->Shutdown(true);
    506  mChild = nullptr;
    507 }
    508 
    509 bool WebTransport::ParseURL(const nsAString& aURL) const {
    510  NS_ENSURE_TRUE(!aURL.IsEmpty(), false);
    511 
    512  // 5.4 = https://w3c.github.io/webtransport/#webtransport-constructor
    513  // 5.4 #1 and #2
    514  nsCOMPtr<nsIURI> uri;
    515  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL);
    516  NS_ENSURE_SUCCESS(rv, false);
    517 
    518  // 5.4 #3
    519  if (!uri->SchemeIs("https")) {
    520    return false;
    521  }
    522 
    523  // 5.4 #4 no fragments
    524  bool hasRef;
    525  rv = uri->GetHasRef(&hasRef);
    526  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !hasRef, false);
    527 
    528  return true;
    529 }
    530 
    531 already_AddRefed<Promise> WebTransport::GetStats(ErrorResult& aError) {
    532  aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
    533  return nullptr;
    534 }
    535 
    536 WebTransportReliabilityMode WebTransport::Reliability() { return mReliability; }
    537 
    538 WebTransportCongestionControl WebTransport::CongestionControl() {
    539  // XXX not implemented
    540  return WebTransportCongestionControl::Default;
    541 }
    542 
    543 void WebTransport::RemoteClosed(bool aCleanly, const uint32_t& aCode,
    544                                const nsACString& aReason) {
    545  LOG(("Server closed: cleanly: %d, code %u, reason %s", aCleanly, aCode,
    546       PromiseFlatCString(aReason).get()));
    547  // Step 2 of https://w3c.github.io/webtransport/#web-transport-termination
    548  // We calculate cleanly on the parent
    549  // Step 2.1: If transport.[[State]] is "closed" or "failed", abort these
    550  // steps.
    551  if (mState == WebTransportState::CLOSED ||
    552      mState == WebTransportState::FAILED) {
    553    return;
    554  }
    555  // Step 2.2: Let error be the result of creating a WebTransportError with
    556  // "session".
    557  RefPtr<WebTransportError> error = new WebTransportError(
    558      "remote WebTransport close"_ns, WebTransportErrorSource::Session);
    559  // Step 2.3: If cleanly is false, then cleanup transport with error, and
    560  // abort these steps.
    561  ErrorResult errorresult;
    562  if (!aCleanly) {
    563    Cleanup(error, nullptr, errorresult);
    564    return;
    565  }
    566  // Step 2.4: Let closeInfo be a new WebTransportCloseInfo.
    567  // Step 2.5: If code is given, set closeInfo’s closeCode to code.
    568  // Step 2.6: If reasonBytes is given, set closeInfo’s reason to reasonBytes,
    569  // UTF-8 decoded.
    570  WebTransportCloseInfo closeinfo;
    571  closeinfo.mCloseCode = aCode;
    572  closeinfo.mReason = aReason;
    573 
    574  // Step 2.7: Cleanup transport with error and closeInfo.
    575  Cleanup(error, &closeinfo, errorresult);
    576 }
    577 
    578 template <typename Stream>
    579 void WebTransport::PropagateError(Stream* aStream, WebTransportError* aError) {
    580  ErrorResult rv;
    581  AutoJSAPI jsapi;
    582  if (!jsapi.Init(mGlobal)) {
    583    rv.ThrowUnknownError("Internal error");
    584    return;
    585  }
    586  JSContext* cx = jsapi.cx();
    587  JS::Rooted<JS::Value> errorValue(cx);
    588  bool ok = ToJSValue(cx, aError, &errorValue);
    589  if (!ok) {
    590    rv.ThrowUnknownError("Internal error");
    591    return;
    592  }
    593 
    594  aStream->ErrorNative(cx, errorValue, IgnoreErrors());
    595 }
    596 
    597 void WebTransport::OnStreamResetOrStopSending(
    598    uint64_t aStreamId, const StreamResetOrStopSendingError& aError) {
    599  LOG(("WebTransport::OnStreamResetOrStopSending %p id=%" PRIx64, this,
    600       aStreamId));
    601  if (aError.type() == StreamResetOrStopSendingError::TStopSendingError) {
    602    RefPtr<WebTransportSendStream> stream = mSendStreams.Get(aStreamId);
    603    if (!stream) {
    604      return;
    605    }
    606    uint8_t errorCode = net::GetWebTransportErrorFromNSResult(
    607        aError.get_StopSendingError().error());
    608    RefPtr<WebTransportError> error = new WebTransportError(
    609        "WebTransportStream StopSending"_ns, WebTransportErrorSource::Stream,
    610        Nullable<uint8_t>(errorCode));
    611    PropagateError(stream.get(), error);
    612  } else if (aError.type() == StreamResetOrStopSendingError::TResetError) {
    613    RefPtr<WebTransportReceiveStream> stream = mReceiveStreams.Get(aStreamId);
    614    LOG(("WebTransport::OnStreamResetOrStopSending reset %p stream=%p", this,
    615         stream.get()));
    616    if (!stream) {
    617      return;
    618    }
    619    uint8_t errorCode =
    620        net::GetWebTransportErrorFromNSResult(aError.get_ResetError().error());
    621    RefPtr<WebTransportError> error = new WebTransportError(
    622        "WebTransportStream Reset"_ns, WebTransportErrorSource::Stream,
    623        Nullable<uint8_t>(errorCode));
    624    PropagateError(stream.get(), error);
    625  }
    626 }
    627 
    628 void WebTransport::Close(const WebTransportCloseInfo& aOptions,
    629                         ErrorResult& aRv) {
    630  LOG(("Close() called"));
    631  // https://w3c.github.io/webtransport/#dom-webtransport-close
    632  // Step 1 and Step 2: If transport.[[State]] is "closed" or "failed", then
    633  // abort these steps.
    634  if (mState == WebTransportState::CLOSED ||
    635      mState == WebTransportState::FAILED) {
    636    return;
    637  }
    638  // Step 3: If transport.[[State]] is "connecting":
    639  if (mState == WebTransportState::CONNECTING) {
    640    // Step 3.1: Let error be the result of creating a WebTransportError with
    641    // "session".
    642    RefPtr<WebTransportError> error = new WebTransportError(
    643        "close() called on WebTransport while connecting"_ns,
    644        WebTransportErrorSource::Session);
    645    // Step 3.2: Cleanup transport with error.
    646    Cleanup(error, nullptr, aRv);
    647    // Step 3.3: Abort these steps.
    648    mChild->Shutdown(true);
    649    mChild = nullptr;
    650    return;
    651  }
    652  LOG(("Sending Close"));
    653  MOZ_ASSERT(mChild);
    654  // Step 4: Let session be transport.[[Session]].
    655  // Step 5: Let code be closeInfo.closeCode.
    656  // Step 6: "Let reasonString be the maximal code unit prefix of
    657  // closeInfo.reason where the length of the UTF-8 encoded prefix
    658  // doesn’t exceed 1024."
    659  // Take the maximal "code unit prefix" of mReason and limit to 1024 bytes
    660  // Step 7: Let reason be reasonString, UTF-8 encoded.
    661  // Step 8: In parallel, terminate session with code and reason.
    662  if (aOptions.mReason.Length() > 1024u) {
    663    // We want to start looking for the previous code point at one past the
    664    // limit, since if a code point ends exactly at the specified length, the
    665    // next byte will be the start of a new code point. Note
    666    // RewindToPriorUTF8Codepoint doesn't reduce the index if it points to the
    667    // start of a code point. We know reason[1024] is accessible since
    668    // Length() > 1024
    669    mChild->SendClose(
    670        aOptions.mCloseCode,
    671        Substring(aOptions.mReason, 0,
    672                  RewindToPriorUTF8Codepoint(aOptions.mReason.get(), 1024u)));
    673  } else {
    674    mChild->SendClose(aOptions.mCloseCode, aOptions.mReason);
    675    LOG(("Close sent"));
    676  }
    677 
    678  // Step 9: Cleanup transport with AbortError and closeInfo. (sets mState to
    679  // Closed)
    680  RefPtr<WebTransportError> error =
    681      new WebTransportError("close()"_ns, WebTransportErrorSource::Session,
    682                            DOMException_Binding::ABORT_ERR);
    683  Cleanup(error, &aOptions, aRv);
    684  LOG(("Cleanup done"));
    685 
    686  // The other side will call `Close()` for us now, make sure we don't call it
    687  // in our destructor.
    688  mChild->Shutdown(false);
    689  mChild = nullptr;
    690  LOG(("Close done"));
    691 }
    692 
    693 already_AddRefed<WebTransportDatagramDuplexStream> WebTransport::GetDatagrams(
    694    ErrorResult& aError) {
    695  return do_AddRef(mDatagrams);
    696 }
    697 
    698 already_AddRefed<Promise> WebTransport::CreateBidirectionalStream(
    699    const WebTransportSendStreamOptions& aOptions, ErrorResult& aRv) {
    700  LOG(("CreateBidirectionalStream() called"));
    701  // https://w3c.github.io/webtransport/#dom-webtransport-createbidirectionalstream
    702  RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
    703 
    704  // Step 2: If transport.[[State]] is "closed" or "failed", return a new
    705  // rejected promise with an InvalidStateError.
    706  if (mState == WebTransportState::CLOSED ||
    707      mState == WebTransportState::FAILED || !mChild) {
    708    aRv.ThrowInvalidStateError("WebTransport closed or failed");
    709    return nullptr;
    710  }
    711 
    712  // Step 3: Let sendOrder be options's sendOrder.
    713  Maybe<int64_t> sendOrder;
    714  if (!aOptions.mSendOrder.IsNull()) {
    715    sendOrder = Some(aOptions.mSendOrder.Value());
    716  }
    717  // Step 4: Let p be a new promise.
    718  // Step 5: Run the following steps in parallel, but abort them whenever
    719  // transport’s [[State]] becomes "closed" or "failed", and instead queue
    720  // a network task with transport to reject p with an InvalidStateError.
    721 
    722  // Ask the parent to create the stream and send us the DataPipeSender/Receiver
    723  // pair
    724  mChild->SendCreateBidirectionalStream(
    725      sendOrder,
    726      [self = RefPtr{this}, sendOrder, promise](
    727          BidirectionalStreamResponse&& aPipes) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    728        LOG(("CreateBidirectionalStream response"));
    729        if (BidirectionalStreamResponse::Tnsresult == aPipes.type()) {
    730          promise->MaybeReject(aPipes.get_nsresult());
    731          return;
    732        }
    733        // Step 5.2.1: If transport.[[State]] is "closed" or "failed",
    734        // reject p with an InvalidStateError and abort these steps.
    735        if (BidirectionalStreamResponse::Tnsresult == aPipes.type()) {
    736          promise->MaybeReject(aPipes.get_nsresult());
    737          return;
    738        }
    739        if (self->mState == WebTransportState::CLOSED ||
    740            self->mState == WebTransportState::FAILED) {
    741          promise->MaybeRejectWithInvalidStateError(
    742              "Transport close/errored before CreateBidirectional finished");
    743          return;
    744        }
    745        uint64_t id = aPipes.get_BidirectionalStream().streamId();
    746        LOG(("Create WebTransportBidirectionalStream id=%" PRIx64, id));
    747        ErrorResult error;
    748        RefPtr<WebTransportBidirectionalStream> newStream =
    749            WebTransportBidirectionalStream::Create(
    750                self, self->mGlobal, id,
    751                aPipes.get_BidirectionalStream().inStream(),
    752                aPipes.get_BidirectionalStream().outStream(), sendOrder, error);
    753        LOG(("Returning a bidirectionalStream"));
    754        promise->MaybeResolve(newStream);
    755      },
    756      [self = RefPtr{this}, promise](mozilla::ipc::ResponseRejectReason) {
    757        LOG(("CreateBidirectionalStream reject"));
    758        promise->MaybeRejectWithInvalidStateError(
    759            "Transport close/errored before CreateBidirectional started");
    760      });
    761 
    762  // Step 6: return p
    763  return promise.forget();
    764 }
    765 
    766 already_AddRefed<ReadableStream> WebTransport::IncomingBidirectionalStreams() {
    767  return do_AddRef(mIncomingBidirectionalStreams);
    768 }
    769 
    770 already_AddRefed<Promise> WebTransport::CreateUnidirectionalStream(
    771    const WebTransportSendStreamOptions& aOptions, ErrorResult& aRv) {
    772  LOG(("CreateUnidirectionalStream() called"));
    773  // https://w3c.github.io/webtransport/#dom-webtransport-createunidirectionalstream
    774  // Step 2: If transport.[[State]] is "closed" or "failed", return a new
    775  // rejected promise with an InvalidStateError.
    776  if (mState == WebTransportState::CLOSED ||
    777      mState == WebTransportState::FAILED || !mChild) {
    778    aRv.ThrowInvalidStateError("WebTransport closed or failed");
    779    return nullptr;
    780  }
    781 
    782  // Step 3: Let sendOrder be options's sendOrder.
    783  Maybe<int64_t> sendOrder;
    784  if (!aOptions.mSendOrder.IsNull()) {
    785    sendOrder = Some(aOptions.mSendOrder.Value());
    786  }
    787  // Step 4: Let p be a new promise.
    788  RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
    789 
    790  // Step 5: Run the following steps in parallel, but abort them whenever
    791  // transport’s [[State]] becomes "closed" or "failed", and instead queue
    792  // a network task with transport to reject p with an InvalidStateError.
    793 
    794  // Ask the parent to create the stream and send us the DataPipeSender
    795  mChild->SendCreateUnidirectionalStream(
    796      sendOrder,
    797      [self = RefPtr{this}, sendOrder,
    798       promise](UnidirectionalStreamResponse&& aResponse)
    799          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    800            LOG(("CreateUnidirectionalStream response"));
    801            if (UnidirectionalStreamResponse::Tnsresult == aResponse.type()) {
    802              promise->MaybeReject(aResponse.get_nsresult());
    803              return;
    804            }
    805            // Step 5.1: Let internalStream be the result of creating an
    806            // outgoing unidirectional stream with transport.[[Session]].
    807            // Step 5.2: Queue a network task with transport to run the
    808            // following steps:
    809            // Step 5.2.1 If transport.[[State]] is "closed" or "failed",
    810            // reject p with an InvalidStateError and abort these steps.
    811            if (self->mState == WebTransportState::CLOSED ||
    812                self->mState == WebTransportState::FAILED ||
    813                aResponse.type() !=
    814                    UnidirectionalStreamResponse::TUnidirectionalStream) {
    815              promise->MaybeRejectWithInvalidStateError(
    816                  "Transport close/errored during CreateUnidirectional");
    817              return;
    818            }
    819 
    820            // Step 5.2.2.: Let stream be the result of creating a
    821            // WebTransportSendStream with internalStream, transport, and
    822            // sendOrder.
    823            ErrorResult error;
    824            uint64_t id = aResponse.get_UnidirectionalStream().streamId();
    825            LOG(("Create WebTransportSendStream id=%" PRIx64, id));
    826            RefPtr<WebTransportSendStream> writableStream =
    827                WebTransportSendStream::Create(
    828                    self, self->mGlobal, id,
    829                    aResponse.get_UnidirectionalStream().outStream(), sendOrder,
    830                    error);
    831            if (!writableStream) {
    832              promise->MaybeReject(std::move(error));
    833              return;
    834            }
    835            LOG(("Returning a writableStream"));
    836            // Step 5.2.3: Resolve p with stream.
    837            promise->MaybeResolve(writableStream);
    838          },
    839      [self = RefPtr{this}, promise](mozilla::ipc::ResponseRejectReason) {
    840        LOG(("CreateUnidirectionalStream reject"));
    841        promise->MaybeRejectWithInvalidStateError(
    842            "Transport close/errored during CreateUnidirectional");
    843      });
    844 
    845  // Step 6: return p
    846  return promise.forget();
    847 }
    848 
    849 already_AddRefed<ReadableStream> WebTransport::IncomingUnidirectionalStreams() {
    850  return do_AddRef(mIncomingUnidirectionalStreams);
    851 }
    852 
    853 // Can be invoked with "error", "error, error, and true/false", or "error and
    854 // closeInfo", but reason and abruptly are never used, and it does use closeinfo
    855 void WebTransport::Cleanup(WebTransportError* aError,
    856                           const WebTransportCloseInfo* aCloseInfo,
    857                           ErrorResult& aRv) {
    858  // https://w3c.github.io/webtransport/#webtransport-cleanup
    859  // Step 1: Let sendStreams be a copy of transport.[[SendStreams]]
    860  // Step 2: Let receiveStreams be a copy of transport.[[ReceiveStreams]]
    861  // Step 3: Let ready be transport.[[Ready]]  -> (mReady)
    862  // Step 4: Let closed be transport.[[Closed]] -> (mClosed)
    863  // Step 5: Let incomingBidirectionalStreams be
    864  // transport.[[IncomingBidirectionalStreams]].
    865  // Step 6: Let incomingUnidirectionalStreams be
    866  // transport.[[IncomingUnidirectionalStreams]].
    867  // Step 7: Set transport.[[SendStreams]] to an empty set.
    868  // Step 8: Set transport.[[ReceiveStreams]] to an empty set.
    869  LOG(("Cleanup started"));
    870  nsTHashMap<uint64_t, RefPtr<WebTransportSendStream>> sendStreams;
    871  sendStreams.SwapElements(mSendStreams);
    872  nsTHashMap<uint64_t, RefPtr<WebTransportReceiveStream>> receiveStreams;
    873  receiveStreams.SwapElements(mReceiveStreams);
    874 
    875  // Step 9: If closeInfo is given, then set transport.[[State]] to "closed".
    876  // Otherwise, set transport.[[State]] to "failed".
    877  mState = aCloseInfo ? WebTransportState::CLOSED : WebTransportState::FAILED;
    878 
    879  // Notify all the listeners of the closed session
    880  if (aCloseInfo && mInnerWindowID != 0) {
    881    mService->WebTransportSessionClosed(
    882        mInnerWindowID, mHttpChannelID, aCloseInfo->mCloseCode,
    883        NS_ConvertUTF8toUTF16(aCloseInfo->mReason));
    884  }
    885 
    886  // Step 10: For each sendStream in sendStreams, error sendStream with error.
    887  AutoJSAPI jsapi;
    888  if (!jsapi.Init(mGlobal)) {
    889    aRv.ThrowUnknownError("Internal error");
    890    return;
    891  }
    892  JSContext* cx = jsapi.cx();
    893  JS::Rooted<JS::Value> errorValue(cx);
    894  bool ok = ToJSValue(cx, aError, &errorValue);
    895  if (!ok) {
    896    aRv.ThrowUnknownError("Internal error");
    897    return;
    898  }
    899 
    900  for (const auto& stream : sendStreams.Values()) {
    901    // This MOZ_KnownLive is redundant, see bug 1620312
    902    MOZ_KnownLive(stream)->ErrorNative(cx, errorValue, IgnoreErrors());
    903  }
    904  // Step 11: For each receiveStream in receiveStreams, error receiveStream with
    905  // error.
    906  for (const auto& stream : receiveStreams.Values()) {
    907    stream->ErrorNative(cx, errorValue, IgnoreErrors());
    908  }
    909  // Step 12:
    910  if (aCloseInfo) {
    911    // 12.1: Resolve closed with closeInfo.
    912    LOG(("Resolving mClosed with closeinfo"));
    913    mClosed->MaybeResolve(*aCloseInfo);
    914    // 12.2: Assert: ready is settled.
    915    MOZ_ASSERT(mReady->State() != Promise::PromiseState::Pending);
    916    // 12.3: Close incomingBidirectionalStreams
    917    // This keeps the clang-plugin happy
    918    RefPtr<ReadableStream> stream = mIncomingBidirectionalStreams;
    919    stream->CloseNative(cx, IgnoreErrors());
    920    // 12.4: Close incomingUnidirectionalStreams
    921    stream = mIncomingUnidirectionalStreams;
    922    stream->CloseNative(cx, IgnoreErrors());
    923  } else {
    924    // Step 13
    925    // 13.1: Reject closed with error
    926    LOG(("Rejecting mClosed"));
    927    mClosed->MaybeReject(errorValue);
    928    // 13.2: Reject ready with error
    929    mReady->MaybeReject(errorValue);
    930    // 13.3: Error incomingBidirectionalStreams with error
    931    mIncomingBidirectionalStreams->ErrorNative(cx, errorValue, IgnoreErrors());
    932    // 13.4: Error incomingUnidirectionalStreams with error
    933    mIncomingUnidirectionalStreams->ErrorNative(cx, errorValue, IgnoreErrors());
    934  }
    935  // Let go of the algorithms
    936  mIncomingBidirectionalAlgorithm = nullptr;
    937  mIncomingUnidirectionalAlgorithm = nullptr;
    938 
    939  // We no longer block BFCache
    940  NotifyToWindow(false);
    941 }
    942 
    943 void WebTransport::SendSetSendOrder(uint64_t aStreamId,
    944                                    Maybe<int64_t> aSendOrder) {
    945  if (!mChild || !mChild->CanSend()) {
    946    return;
    947  }
    948  mChild->SendSetSendOrder(aStreamId, aSendOrder);
    949 }
    950 
    951 void WebTransport::NotifyBFCacheOnMainThread(nsPIDOMWindowInner* aInner,
    952                                             bool aCreated) {
    953  AssertIsOnMainThread();
    954  if (!aInner) {
    955    return;
    956  }
    957  if (aCreated) {
    958    aInner->RemoveFromBFCacheSync();
    959  }
    960 
    961  uint32_t count = aInner->UpdateWebTransportCount(aCreated);
    962  // It's okay for WindowGlobalChild to not exist, as it should mean it already
    963  // is destroyed and can't enter bfcache anyway.
    964  if (WindowGlobalChild* child = aInner->GetWindowGlobalChild()) {
    965    if (aCreated && count == 1) {
    966      // The first WebTransport is active.
    967      child->BlockBFCacheFor(BFCacheStatus::ACTIVE_WEBTRANSPORT);
    968    } else if (count == 0) {
    969      child->UnblockBFCacheFor(BFCacheStatus::ACTIVE_WEBTRANSPORT);
    970    }
    971  }
    972 }
    973 
    974 class BFCacheNotifyWTRunnable final : public WorkerProxyToMainThreadRunnable {
    975 public:
    976  explicit BFCacheNotifyWTRunnable(bool aCreated) : mCreated(aCreated) {}
    977 
    978  void RunOnMainThread(WorkerPrivate* aWorkerPrivate) override {
    979    MOZ_ASSERT(aWorkerPrivate);
    980    AssertIsOnMainThread();
    981    if (aWorkerPrivate->IsDedicatedWorker()) {
    982      WebTransport::NotifyBFCacheOnMainThread(
    983          aWorkerPrivate->GetAncestorWindow(), mCreated);
    984      return;
    985    }
    986    if (aWorkerPrivate->IsSharedWorker()) {
    987      aWorkerPrivate->GetRemoteWorkerController()->NotifyWebTransport(mCreated);
    988      return;
    989    }
    990    MOZ_ASSERT_UNREACHABLE("Unexpected worker type");
    991  }
    992 
    993  void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override {
    994    MOZ_ASSERT(aWorkerPrivate);
    995    aWorkerPrivate->AssertIsOnWorkerThread();
    996  }
    997 
    998 private:
    999  bool mCreated;
   1000 };
   1001 
   1002 void WebTransport::NotifyToWindow(bool aCreated) const {
   1003  if (NS_IsMainThread()) {
   1004    NotifyBFCacheOnMainThread(GetParentObject()->GetAsInnerWindow(), aCreated);
   1005    return;
   1006  }
   1007 
   1008  WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
   1009  if (wp->IsDedicatedWorker() || wp->IsSharedWorker()) {
   1010    RefPtr<BFCacheNotifyWTRunnable> runnable =
   1011        new BFCacheNotifyWTRunnable(aCreated);
   1012 
   1013    runnable->Dispatch(wp);
   1014  }
   1015 };
   1016 
   1017 }  // namespace mozilla::dom