tor-browser

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

Transferable.cpp (39064B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 "ErrorList.h"
      8 #include "ReadableStreamPipeTo.h"
      9 #include "js/RootingAPI.h"
     10 #include "js/String.h"
     11 #include "js/TypeDecls.h"
     12 #include "js/Value.h"
     13 #include "mozilla/AlreadyAddRefed.h"
     14 #include "mozilla/ErrorResult.h"
     15 #include "mozilla/dom/DOMException.h"
     16 #include "mozilla/dom/DOMExceptionBinding.h"
     17 #include "mozilla/dom/MessageChannel.h"
     18 #include "mozilla/dom/MessageEvent.h"
     19 #include "mozilla/dom/MessagePort.h"
     20 #include "mozilla/dom/Promise-inl.h"
     21 #include "mozilla/dom/Promise.h"
     22 #include "mozilla/dom/ReadableStream.h"
     23 #include "mozilla/dom/TransformStream.h"
     24 #include "mozilla/dom/WritableStream.h"
     25 #include "nsCycleCollectionParticipant.h"
     26 #include "nsIDOMEventListener.h"
     27 #include "nsIGlobalObject.h"
     28 #include "nsISupportsImpl.h"
     29 
     30 namespace mozilla::dom {
     31 
     32 using namespace streams_abstract;
     33 
     34 static void PackAndPostMessage(JSContext* aCx, MessagePort* aPort,
     35                               const nsAString& aType,
     36                               JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
     37  JS::Rooted<JSObject*> obj(aCx,
     38                            JS_NewObjectWithGivenProto(aCx, nullptr, nullptr));
     39  if (!obj) {
     40    // XXX: Should we crash here and there? See also bug 1762233.
     41    JS_ClearPendingException(aCx);
     42    aRv.Throw(NS_ERROR_UNEXPECTED);
     43    return;
     44  }
     45 
     46  JS::Rooted<JS::Value> type(aCx);
     47  if (!xpc::NonVoidStringToJsval(aCx, aType, &type)) {
     48    JS_ClearPendingException(aCx);
     49    aRv.Throw(NS_ERROR_UNEXPECTED);
     50    return;
     51  }
     52  if (!JS_DefineProperty(aCx, obj, "type", type, JSPROP_ENUMERATE)) {
     53    JS_ClearPendingException(aCx);
     54    aRv.Throw(NS_ERROR_UNEXPECTED);
     55    return;
     56  }
     57  JS::Rooted<JS::Value> value(aCx, aValue);
     58  if (!JS_WrapValue(aCx, &value)) {
     59    JS_ClearPendingException(aCx);
     60    aRv.Throw(NS_ERROR_UNEXPECTED);
     61    return;
     62  }
     63  if (!JS_DefineProperty(aCx, obj, "value", value, JSPROP_ENUMERATE)) {
     64    JS_ClearPendingException(aCx);
     65    aRv.Throw(NS_ERROR_UNEXPECTED);
     66    return;
     67  }
     68 
     69  Sequence<JSObject*> transferables;  // none in this case
     70  JS::Rooted<JS::Value> objValue(aCx, JS::ObjectValue(*obj));
     71  aPort->PostMessage(aCx, objValue, transferables, aRv);
     72 }
     73 
     74 // https://streams.spec.whatwg.org/#abstract-opdef-crossrealmtransformsenderror
     75 static void CrossRealmTransformSendError(JSContext* aCx, MessagePort* aPort,
     76                                         JS::Handle<JS::Value> aError) {
     77  // Step 1: Perform PackAndPostMessage(port, "error", error), discarding the
     78  // result.
     79  PackAndPostMessage(aCx, aPort, u"error"_ns, aError, IgnoreErrors());
     80 }
     81 
     82 class SetUpTransformWritableMessageEventListener final
     83    : public nsIDOMEventListener {
     84 public:
     85  SetUpTransformWritableMessageEventListener(
     86      WritableStreamDefaultController* aController, Promise* aPromise)
     87      : mController(aController), mBackpressurePromise(aPromise) {}
     88 
     89  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     90  NS_DECL_CYCLE_COLLECTION_CLASS(SetUpTransformWritableMessageEventListener)
     91 
     92  // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable
     93  // The handler steps of Step 4.
     94  MOZ_CAN_RUN_SCRIPT NS_IMETHOD HandleEvent(Event* aEvent) override {
     95    AutoJSAPI jsapi;
     96    if (!jsapi.Init(mController->GetParentObject())) {
     97      return NS_OK;
     98    }
     99    JSContext* cx = jsapi.cx();
    100    MessageEvent* messageEvent = aEvent->AsMessageEvent();
    101    if (NS_WARN_IF(!messageEvent || !messageEvent->IsTrusted())) {
    102      return NS_OK;
    103    }
    104 
    105    // Step 1: Let data be the data of the message.
    106    JS::Rooted<JS::Value> dataValue(cx);
    107    IgnoredErrorResult rv;
    108    messageEvent->GetData(cx, &dataValue, rv);
    109    if (rv.Failed()) {
    110      return NS_OK;
    111    }
    112 
    113    // Step 2: Assert: Type(data) is Object.
    114    // (But we check in runtime instead to avoid potential malicious events from
    115    // a compromised process. Same below.)
    116    if (NS_WARN_IF(!dataValue.isObject())) {
    117      return NS_OK;
    118    }
    119    JS::Rooted<JSObject*> data(cx, &dataValue.toObject());
    120 
    121    // Step 3: Let type be ! Get(data, "type").
    122    JS::Rooted<JS::Value> type(cx);
    123    if (!JS_GetProperty(cx, data, "type", &type)) {
    124      // XXX: See bug 1762233
    125      JS_ClearPendingException(cx);
    126      return NS_OK;
    127    }
    128 
    129    // Step 4: Let value be ! Get(data, "value").
    130    JS::Rooted<JS::Value> value(cx);
    131    if (!JS_GetProperty(cx, data, "value", &value)) {
    132      JS_ClearPendingException(cx);
    133      return NS_OK;
    134    }
    135 
    136    // Step 5: Assert: Type(type) is String.
    137    if (NS_WARN_IF(!type.isString())) {
    138      return NS_OK;
    139    }
    140 
    141    // Step 6: If type is "pull",
    142    bool equals = false;
    143    if (!JS_StringEqualsLiteral(cx, type.toString(), "pull", &equals)) {
    144      JS_ClearPendingException(cx);
    145      return NS_OK;
    146    }
    147    if (equals) {
    148      // Step 6.1: If backpressurePromise is not undefined,
    149      MaybeResolveAndClearBackpressurePromise();
    150      return NS_OK;  // implicit
    151    }
    152 
    153    // Step 7: If type is "error",
    154    if (!JS_StringEqualsLiteral(cx, type.toString(), "error", &equals)) {
    155      JS_ClearPendingException(cx);
    156      return NS_OK;
    157    }
    158    if (equals) {
    159      // Step 7.1: Perform !
    160      // WritableStreamDefaultControllerErrorIfNeeded(controller, value).
    161      WritableStreamDefaultControllerErrorIfNeeded(cx, mController, value, rv);
    162      if (rv.Failed()) {
    163        return NS_OK;
    164      }
    165 
    166      // Step 7.2: If backpressurePromise is not undefined,
    167      MaybeResolveAndClearBackpressurePromise();
    168      return NS_OK;  // implicit
    169    }
    170 
    171    // Logically it should be unreachable here, but we should expect random
    172    // malicious messages.
    173    NS_WARNING("Got an unexpected type other than pull/error.");
    174    return NS_OK;
    175  }
    176 
    177  void MaybeResolveAndClearBackpressurePromise() {
    178    if (mBackpressurePromise) {
    179      mBackpressurePromise->MaybeResolveWithUndefined();
    180      mBackpressurePromise = nullptr;
    181    }
    182  }
    183 
    184  // Note: This promise field is shared with the sink algorithms.
    185  Promise* BackpressurePromise() { return mBackpressurePromise; }
    186 
    187  void CreateBackpressurePromise() {
    188    mBackpressurePromise =
    189        Promise::CreateInfallible(mController->GetParentObject());
    190  }
    191 
    192 private:
    193  ~SetUpTransformWritableMessageEventListener() = default;
    194 
    195  // mController never changes before CC
    196  // TODO: MOZ_IMMUTABLE_OUTSIDE_CC
    197  MOZ_KNOWN_LIVE RefPtr<WritableStreamDefaultController> mController;
    198  RefPtr<Promise> mBackpressurePromise;
    199 };
    200 
    201 NS_IMPL_CYCLE_COLLECTION(SetUpTransformWritableMessageEventListener,
    202                         mController, mBackpressurePromise)
    203 NS_IMPL_CYCLE_COLLECTING_ADDREF(SetUpTransformWritableMessageEventListener)
    204 NS_IMPL_CYCLE_COLLECTING_RELEASE(SetUpTransformWritableMessageEventListener)
    205 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    206    SetUpTransformWritableMessageEventListener)
    207  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    208 NS_INTERFACE_MAP_END
    209 
    210 class SetUpTransformWritableMessageErrorEventListener final
    211    : public nsIDOMEventListener {
    212 public:
    213  SetUpTransformWritableMessageErrorEventListener(
    214      WritableStreamDefaultController* aController, MessagePort* aPort)
    215      : mController(aController), mPort(aPort) {}
    216 
    217  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    218  NS_DECL_CYCLE_COLLECTION_CLASS(
    219      SetUpTransformWritableMessageErrorEventListener)
    220 
    221  // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable
    222  // The handler steps of Step 5.
    223  MOZ_CAN_RUN_SCRIPT NS_IMETHOD HandleEvent(Event* aEvent) override {
    224    auto cleanupPort =
    225        MakeScopeExit([port = RefPtr<MessagePort>(mPort)]() { port->Close(); });
    226 
    227    if (NS_WARN_IF(!aEvent->AsMessageEvent() || !aEvent->IsTrusted())) {
    228      return NS_OK;
    229    }
    230 
    231    // Step 1: Let error be a new "DataCloneError" DOMException.
    232    RefPtr<DOMException> exception =
    233        DOMException::Create(NS_ERROR_DOM_DATA_CLONE_ERR);
    234 
    235    AutoJSAPI jsapi;
    236    if (!jsapi.Init(mPort->GetParentObject())) {
    237      return NS_OK;
    238    }
    239    JSContext* cx = jsapi.cx();
    240    JS::Rooted<JS::Value> error(cx);
    241    if (!ToJSValue(cx, *exception, &error)) {
    242      return NS_OK;
    243    }
    244 
    245    // Step 2: Perform ! CrossRealmTransformSendError(port, error).
    246    CrossRealmTransformSendError(cx, mPort, error);
    247 
    248    // Step 3: Perform
    249    // ! WritableStreamDefaultControllerErrorIfNeeded(controller, error).
    250    WritableStreamDefaultControllerErrorIfNeeded(cx, mController, error,
    251                                                 IgnoreErrors());
    252 
    253    // Step 4: Disentangle port.
    254    // (Close() does it)
    255    mPort->Close();
    256    cleanupPort.release();
    257 
    258    return NS_OK;
    259  }
    260 
    261 private:
    262  ~SetUpTransformWritableMessageErrorEventListener() = default;
    263 
    264  // mController never changes before CC
    265  // TODO: MOZ_IMMUTABLE_OUTSIDE_CC
    266  MOZ_KNOWN_LIVE RefPtr<WritableStreamDefaultController> mController;
    267  RefPtr<MessagePort> mPort;
    268 };
    269 
    270 NS_IMPL_CYCLE_COLLECTION(SetUpTransformWritableMessageErrorEventListener,
    271                         mController, mPort)
    272 NS_IMPL_CYCLE_COLLECTING_ADDREF(SetUpTransformWritableMessageErrorEventListener)
    273 NS_IMPL_CYCLE_COLLECTING_RELEASE(
    274    SetUpTransformWritableMessageErrorEventListener)
    275 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    276    SetUpTransformWritableMessageErrorEventListener)
    277  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    278 NS_INTERFACE_MAP_END
    279 
    280 // https://streams.spec.whatwg.org/#abstract-opdef-packandpostmessagehandlingerror
    281 static bool PackAndPostMessageHandlingError(
    282    JSContext* aCx, mozilla::dom::MessagePort* aPort, const nsAString& aType,
    283    JS::Handle<JS::Value> aValue, JS::MutableHandle<JS::Value> aError) {
    284  // Step 1: Let result be PackAndPostMessage(port, type, value).
    285  ErrorResult rv;
    286  PackAndPostMessage(aCx, aPort, aType, aValue, rv);
    287 
    288  // Step 2: If result is an abrupt completion,
    289  if (rv.Failed()) {
    290    // Step 2.2: Perform ! CrossRealmTransformSendError(port, result.[[Value]]).
    291    MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), aError));
    292    CrossRealmTransformSendError(aCx, aPort, aError);
    293    return false;
    294  }
    295 
    296  // Step 3: Return result as a completion record.
    297  return true;
    298 }
    299 
    300 // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable
    301 class CrossRealmWritableUnderlyingSinkAlgorithms final
    302    : public UnderlyingSinkAlgorithmsBase {
    303 public:
    304  NS_DECL_ISUPPORTS_INHERITED
    305  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
    306      CrossRealmWritableUnderlyingSinkAlgorithms, UnderlyingSinkAlgorithmsBase)
    307 
    308  CrossRealmWritableUnderlyingSinkAlgorithms(
    309      SetUpTransformWritableMessageEventListener* aListener, MessagePort* aPort)
    310      : mListener(aListener), mPort(aPort) {}
    311 
    312  void StartCallback(JSContext* aCx,
    313                     WritableStreamDefaultController& aController,
    314                     JS::MutableHandle<JS::Value> aRetVal,
    315                     ErrorResult& aRv) override {
    316    // Step 7. Let startAlgorithm be an algorithm that returns undefined.
    317    aRetVal.setUndefined();
    318  }
    319 
    320  already_AddRefed<Promise> WriteCallback(
    321      JSContext* aCx, JS::Handle<JS::Value> aChunk,
    322      WritableStreamDefaultController& aController, ErrorResult& aRv) override {
    323    // Step 1: If backpressurePromise is undefined, set backpressurePromise to a
    324    // promise resolved with undefined.
    325    // Note: This promise field is shared with the message event listener.
    326    if (!mListener->BackpressurePromise()) {
    327      mListener->CreateBackpressurePromise();
    328      mListener->BackpressurePromise()->MaybeResolveWithUndefined();
    329    }
    330 
    331    // Step 2: Return the result of reacting to backpressurePromise with the
    332    // following fulfillment steps:
    333    auto result =
    334        mListener->BackpressurePromise()->ThenWithCycleCollectedArgsJS(
    335            [](JSContext* aCx, JS::Handle<JS::Value>, ErrorResult& aRv,
    336               SetUpTransformWritableMessageEventListener* aListener,
    337               MessagePort* aPort,
    338               JS::Handle<JS::Value> aChunk) -> already_AddRefed<Promise> {
    339              // Step 2.1: Set backpressurePromise to a new promise.
    340              aListener->CreateBackpressurePromise();
    341 
    342              // Step 2.2: Let result be PackAndPostMessageHandlingError(port,
    343              // "chunk", chunk).
    344              JS::Rooted<JS::Value> error(aCx);
    345              bool result = PackAndPostMessageHandlingError(
    346                  aCx, aPort, u"chunk"_ns, aChunk, &error);
    347 
    348              // Step 2.3: If result is an abrupt completion,
    349              if (!result) {
    350                // Step 2.3.1: Disentangle port.
    351                // (Close() does it)
    352                aPort->Close();
    353 
    354                // Step 2.3.2: Return a promise rejected with result.[[Value]].
    355                return Promise::CreateRejected(aPort->GetParentObject(), error,
    356                                               aRv);
    357              }
    358 
    359              // Step 2.4: Otherwise, return a promise resolved with undefined.
    360              return Promise::CreateResolvedWithUndefined(
    361                  aPort->GetParentObject(), aRv);
    362            },
    363            std::make_tuple(mListener, mPort), std::make_tuple(aChunk));
    364    if (result.isErr()) {
    365      aRv.Throw(result.unwrapErr());
    366      return nullptr;
    367    }
    368    return result.unwrap().forget();
    369  }
    370 
    371  already_AddRefed<Promise> CloseCallback(JSContext* aCx,
    372                                          ErrorResult& aRv) override {
    373    // Step 1: Perform ! PackAndPostMessage(port, "close", undefined).
    374    PackAndPostMessage(aCx, mPort, u"close"_ns, JS::UndefinedHandleValue, aRv);
    375    // (We'll check the result after step 2)
    376 
    377    // Step 2: Disentangle port.
    378    // (Close() will do this)
    379    mPort->Close();
    380 
    381    if (aRv.Failed()) {
    382      return nullptr;
    383    }
    384 
    385    // Step 3: Return a promise resolved with undefined.
    386    return Promise::CreateResolvedWithUndefined(mPort->GetParentObject(), aRv);
    387  }
    388 
    389  already_AddRefed<Promise> AbortCallback(
    390      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    391      ErrorResult& aRv) override {
    392    // Step 1: Let result be PackAndPostMessageHandlingError(port, "error",
    393    // reason).
    394    JS::Rooted<JS::Value> error(aCx);
    395    bool result = PackAndPostMessageHandlingError(
    396        aCx, mPort, u"error"_ns,
    397        aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue,
    398        &error);
    399 
    400    // Step 2: Disentangle port.
    401    // (Close() will do this)
    402    mPort->Close();
    403 
    404    // Step 3: If result is an abrupt completion, return a promise rejected with
    405    // result.[[Value]].
    406    if (!result) {
    407      return Promise::CreateRejected(mPort->GetParentObject(), error, aRv);
    408    }
    409 
    410    // Step 4: Otherwise, return a promise resolved with undefined.
    411    return Promise::CreateResolvedWithUndefined(mPort->GetParentObject(), aRv);
    412  }
    413 
    414 protected:
    415  ~CrossRealmWritableUnderlyingSinkAlgorithms() override = default;
    416 
    417 private:
    418  RefPtr<SetUpTransformWritableMessageEventListener> mListener;
    419  RefPtr<MessagePort> mPort;
    420 };
    421 
    422 NS_IMPL_CYCLE_COLLECTION_INHERITED(CrossRealmWritableUnderlyingSinkAlgorithms,
    423                                   UnderlyingSinkAlgorithmsBase, mListener,
    424                                   mPort)
    425 NS_IMPL_ADDREF_INHERITED(CrossRealmWritableUnderlyingSinkAlgorithms,
    426                         UnderlyingSinkAlgorithmsBase)
    427 NS_IMPL_RELEASE_INHERITED(CrossRealmWritableUnderlyingSinkAlgorithms,
    428                          UnderlyingSinkAlgorithmsBase)
    429 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    430    CrossRealmWritableUnderlyingSinkAlgorithms)
    431 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSinkAlgorithmsBase)
    432 
    433 // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable
    434 MOZ_CAN_RUN_SCRIPT static void SetUpCrossRealmTransformWritable(
    435    WritableStream* aWritable, MessagePort* aPort, ErrorResult& aRv) {
    436  // (This is only needed for step 12, but let's do this early to fail early
    437  // enough)
    438  AutoJSAPI jsapi;
    439  if (!jsapi.Init(aWritable->GetParentObject())) {
    440    return;
    441  }
    442  JSContext* cx = jsapi.cx();
    443 
    444  // Step 1: Perform ! InitializeWritableStream(stream).
    445  // (Done by the constructor)
    446 
    447  // Step 2: Let controller be a new WritableStreamDefaultController.
    448  auto controller = MakeRefPtr<WritableStreamDefaultController>(
    449      aWritable->GetParentObject(), *aWritable);
    450 
    451  // Step 3: Let backpressurePromise be a new promise.
    452  RefPtr<Promise> backpressurePromise =
    453      Promise::CreateInfallible(aWritable->GetParentObject());
    454 
    455  // Step 4: Add a handler for port’s message event with the following steps:
    456  auto listener = MakeRefPtr<SetUpTransformWritableMessageEventListener>(
    457      controller, backpressurePromise);
    458  aPort->AddEventListener(u"message"_ns, listener, false);
    459 
    460  // Step 5: Add a handler for port’s messageerror event with the following
    461  // steps:
    462  auto errorListener =
    463      MakeRefPtr<SetUpTransformWritableMessageErrorEventListener>(controller,
    464                                                                  aPort);
    465  aPort->AddEventListener(u"messageerror"_ns, errorListener, false);
    466 
    467  // Step 6: Enable port’s port message queue.
    468  // (Start() does it)
    469  aPort->Start();
    470 
    471  // Step 7 - 10:
    472  auto algorithms =
    473      MakeRefPtr<CrossRealmWritableUnderlyingSinkAlgorithms>(listener, aPort);
    474 
    475  // Step 11: Let sizeAlgorithm be an algorithm that returns 1.
    476  // (nullptr should serve this purpose. See also WritableStream::Constructor)
    477 
    478  // Step 12: Perform ! SetUpWritableStreamDefaultController(stream, controller,
    479  // startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, 1,
    480  // sizeAlgorithm).
    481  SetUpWritableStreamDefaultController(cx, aWritable, controller, algorithms, 1,
    482                                       /* aSizeAlgorithm */ nullptr, aRv);
    483 }
    484 
    485 class SetUpTransformReadableMessageEventListener final
    486    : public nsIDOMEventListener {
    487 public:
    488  SetUpTransformReadableMessageEventListener(
    489      ReadableStreamDefaultController* aController, MessagePort* aPort)
    490      : mController(aController), mPort(aPort) {}
    491 
    492  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    493  NS_DECL_CYCLE_COLLECTION_CLASS(SetUpTransformReadableMessageEventListener)
    494 
    495  // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
    496  // The handler steps of Step 3.
    497  MOZ_CAN_RUN_SCRIPT NS_IMETHOD HandleEvent(Event* aEvent) override {
    498    auto cleanupPort =
    499        MakeScopeExit([port = RefPtr<MessagePort>(mPort)]() { port->Close(); });
    500 
    501    AutoJSAPI jsapi;
    502    if (!jsapi.Init(mPort->GetParentObject())) {
    503      return NS_OK;
    504    }
    505    JSContext* cx = jsapi.cx();
    506    MessageEvent* messageEvent = aEvent->AsMessageEvent();
    507    if (NS_WARN_IF(!messageEvent || !messageEvent->IsTrusted())) {
    508      return NS_OK;
    509    }
    510 
    511    // Step 1: Let data be the data of the message.
    512    JS::Rooted<JS::Value> dataValue(cx);
    513    IgnoredErrorResult rv;
    514    messageEvent->GetData(cx, &dataValue, rv);
    515    if (rv.Failed()) {
    516      return NS_OK;
    517    }
    518 
    519    // Step 2: Assert: Type(data) is Object.
    520    // (But we check in runtime instead to avoid potential malicious events from
    521    // a compromised process. Same below.)
    522    if (NS_WARN_IF(!dataValue.isObject())) {
    523      return NS_OK;
    524    }
    525    JS::Rooted<JSObject*> data(cx, JS::ToObject(cx, dataValue));
    526 
    527    // Step 3: Let type be ! Get(data, "type").
    528    JS::Rooted<JS::Value> type(cx);
    529    if (!JS_GetProperty(cx, data, "type", &type)) {
    530      // XXX: See bug 1762233
    531      JS_ClearPendingException(cx);
    532      return NS_OK;
    533    }
    534 
    535    // Step 4: Let value be ! Get(data, "value").
    536    JS::Rooted<JS::Value> value(cx);
    537    if (!JS_GetProperty(cx, data, "value", &value)) {
    538      JS_ClearPendingException(cx);
    539      return NS_OK;
    540    }
    541 
    542    // Step 5: Assert: Type(type) is String.
    543    if (NS_WARN_IF(!type.isString())) {
    544      return NS_OK;
    545    }
    546 
    547    // Step 6: If type is "chunk",
    548    bool equals = false;
    549    if (!JS_StringEqualsLiteral(cx, type.toString(), "chunk", &equals)) {
    550      JS_ClearPendingException(cx);
    551      return NS_OK;
    552    }
    553    if (equals) {
    554      // Step 6.1: Perform ! ReadableStreamDefaultControllerEnqueue(controller,
    555      // value).
    556      ReadableStreamDefaultControllerEnqueue(cx, mController, value,
    557                                             IgnoreErrors());
    558      cleanupPort.release();
    559      return NS_OK;  // implicit
    560    }
    561 
    562    // Step 7: Otherwise, if type is "close",
    563    if (!JS_StringEqualsLiteral(cx, type.toString(), "close", &equals)) {
    564      JS_ClearPendingException(cx);
    565      return NS_OK;
    566    }
    567    if (equals) {
    568      // Step 7.1: Perform ! ReadableStreamDefaultControllerClose(controller).
    569      ReadableStreamDefaultControllerClose(cx, mController, IgnoreErrors());
    570      // Step 7.2: Disentangle port.
    571      // (Close() does it)
    572      mPort->Close();
    573      cleanupPort.release();
    574      return NS_OK;  // implicit
    575    }
    576 
    577    // Step 8: Otherwise, if type is "error",
    578    if (!JS_StringEqualsLiteral(cx, type.toString(), "error", &equals)) {
    579      JS_ClearPendingException(cx);
    580      return NS_OK;
    581    }
    582    if (equals) {
    583      // Step 8.1: Perform ! ReadableStreamDefaultControllerError(controller,
    584      // value).
    585      ReadableStreamDefaultControllerError(cx, mController, value,
    586                                           IgnoreErrors());
    587 
    588      // Step 8.2: Disentangle port.
    589      // (Close() does it)
    590      mPort->Close();
    591      cleanupPort.release();
    592      return NS_OK;  // implicit
    593    }
    594 
    595    // Logically it should be unreachable here, but we should expect random
    596    // malicious messages.
    597    NS_WARNING("Got an unexpected type other than chunk/close/error.");
    598    return NS_OK;
    599  }
    600 
    601 private:
    602  ~SetUpTransformReadableMessageEventListener() = default;
    603 
    604  // mController never changes before CC
    605  // TODO: MOZ_IMMUTABLE_OUTSIDE_CC
    606  MOZ_KNOWN_LIVE RefPtr<ReadableStreamDefaultController> mController;
    607  RefPtr<MessagePort> mPort;
    608 };
    609 
    610 NS_IMPL_CYCLE_COLLECTION(SetUpTransformReadableMessageEventListener,
    611                         mController, mPort)
    612 NS_IMPL_CYCLE_COLLECTING_ADDREF(SetUpTransformReadableMessageEventListener)
    613 NS_IMPL_CYCLE_COLLECTING_RELEASE(SetUpTransformReadableMessageEventListener)
    614 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    615    SetUpTransformReadableMessageEventListener)
    616  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    617 NS_INTERFACE_MAP_END
    618 
    619 class SetUpTransformReadableMessageErrorEventListener final
    620    : public nsIDOMEventListener {
    621 public:
    622  SetUpTransformReadableMessageErrorEventListener(
    623      ReadableStreamDefaultController* aController, MessagePort* aPort)
    624      : mController(aController), mPort(aPort) {}
    625 
    626  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    627  NS_DECL_CYCLE_COLLECTION_CLASS(
    628      SetUpTransformReadableMessageErrorEventListener)
    629 
    630  // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
    631  // The handler steps of Step 4.
    632  MOZ_CAN_RUN_SCRIPT NS_IMETHOD HandleEvent(Event* aEvent) override {
    633    auto cleanupPort =
    634        MakeScopeExit([port = RefPtr<MessagePort>(mPort)]() { port->Close(); });
    635 
    636    if (NS_WARN_IF(!aEvent->AsMessageEvent() || !aEvent->IsTrusted())) {
    637      return NS_OK;
    638    }
    639 
    640    // Step 1: Let error be a new "DataCloneError" DOMException.
    641    RefPtr<DOMException> exception =
    642        DOMException::Create(NS_ERROR_DOM_DATA_CLONE_ERR);
    643 
    644    AutoJSAPI jsapi;
    645    if (!jsapi.Init(mPort->GetParentObject())) {
    646      return NS_OK;
    647    }
    648    JSContext* cx = jsapi.cx();
    649    JS::Rooted<JS::Value> error(cx);
    650    if (!ToJSValue(cx, *exception, &error)) {
    651      return NS_OK;
    652    }
    653 
    654    // Step 2: Perform ! CrossRealmTransformSendError(port, error).
    655    CrossRealmTransformSendError(cx, mPort, error);
    656 
    657    // Step 3: Perform ! ReadableStreamDefaultControllerError(controller,
    658    // error).
    659    ReadableStreamDefaultControllerError(cx, mController, error,
    660                                         IgnoreErrors());
    661 
    662    // Step 4: Disentangle port.
    663    // (Close() does it)
    664    mPort->Close();
    665    cleanupPort.release();
    666 
    667    return NS_OK;
    668  }
    669 
    670 private:
    671  ~SetUpTransformReadableMessageErrorEventListener() = default;
    672 
    673  RefPtr<ReadableStreamDefaultController> mController;
    674  RefPtr<MessagePort> mPort;
    675 };
    676 
    677 NS_IMPL_CYCLE_COLLECTION(SetUpTransformReadableMessageErrorEventListener,
    678                         mController, mPort)
    679 NS_IMPL_CYCLE_COLLECTING_ADDREF(SetUpTransformReadableMessageErrorEventListener)
    680 NS_IMPL_CYCLE_COLLECTING_RELEASE(
    681    SetUpTransformReadableMessageErrorEventListener)
    682 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    683    SetUpTransformReadableMessageErrorEventListener)
    684  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    685 NS_INTERFACE_MAP_END
    686 
    687 // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
    688 class CrossRealmReadableUnderlyingSourceAlgorithms final
    689    : public UnderlyingSourceAlgorithmsBase {
    690 public:
    691  NS_DECL_ISUPPORTS_INHERITED
    692  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
    693      CrossRealmReadableUnderlyingSourceAlgorithms,
    694      UnderlyingSourceAlgorithmsBase)
    695 
    696  explicit CrossRealmReadableUnderlyingSourceAlgorithms(MessagePort* aPort)
    697      : mPort(aPort) {}
    698 
    699  void StartCallback(JSContext* aCx, ReadableStreamControllerBase& aController,
    700                     JS::MutableHandle<JS::Value> aRetVal,
    701                     ErrorResult& aRv) override {
    702    // Step 6. Let startAlgorithm be an algorithm that returns undefined.
    703    aRetVal.setUndefined();
    704  }
    705 
    706  already_AddRefed<Promise> PullCallback(
    707      JSContext* aCx, ReadableStreamControllerBase& aController,
    708      ErrorResult& aRv) override {
    709    // Step 7: Let pullAlgorithm be the following steps:
    710 
    711    // Step 7.1: Perform ! PackAndPostMessage(port, "pull", undefined).
    712    PackAndPostMessage(aCx, mPort, u"pull"_ns, JS::UndefinedHandleValue, aRv);
    713    if (aRv.Failed()) {
    714      return nullptr;
    715    }
    716 
    717    // Step 7.2: Return a promise resolved with undefined.
    718    return Promise::CreateResolvedWithUndefined(mPort->GetParentObject(), aRv);
    719  }
    720 
    721  already_AddRefed<Promise> CancelCallback(
    722      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    723      ErrorResult& aRv) override {
    724    // Step 8: Let cancelAlgorithm be the following steps, taking a reason
    725    // argument:
    726 
    727    // Step 8.1: Let result be PackAndPostMessageHandlingError(port, "error",
    728    // reason).
    729    JS::Rooted<JS::Value> error(aCx);
    730    bool result = PackAndPostMessageHandlingError(
    731        aCx, mPort, u"error"_ns,
    732        aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue,
    733        &error);
    734 
    735    // Step 8.2: Disentangle port.
    736    // (Close() does it)
    737    mPort->Close();
    738 
    739    // Step 8.3: If result is an abrupt completion, return a promise rejected
    740    // with result.[[Value]].
    741    if (!result) {
    742      return Promise::CreateRejected(mPort->GetParentObject(), error, aRv);
    743    }
    744 
    745    // Step 8.4: Otherwise, return a promise resolved with undefined.
    746    return Promise::CreateResolvedWithUndefined(mPort->GetParentObject(), aRv);
    747  }
    748 
    749 protected:
    750  ~CrossRealmReadableUnderlyingSourceAlgorithms() override = default;
    751 
    752 private:
    753  RefPtr<MessagePort> mPort;
    754 };
    755 
    756 NS_IMPL_CYCLE_COLLECTION_INHERITED(CrossRealmReadableUnderlyingSourceAlgorithms,
    757                                   UnderlyingSourceAlgorithmsBase, mPort)
    758 NS_IMPL_ADDREF_INHERITED(CrossRealmReadableUnderlyingSourceAlgorithms,
    759                         UnderlyingSourceAlgorithmsBase)
    760 NS_IMPL_RELEASE_INHERITED(CrossRealmReadableUnderlyingSourceAlgorithms,
    761                          UnderlyingSourceAlgorithmsBase)
    762 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    763    CrossRealmReadableUnderlyingSourceAlgorithms)
    764 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase)
    765 
    766 // https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
    767 MOZ_CAN_RUN_SCRIPT static void SetUpCrossRealmTransformReadable(
    768    ReadableStream* aReadable, MessagePort* aPort, ErrorResult& aRv) {
    769  // (This is only needed for step 10, but let's do this early to fail early
    770  // enough)
    771  AutoJSAPI jsapi;
    772  if (!jsapi.Init(aReadable->GetParentObject())) {
    773    return;
    774  }
    775  JSContext* cx = jsapi.cx();
    776 
    777  // Step 1: Perform ! InitializeReadableStream(stream).
    778  // (This is implicitly done by the constructor)
    779 
    780  // Step 2: Let controller be a new ReadableStreamDefaultController.
    781  auto controller =
    782      MakeRefPtr<ReadableStreamDefaultController>(aReadable->GetParentObject());
    783 
    784  // Step 3: Add a handler for port’s message event with the following steps:
    785  auto listener =
    786      MakeRefPtr<SetUpTransformReadableMessageEventListener>(controller, aPort);
    787  aPort->AddEventListener(u"message"_ns, listener, false);
    788 
    789  // Step 4: Add a handler for port’s messageerror event with the following
    790  // steps:
    791  auto errorListener =
    792      MakeRefPtr<SetUpTransformReadableMessageErrorEventListener>(controller,
    793                                                                  aPort);
    794  aPort->AddEventListener(u"messageerror"_ns, errorListener, false);
    795 
    796  // Step 5: Enable port’s port message queue.
    797  // (Start() does it)
    798  aPort->Start();
    799 
    800  // Step 6-8:
    801  auto algorithms =
    802      MakeRefPtr<CrossRealmReadableUnderlyingSourceAlgorithms>(aPort);
    803 
    804  // Step 9: Let sizeAlgorithm be an algorithm that returns 1.
    805  // (nullptr should serve this purpose. See also ReadableStream::Constructor)
    806 
    807  // Step 10: Perform ! SetUpReadableStreamDefaultController(stream, controller,
    808  // startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, sizeAlgorithm).
    809  SetUpReadableStreamDefaultController(cx, aReadable, controller, algorithms, 0,
    810                                       /* aSizeAlgorithm */ nullptr, aRv);
    811 }
    812 
    813 // https://streams.spec.whatwg.org/#ref-for-transfer-steps
    814 bool ReadableStream::Transfer(JSContext* aCx, UniqueMessagePortId& aPortId) {
    815  // Step 1: If ! IsReadableStreamLocked(value) is true, throw a
    816  // "DataCloneError" DOMException.
    817  // (Implemented in StructuredCloneHolder::CustomCanTransferHandler, but double
    818  // check here as the state might have changed in case this ReadableStream is
    819  // created by a TransferStream and being transferred together with the
    820  // parent.)
    821  if (IsReadableStreamLocked(this)) {
    822    return false;
    823  }
    824 
    825  // Step 2: Let port1 be a new MessagePort in the current Realm.
    826  // Step 3: Let port2 be a new MessagePort in the current Realm.
    827  // Step 4: Entangle port1 and port2.
    828  // (The MessageChannel constructor does exactly that.)
    829  // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messagechannel
    830  ErrorResult rv;
    831  RefPtr<dom::MessageChannel> channel =
    832      dom::MessageChannel::Constructor(mGlobal, rv);
    833  if (rv.MaybeSetPendingException(aCx)) {
    834    return false;
    835  }
    836 
    837  // Step 5: Let writable be a new WritableStream in the current Realm.
    838  RefPtr<WritableStream> writable = new WritableStream(
    839      mGlobal, WritableStream::HoldDropJSObjectsCaller::Implicit);
    840 
    841  // Step 6: Perform ! SetUpCrossRealmTransformWritable(writable, port1).
    842  // MOZ_KnownLive because Port1 never changes before CC
    843  SetUpCrossRealmTransformWritable(writable, MOZ_KnownLive(channel->Port1()),
    844                                   rv);
    845  if (rv.MaybeSetPendingException(aCx)) {
    846    return false;
    847  }
    848 
    849  // Step 7: Let promise be ! ReadableStreamPipeTo(value, writable, false,
    850  // false, false).
    851  RefPtr<Promise> promise =
    852      ReadableStreamPipeTo(this, writable, false, false, false, nullptr, rv);
    853  if (rv.MaybeSetPendingException(aCx)) {
    854    return false;
    855  }
    856 
    857  // Step 8: Set promise.[[PromiseIsHandled]] to true.
    858  MOZ_ALWAYS_TRUE(promise->SetAnyPromiseIsHandled());
    859 
    860  // Step 9: Set dataHolder.[[port]] to ! StructuredSerializeWithTransfer(port2,
    861  // « port2 »).
    862  channel->Port2()->CloneAndDisentangle(aPortId);
    863 
    864  return true;
    865 }
    866 
    867 // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps
    868 MOZ_CAN_RUN_SCRIPT already_AddRefed<ReadableStream>
    869 ReadableStream::ReceiveTransferImpl(JSContext* aCx, nsIGlobalObject* aGlobal,
    870                                    MessagePort& aPort) {
    871  // Step 1: Let deserializedRecord be
    872  // ! StructuredDeserializeWithTransfer(dataHolder.[[port]], the current
    873  // Realm).
    874  // Step 2: Let port be deserializedRecord.[[Deserialized]].
    875 
    876  // Step 3: Perform ! SetUpCrossRealmTransformReadable(value, port).
    877  RefPtr<ReadableStream> readable =
    878      new ReadableStream(aGlobal, HoldDropJSObjectsCaller::Implicit);
    879  ErrorResult rv;
    880  SetUpCrossRealmTransformReadable(readable, &aPort, rv);
    881  if (rv.MaybeSetPendingException(aCx)) {
    882    return nullptr;
    883  }
    884  return readable.forget();
    885 }
    886 
    887 bool ReadableStream::ReceiveTransfer(
    888    JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort,
    889    JS::MutableHandle<JSObject*> aReturnObject) {
    890  RefPtr<ReadableStream> readable =
    891      ReadableStream::ReceiveTransferImpl(aCx, aGlobal, aPort);
    892  if (!readable) {
    893    return false;
    894  }
    895 
    896  JS::Rooted<JS::Value> value(aCx);
    897  if (!GetOrCreateDOMReflector(aCx, readable, &value)) {
    898    JS_ClearPendingException(aCx);
    899    return false;
    900  }
    901  aReturnObject.set(&value.toObject());
    902 
    903  return true;
    904 }
    905 
    906 // https://streams.spec.whatwg.org/#ref-for-transfer-steps①
    907 bool WritableStream::Transfer(JSContext* aCx, UniqueMessagePortId& aPortId) {
    908  // Step 1: If ! IsWritableStreamLocked(value) is true, throw a
    909  // "DataCloneError" DOMException.
    910  // (Implemented in StructuredCloneHolder::CustomCanTransferHandler, but double
    911  // check here as the state might have changed in case this WritableStream is
    912  // created by a TransferStream and being transferred together with the
    913  // parent.)
    914  if (IsWritableStreamLocked(this)) {
    915    return false;
    916  }
    917 
    918  // Step 2: Let port1 be a new MessagePort in the current Realm.
    919  // Step 3: Let port2 be a new MessagePort in the current Realm.
    920  // Step 4: Entangle port1 and port2.
    921  // (The MessageChannel constructor does exactly that.)
    922  // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messagechannel
    923  ErrorResult rv;
    924  RefPtr<dom::MessageChannel> channel =
    925      dom::MessageChannel::Constructor(mGlobal, rv);
    926  if (rv.MaybeSetPendingException(aCx)) {
    927    return false;
    928  }
    929 
    930  // Step 5: Let readable be a new ReadableStream in the current Realm.
    931  RefPtr<ReadableStream> readable = new ReadableStream(
    932      mGlobal, ReadableStream::HoldDropJSObjectsCaller::Implicit);
    933 
    934  // Step 6: Perform ! SetUpCrossRealmTransformReadable(readable, port1).
    935  // MOZ_KnownLive because Port1 never changes before CC
    936  SetUpCrossRealmTransformReadable(readable, MOZ_KnownLive(channel->Port1()),
    937                                   rv);
    938  if (rv.MaybeSetPendingException(aCx)) {
    939    return false;
    940  }
    941 
    942  // Step 7: Let promise be ! ReadableStreamPipeTo(readable, value, false,
    943  // false, false).
    944  RefPtr<Promise> promise =
    945      ReadableStreamPipeTo(readable, this, false, false, false, nullptr, rv);
    946  if (rv.Failed()) {
    947    return false;
    948  }
    949 
    950  // Step 8: Set promise.[[PromiseIsHandled]] to true.
    951  MOZ_ALWAYS_TRUE(promise->SetAnyPromiseIsHandled());
    952 
    953  // Step 9: Set dataHolder.[[port]] to ! StructuredSerializeWithTransfer(port2,
    954  // « port2 »).
    955  channel->Port2()->CloneAndDisentangle(aPortId);
    956 
    957  return true;
    958 }
    959 
    960 // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps①
    961 MOZ_CAN_RUN_SCRIPT already_AddRefed<WritableStream>
    962 WritableStream::ReceiveTransferImpl(JSContext* aCx, nsIGlobalObject* aGlobal,
    963                                    MessagePort& aPort) {
    964  // Step 1: Let deserializedRecord be !
    965  // StructuredDeserializeWithTransfer(dataHolder.[[port]], the current Realm).
    966  // Step 2: Let port be a deserializedRecord.[[Deserialized]].
    967 
    968  // Step 3: Perform ! SetUpCrossRealmTransformWritable(value, port).
    969  RefPtr<WritableStream> writable = new WritableStream(
    970      aGlobal, WritableStream::HoldDropJSObjectsCaller::Implicit);
    971  ErrorResult rv;
    972  SetUpCrossRealmTransformWritable(writable, &aPort, rv);
    973  if (rv.MaybeSetPendingException(aCx)) {
    974    return nullptr;
    975  }
    976  return writable.forget();
    977 }
    978 
    979 // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps①
    980 bool WritableStream::ReceiveTransfer(
    981    JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort,
    982    JS::MutableHandle<JSObject*> aReturnObject) {
    983  RefPtr<WritableStream> writable =
    984      WritableStream::ReceiveTransferImpl(aCx, aGlobal, aPort);
    985  if (!writable) {
    986    return false;
    987  }
    988 
    989  JS::Rooted<JS::Value> value(aCx);
    990  if (!GetOrCreateDOMReflector(aCx, writable, &value)) {
    991    JS_ClearPendingException(aCx);
    992    return false;
    993  }
    994  aReturnObject.set(&value.toObject());
    995 
    996  return true;
    997 }
    998 
    999 // https://streams.spec.whatwg.org/#ref-for-transfer-steps②
   1000 bool TransformStream::Transfer(JSContext* aCx, UniqueMessagePortId& aPortId1,
   1001                               UniqueMessagePortId& aPortId2) {
   1002  // Step 1: Let readable be value.[[readable]].
   1003  // Step 2: Let writable be value.[[writable]].
   1004  // Step 3: If ! IsReadableStreamLocked(readable) is true, throw a
   1005  // "DataCloneError" DOMException.
   1006  // Step 4: If ! IsWritableStreamLocked(writable) is true, throw a
   1007  // "DataCloneError" DOMException.
   1008  // (Implemented in StructuredCloneHolder::CustomCanTransferHandler, but double
   1009  // check here as the state might have changed by
   1010  // Readable/WritableStream::Transfer in case the stream members of this
   1011  // TransformStream are being transferred together.)
   1012  if (IsReadableStreamLocked(mReadable) || IsWritableStreamLocked(mWritable)) {
   1013    return false;
   1014  }
   1015 
   1016  // Step 5: Set dataHolder.[[readable]] to !
   1017  // StructuredSerializeWithTransfer(readable, « readable »).
   1018  // TODO: Mark mReadable as MOZ_KNOWN_LIVE again (bug 1769854)
   1019  if (!MOZ_KnownLive(mReadable)->Transfer(aCx, aPortId1)) {
   1020    return false;
   1021  }
   1022 
   1023  // Step 6: Set dataHolder.[[writable]] to !
   1024  // StructuredSerializeWithTransfer(writable, « writable »).
   1025  // TODO: Mark mReadable as MOZ_KNOWN_LIVE again (bug 1769854)
   1026  return MOZ_KnownLive(mWritable)->Transfer(aCx, aPortId2);
   1027 }
   1028 
   1029 // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps②
   1030 bool TransformStream::ReceiveTransfer(
   1031    JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort1,
   1032    MessagePort& aPort2, JS::MutableHandle<JSObject*> aReturnObject) {
   1033  // Step 1: Let readableRecord be !
   1034  // StructuredDeserializeWithTransfer(dataHolder.[[readable]], the current
   1035  // Realm).
   1036  RefPtr<ReadableStream> readable =
   1037      ReadableStream::ReceiveTransferImpl(aCx, aGlobal, aPort1);
   1038  if (!readable) {
   1039    return false;
   1040  }
   1041 
   1042  // Step 2: Let writableRecord be !
   1043  // StructuredDeserializeWithTransfer(dataHolder.[[writable]], the current
   1044  // Realm).
   1045  RefPtr<WritableStream> writable =
   1046      WritableStream::ReceiveTransferImpl(aCx, aGlobal, aPort2);
   1047  if (!writable) {
   1048    return false;
   1049  }
   1050 
   1051  // Step 3: Set value.[[readable]] to readableRecord.[[Deserialized]].
   1052  // Step 4: Set value.[[writable]] to writableRecord.[[Deserialized]].
   1053  // Step 5: Set value.[[backpressure]], value.[[backpressureChangePromise]],
   1054  // and value.[[controller]] to undefined.
   1055  RefPtr<TransformStream> stream =
   1056      new TransformStream(aGlobal, readable, writable);
   1057  JS::Rooted<JS::Value> value(aCx);
   1058  if (!GetOrCreateDOMReflector(aCx, stream, &value)) {
   1059    JS_ClearPendingException(aCx);
   1060    return false;
   1061  }
   1062  aReturnObject.set(&value.toObject());
   1063 
   1064  return true;
   1065 }
   1066 
   1067 }  // namespace mozilla::dom