Skip to main content

script/dom/stream/
writablestream.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, RefCell};
6use std::collections::VecDeque;
7use std::mem;
8use std::ptr::{self};
9use std::rc::Rc;
10
11use dom_struct::dom_struct;
12use js::context::JSContext;
13use js::jsapi::{Heap, JSObject};
14use js::jsval::{JSVal, ObjectValue, UndefinedValue};
15use js::realm::CurrentRealm;
16use js::rust::{
17    HandleObject as SafeHandleObject, HandleValue as SafeHandleValue,
18    MutableHandleValue as SafeMutableHandleValue,
19};
20use rustc_hash::FxHashMap;
21use script_bindings::cell::DomRefCell;
22use script_bindings::codegen::GenericBindings::MessagePortBinding::MessagePortMethods;
23use script_bindings::conversions::SafeToJSValConvertible;
24use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
25use servo_base::id::{MessagePortId, MessagePortIndex};
26use servo_constellation_traits::MessagePortImpl;
27
28use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::{
29    QueuingStrategy, QueuingStrategySize,
30};
31use crate::dom::bindings::codegen::Bindings::UnderlyingSinkBinding::UnderlyingSink;
32use crate::dom::bindings::codegen::Bindings::WritableStreamBinding::WritableStreamMethods;
33use crate::dom::bindings::conversions::ConversionResult;
34use crate::dom::bindings::error::{Error, Fallible};
35use crate::dom::bindings::reflector::DomGlobal;
36use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
37use crate::dom::bindings::structuredclone::StructuredData;
38use crate::dom::bindings::transferable::Transferable;
39use crate::dom::domexception::{DOMErrorName, DOMException};
40use crate::dom::globalscope::GlobalScope;
41use crate::dom::messageport::MessagePort;
42use crate::dom::promise::Promise;
43use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
44use crate::dom::readablestream::{ReadableStream, get_type_and_value_from_message};
45use crate::dom::stream::countqueuingstrategy::{extract_high_water_mark, extract_size_algorithm};
46use crate::dom::stream::writablestreamdefaultcontroller::{
47    UnderlyingSinkType, WritableStreamDefaultController,
48};
49use crate::dom::stream::writablestreamdefaultwriter::WritableStreamDefaultWriter;
50use crate::realms::enter_auto_realm;
51
52impl js::gc::Rootable for AbortAlgorithmFulfillmentHandler {}
53
54/// The fulfillment handler for the abort steps of
55/// <https://streams.spec.whatwg.org/#writable-stream-finish-erroring>
56#[derive(JSTraceable, MallocSizeOf)]
57#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
58struct AbortAlgorithmFulfillmentHandler {
59    stream: Dom<WritableStream>,
60    #[conditional_malloc_size_of]
61    abort_request_promise: Rc<Promise>,
62}
63
64impl Callback for AbortAlgorithmFulfillmentHandler {
65    fn callback(&self, cx: &mut CurrentRealm, _v: SafeHandleValue) {
66        // Resolve abortRequest’s promise with undefined.
67        self.abort_request_promise.resolve_native(cx, &());
68
69        // Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
70        self.stream
71            .as_rooted()
72            .reject_close_and_closed_promise_if_needed(cx);
73    }
74}
75
76impl js::gc::Rootable for AbortAlgorithmRejectionHandler {}
77
78/// The rejection handler for the abort steps of
79/// <https://streams.spec.whatwg.org/#writable-stream-finish-erroring>
80#[derive(JSTraceable, MallocSizeOf)]
81#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
82struct AbortAlgorithmRejectionHandler {
83    stream: Dom<WritableStream>,
84    #[conditional_malloc_size_of]
85    abort_request_promise: Rc<Promise>,
86}
87
88impl Callback for AbortAlgorithmRejectionHandler {
89    fn callback(&self, cx: &mut CurrentRealm, reason: SafeHandleValue) {
90        // Reject abortRequest’s promise with reason.
91        self.abort_request_promise.reject_native(cx, &reason);
92
93        // Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
94        self.stream
95            .as_rooted()
96            .reject_close_and_closed_promise_if_needed(cx);
97    }
98}
99
100impl js::gc::Rootable for PendingAbortRequest {}
101
102/// <https://streams.spec.whatwg.org/#pending-abort-request>
103#[derive(JSTraceable, MallocSizeOf)]
104#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
105struct PendingAbortRequest {
106    /// <https://streams.spec.whatwg.org/#pending-abort-request-promise>
107    #[conditional_malloc_size_of]
108    promise: Rc<Promise>,
109
110    /// <https://streams.spec.whatwg.org/#pending-abort-request-reason>
111    #[ignore_malloc_size_of = "mozjs"]
112    reason: Box<Heap<JSVal>>,
113
114    /// <https://streams.spec.whatwg.org/#pending-abort-request-was-already-erroring>
115    was_already_erroring: bool,
116}
117
118/// <https://streams.spec.whatwg.org/#writablestream-state>
119#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
120pub(crate) enum WritableStreamState {
121    #[default]
122    Writable,
123    Closed,
124    Erroring,
125    Errored,
126}
127
128/// <https://streams.spec.whatwg.org/#ws-class>
129#[dom_struct]
130pub struct WritableStream {
131    reflector_: Reflector,
132
133    /// <https://streams.spec.whatwg.org/#writablestream-backpressure>
134    backpressure: Cell<bool>,
135
136    /// <https://streams.spec.whatwg.org/#writablestream-closerequest>
137    #[conditional_malloc_size_of]
138    close_request: DomRefCell<Option<Rc<Promise>>>,
139
140    /// <https://streams.spec.whatwg.org/#writablestream-controller>
141    controller: MutNullableDom<WritableStreamDefaultController>,
142
143    /// <https://streams.spec.whatwg.org/#writablestream-detached>
144    detached: Cell<bool>,
145
146    /// <https://streams.spec.whatwg.org/#writablestream-inflightwriterequest>
147    #[conditional_malloc_size_of]
148    in_flight_write_request: DomRefCell<Option<Rc<Promise>>>,
149
150    /// <https://streams.spec.whatwg.org/#writablestream-inflightcloserequest>
151    #[conditional_malloc_size_of]
152    in_flight_close_request: DomRefCell<Option<Rc<Promise>>>,
153
154    /// <https://streams.spec.whatwg.org/#writablestream-pendingabortrequest>
155    pending_abort_request: DomRefCell<Option<PendingAbortRequest>>,
156
157    /// <https://streams.spec.whatwg.org/#writablestream-state>
158    state: Cell<WritableStreamState>,
159
160    /// <https://streams.spec.whatwg.org/#writablestream-storederror>
161    #[ignore_malloc_size_of = "mozjs"]
162    stored_error: Heap<JSVal>,
163
164    /// <https://streams.spec.whatwg.org/#writablestream-writer>
165    writer: MutNullableDom<WritableStreamDefaultWriter>,
166
167    /// <https://streams.spec.whatwg.org/#writablestream-writerequests>
168    #[conditional_malloc_size_of]
169    write_requests: DomRefCell<VecDeque<Rc<Promise>>>,
170}
171
172impl WritableStream {
173    /// <https://streams.spec.whatwg.org/#initialize-writable-stream>
174    fn new_inherited() -> WritableStream {
175        WritableStream {
176            reflector_: Reflector::new(),
177            backpressure: Default::default(),
178            close_request: Default::default(),
179            controller: Default::default(),
180            detached: Default::default(),
181            in_flight_write_request: Default::default(),
182            in_flight_close_request: Default::default(),
183            pending_abort_request: Default::default(),
184            state: Default::default(),
185            stored_error: Default::default(),
186            writer: Default::default(),
187            write_requests: Default::default(),
188        }
189    }
190
191    pub(crate) fn new_with_proto(
192        cx: &mut JSContext,
193        global: &GlobalScope,
194        proto: Option<SafeHandleObject>,
195    ) -> DomRoot<WritableStream> {
196        reflect_dom_object_with_proto_and_cx(
197            Box::new(WritableStream::new_inherited()),
198            global,
199            proto,
200            cx,
201        )
202    }
203
204    /// Used as part of
205    /// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller>
206    pub(crate) fn assert_no_controller(&self) {
207        assert!(self.controller.get().is_none());
208    }
209
210    /// Used as part of
211    /// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller>
212    pub(crate) fn set_default_controller(&self, controller: &WritableStreamDefaultController) {
213        self.controller.set(Some(controller));
214    }
215
216    pub(crate) fn get_default_controller(&self) -> DomRoot<WritableStreamDefaultController> {
217        self.controller.get().expect("Controller should be set.")
218    }
219
220    pub(crate) fn is_writable(&self) -> bool {
221        matches!(self.state.get(), WritableStreamState::Writable)
222    }
223
224    pub(crate) fn is_erroring(&self) -> bool {
225        matches!(self.state.get(), WritableStreamState::Erroring)
226    }
227
228    pub(crate) fn is_errored(&self) -> bool {
229        matches!(self.state.get(), WritableStreamState::Errored)
230    }
231
232    pub(crate) fn is_closed(&self) -> bool {
233        matches!(self.state.get(), WritableStreamState::Closed)
234    }
235
236    pub(crate) fn has_in_flight_write_request(&self) -> bool {
237        self.in_flight_write_request.borrow().is_some()
238    }
239
240    /// <https://streams.spec.whatwg.org/#writable-stream-has-operation-marked-in-flight>
241    pub(crate) fn has_operations_marked_inflight(&self) -> bool {
242        let in_flight_write_requested = self.in_flight_write_request.borrow().is_some();
243        let in_flight_close_requested = self.in_flight_close_request.borrow().is_some();
244
245        in_flight_write_requested || in_flight_close_requested
246    }
247
248    /// <https://streams.spec.whatwg.org/#writablestream-storederror>
249    pub(crate) fn get_stored_error(&self, mut handle_mut: SafeMutableHandleValue) {
250        handle_mut.set(self.stored_error.get());
251    }
252
253    /// <https://streams.spec.whatwg.org/#writable-stream-finish-erroring>
254    pub(crate) fn finish_erroring(&self, cx: &mut JSContext, global: &GlobalScope) {
255        // Assert: stream.[[state]] is "erroring".
256        assert!(self.is_erroring());
257
258        // Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is false.
259        assert!(!self.has_operations_marked_inflight());
260
261        // Set stream.[[state]] to "errored".
262        self.state.set(WritableStreamState::Errored);
263
264        // Perform ! stream.[[controller]].[[ErrorSteps]]().
265        let Some(controller) = self.controller.get() else {
266            unreachable!("Stream should have a controller.");
267        };
268        controller.perform_error_steps();
269
270        // Let storedError be stream.[[storedError]].
271        rooted!(&in(cx) let mut stored_error = UndefinedValue());
272        self.get_stored_error(stored_error.handle_mut());
273
274        // For each writeRequest of stream.[[writeRequests]]:
275        let write_requests = mem::take(&mut *self.write_requests.borrow_mut());
276        for request in write_requests {
277            // Reject writeRequest with storedError.
278            request.reject(cx, stored_error.handle());
279        }
280
281        // Set stream.[[writeRequests]] to an empty list.
282        // Done above with `drain`.
283
284        // If stream.[[pendingAbortRequest]] is undefined,
285        if self.pending_abort_request.borrow().is_none() {
286            // Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
287            self.reject_close_and_closed_promise_if_needed(cx);
288
289            // Return.
290            return;
291        }
292
293        // Let abortRequest be stream.[[pendingAbortRequest]].
294        // Set stream.[[pendingAbortRequest]] to undefined.
295        rooted!(&in(cx) let pending_abort_request = self.pending_abort_request.borrow_mut().take());
296        if let Some(pending_abort_request) = &*pending_abort_request {
297            // If abortRequest’s was already erroring is true,
298            if pending_abort_request.was_already_erroring {
299                // Reject abortRequest’s promise with storedError.
300                pending_abort_request
301                    .promise
302                    .reject(cx, stored_error.handle());
303
304                // Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
305                self.reject_close_and_closed_promise_if_needed(cx);
306
307                // Return.
308                return;
309            }
310
311            // Let promise be ! stream.[[controller]].[[AbortSteps]](abortRequest’s reason).
312            rooted!(&in(cx) let mut reason = UndefinedValue());
313            reason.set(pending_abort_request.reason.get());
314            let promise = controller.abort_steps(cx, global, reason.handle());
315
316            // Upon fulfillment of promise,
317            rooted!(&in(cx) let mut fulfillment_handler = Some(AbortAlgorithmFulfillmentHandler {
318                stream: Dom::from_ref(self),
319                abort_request_promise: pending_abort_request.promise.clone(),
320            }));
321
322            // Upon rejection of promise with reason r,
323            rooted!(&in(cx) let mut rejection_handler = Some(AbortAlgorithmRejectionHandler {
324                stream: Dom::from_ref(self),
325                abort_request_promise: pending_abort_request.promise.clone(),
326            }));
327
328            let handler = PromiseNativeHandler::new(
329                cx,
330                global,
331                fulfillment_handler.take().map(|h| Box::new(h) as Box<_>),
332                rejection_handler.take().map(|h| Box::new(h) as Box<_>),
333            );
334
335            let mut realm = enter_auto_realm(cx, global);
336            let cx = &mut realm.current_realm();
337            promise.append_native_handler(cx, &handler);
338        }
339    }
340
341    /// <https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed>
342    fn reject_close_and_closed_promise_if_needed(&self, cx: &mut JSContext) {
343        // Assert: stream.[[state]] is "errored".
344        assert!(self.is_errored());
345
346        rooted!(&in(cx) let mut stored_error = UndefinedValue());
347        self.get_stored_error(stored_error.handle_mut());
348
349        // If stream.[[closeRequest]] is not undefined
350        let close_request = self.close_request.borrow_mut().take();
351        if let Some(close_request) = close_request {
352            // Assert: stream.[[inFlightCloseRequest]] is undefined.
353            assert!(self.in_flight_close_request.borrow().is_none());
354
355            // Reject stream.[[closeRequest]] with stream.[[storedError]].
356            close_request.reject_native(cx, &stored_error.handle())
357
358            // Set stream.[[closeRequest]] to undefined.
359            // Done with `take` above.
360        }
361
362        // Let writer be stream.[[writer]].
363        // If writer is not undefined,
364        if let Some(writer) = self.writer.get() {
365            // Reject writer.[[closedPromise]] with stream.[[storedError]].
366            writer.reject_closed_promise_with_stored_error(cx, &stored_error.handle());
367
368            // Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
369            writer.set_close_promise_is_handled(cx);
370        }
371    }
372
373    /// <https://streams.spec.whatwg.org/#writable-stream-close-queued-or-in-flight>
374    pub(crate) fn close_queued_or_in_flight(&self) -> bool {
375        let close_requested = self.close_request.borrow().is_some();
376        let in_flight_close_requested = self.in_flight_close_request.borrow().is_some();
377
378        close_requested || in_flight_close_requested
379    }
380
381    /// <https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write>
382    pub(crate) fn finish_in_flight_write(&self, cx: &mut JSContext) {
383        let Some(in_flight_write_request) = self.in_flight_write_request.borrow_mut().take() else {
384            // Assert: stream.[[inFlightWriteRequest]] is not undefined.
385            unreachable!("Stream should have a write request");
386        };
387
388        // Resolve stream.[[inFlightWriteRequest]] with undefined.
389        in_flight_write_request.resolve_native(cx, &());
390
391        // Set stream.[[inFlightWriteRequest]] to undefined.
392        // Done above with `take`.
393    }
394
395    /// <https://streams.spec.whatwg.org/#writable-stream-start-erroring>
396    pub(crate) fn start_erroring(
397        &self,
398        cx: &mut JSContext,
399        global: &GlobalScope,
400        error: SafeHandleValue,
401    ) {
402        // Assert: stream.[[storedError]] is undefined.
403        assert!(self.stored_error.get().is_undefined());
404
405        // Assert: stream.[[state]] is "writable".
406        assert!(self.is_writable());
407
408        // Let controller be stream.[[controller]].
409        let Some(controller) = self.controller.get() else {
410            // Assert: controller is not undefined.
411            unreachable!("Stream should have a controller.");
412        };
413
414        // Set stream.[[state]] to "erroring".
415        self.state.set(WritableStreamState::Erroring);
416
417        // Set stream.[[storedError]] to reason.
418        self.stored_error.set(*error);
419
420        // Let writer be stream.[[writer]].
421        if let Some(writer) = self.writer.get() {
422            // If writer is not undefined, perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected
423            writer.ensure_ready_promise_rejected(cx, global, error);
424        }
425
426        // If ! WritableStreamHasOperationMarkedInFlight(stream) is false and controller.[[started]] is true
427        if !self.has_operations_marked_inflight() && controller.started() {
428            // perform ! WritableStreamFinishErroring
429            self.finish_erroring(cx, global);
430        }
431    }
432
433    /// <https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection>
434    pub(crate) fn deal_with_rejection(
435        &self,
436        cx: &mut JSContext,
437        global: &GlobalScope,
438        error: SafeHandleValue,
439    ) {
440        // Let state be stream.[[state]].
441
442        // If state is "writable",
443        if self.is_writable() {
444            // Perform ! WritableStreamStartErroring(stream, error).
445            self.start_erroring(cx, global, error);
446
447            // Return.
448            return;
449        }
450
451        // Assert: state is "erroring".
452        assert!(self.is_erroring());
453
454        // Perform ! WritableStreamFinishErroring(stream).
455        self.finish_erroring(cx, global);
456    }
457
458    /// <https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight>
459    pub(crate) fn mark_first_write_request_in_flight(&self) {
460        let mut in_flight_write_request = self.in_flight_write_request.borrow_mut();
461        let mut write_requests = self.write_requests.borrow_mut();
462
463        // Assert: stream.[[inFlightWriteRequest]] is undefined.
464        assert!(in_flight_write_request.is_none());
465
466        // Assert: stream.[[writeRequests]] is not empty.
467        assert!(!write_requests.is_empty());
468
469        // Let writeRequest be stream.[[writeRequests]][0].
470        // Remove writeRequest from stream.[[writeRequests]].
471        let write_request = write_requests.pop_front().unwrap();
472
473        // Set stream.[[inFlightWriteRequest]] to writeRequest.
474        *in_flight_write_request = Some(write_request);
475    }
476
477    /// <https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight>
478    pub(crate) fn mark_close_request_in_flight(&self) {
479        let mut in_flight_close_request = self.in_flight_close_request.borrow_mut();
480        let mut close_request = self.close_request.borrow_mut();
481
482        // Assert: stream.[[inFlightCloseRequest]] is undefined.
483        assert!(in_flight_close_request.is_none());
484
485        // Assert: stream.[[closeRequest]] is not undefined.
486        assert!(close_request.is_some());
487
488        // Let closeRequest be stream.[[closeRequest]].
489        // Set stream.[[closeRequest]] to undefined.
490        let close_request = close_request.take().unwrap();
491
492        // Set stream.[[inFlightCloseRequest]] to closeRequest.
493        *in_flight_close_request = Some(close_request);
494    }
495
496    /// <https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close>
497    pub(crate) fn finish_in_flight_close(&self, cx: &mut JSContext) {
498        let Some(in_flight_close_request) = self.in_flight_close_request.borrow_mut().take() else {
499            // Assert: stream.[[inFlightCloseRequest]] is not undefined.
500            unreachable!("in_flight_close_request must be Some");
501        };
502
503        // Resolve stream.[[inFlightCloseRequest]] with undefined.
504        in_flight_close_request.resolve_native(cx, &());
505
506        // Set stream.[[inFlightCloseRequest]] to undefined.
507        // Done with take above.
508
509        // Assert: stream.[[state]] is "writable" or "erroring".
510        assert!(self.is_writable() || self.is_erroring());
511
512        // If state is "erroring",
513        if self.is_erroring() {
514            // Set stream.[[storedError]] to undefined.
515            self.stored_error.set(UndefinedValue());
516
517            // If stream.[[pendingAbortRequest]] is not undefined,
518            rooted!(&in(cx) let pending_abort_request = self.pending_abort_request.borrow_mut().take());
519            if let Some(pending_abort_request) = &*pending_abort_request {
520                // Resolve stream.[[pendingAbortRequest]]'s promise with undefined.
521                pending_abort_request.promise.resolve_native(cx, &());
522
523                // Set stream.[[pendingAbortRequest]] to undefined.
524                // Done above with `take`.
525            }
526        }
527
528        // Set stream.[[state]] to "closed".
529        self.state.set(WritableStreamState::Closed);
530
531        // Let writer be stream.[[writer]].
532        if let Some(writer) = self.writer.get() {
533            // If writer is not undefined,
534            // resolve writer.[[closedPromise]] with undefined.
535            writer.resolve_closed_promise_with_undefined(cx);
536        }
537
538        // Assert: stream.[[pendingAbortRequest]] is undefined.
539        assert!(self.pending_abort_request.borrow().is_none());
540
541        // Assert: stream.[[storedError]] is undefined.
542        assert!(self.stored_error.get().is_undefined());
543    }
544
545    /// <https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error>
546    pub(crate) fn finish_in_flight_close_with_error(
547        &self,
548        cx: &mut JSContext,
549        global: &GlobalScope,
550        error: SafeHandleValue,
551    ) {
552        let Some(in_flight_close_request) = self.in_flight_close_request.borrow_mut().take() else {
553            // Assert: stream.[[inFlightCloseRequest]] is not undefined.
554            unreachable!("Inflight close request must be defined.");
555        };
556
557        // Reject stream.[[inFlightCloseRequest]] with error.
558        in_flight_close_request.reject_native(cx, &error);
559
560        // Set stream.[[inFlightCloseRequest]] to undefined.
561        // Done above with `take`.
562
563        // Assert: stream.[[state]] is "writable" or "erroring".
564        assert!(self.is_erroring() || self.is_writable());
565
566        // If stream.[[pendingAbortRequest]] is not undefined,
567        rooted!(&in(cx) let pending_abort_request = self.pending_abort_request.borrow_mut().take());
568        if let Some(pending_abort_request) = &*pending_abort_request {
569            // Reject stream.[[pendingAbortRequest]]'s promise with error.
570            pending_abort_request.promise.reject_native(cx, &error);
571
572            // Set stream.[[pendingAbortRequest]] to undefined.
573            // Done above with `take`.
574        }
575
576        // Perform ! WritableStreamDealWithRejection(stream, error).
577        self.deal_with_rejection(cx, global, error);
578    }
579
580    /// <https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error>
581    pub(crate) fn finish_in_flight_write_with_error(
582        &self,
583        cx: &mut JSContext,
584        global: &GlobalScope,
585        error: SafeHandleValue,
586    ) {
587        let Some(in_flight_write_request) = self.in_flight_write_request.borrow_mut().take() else {
588            // Assert: stream.[[inFlightWriteRequest]] is not undefined.
589            unreachable!("Inflight write request must be defined.");
590        };
591
592        // Reject stream.[[inFlightWriteRequest]] with error.
593        in_flight_write_request.reject_native(cx, &error);
594
595        // Set stream.[[inFlightWriteRequest]] to undefined.
596        // Done above with `take`.
597
598        // Assert: stream.[[state]] is "writable" or "erroring".
599        assert!(self.is_erroring() || self.is_writable());
600
601        // Perform ! WritableStreamDealWithRejection(stream, error).
602        self.deal_with_rejection(cx, global, error);
603    }
604
605    pub(crate) fn get_writer(&self) -> Option<DomRoot<WritableStreamDefaultWriter>> {
606        self.writer.get()
607    }
608
609    pub(crate) fn set_writer(&self, writer: Option<&WritableStreamDefaultWriter>) {
610        self.writer.set(writer);
611    }
612
613    pub(crate) fn set_backpressure(&self, backpressure: bool) {
614        self.backpressure.set(backpressure);
615    }
616
617    pub(crate) fn get_backpressure(&self) -> bool {
618        self.backpressure.get()
619    }
620
621    /// <https://streams.spec.whatwg.org/#is-writable-stream-locked>
622    pub(crate) fn is_locked(&self) -> bool {
623        // If stream.[[writer]] is undefined, return false.
624        // Return true.
625        self.get_writer().is_some()
626    }
627
628    /// <https://streams.spec.whatwg.org/#writable-stream-add-write-request>
629    pub(crate) fn add_write_request(
630        &self,
631        cx: &mut JSContext,
632        global: &GlobalScope,
633    ) -> Rc<Promise> {
634        // Assert: ! IsWritableStreamLocked(stream) is true.
635        assert!(self.is_locked());
636
637        // Assert: stream.[[state]] is "writable".
638        assert!(self.is_writable());
639
640        // Let promise be a new promise.
641        let promise = Promise::new(cx, global);
642
643        // Append promise to stream.[[writeRequests]].
644        self.write_requests.borrow_mut().push_back(promise.clone());
645
646        // Return promise.
647        promise
648    }
649
650    // Returns the rooted controller of the stream, if any.
651    pub(crate) fn get_controller(&self) -> Option<DomRoot<WritableStreamDefaultController>> {
652        self.controller.get()
653    }
654
655    /// <https://streams.spec.whatwg.org/#writable-stream-abort>
656    pub(crate) fn abort(
657        &self,
658        cx: &mut CurrentRealm,
659        global: &GlobalScope,
660        provided_reason: SafeHandleValue,
661    ) -> Rc<Promise> {
662        // If stream.[[state]] is "closed" or "errored",
663        if self.is_closed() || self.is_errored() {
664            // return a promise resolved with undefined.
665            return Promise::new_resolved(cx, global, ());
666        }
667
668        // Signal abort on stream.[[controller]].[[abortController]] with reason.
669        self.get_controller()
670            .expect("Stream must have a controller.")
671            .signal_abort(cx, provided_reason);
672
673        // Let state be stream.[[state]].
674        let state = self.state.get();
675
676        // If state is "closed" or "errored", return a promise resolved with undefined.
677        if matches!(
678            state,
679            WritableStreamState::Closed | WritableStreamState::Errored
680        ) {
681            return Promise::new_resolved(cx, global, ());
682        }
683
684        // If stream.[[pendingAbortRequest]] is not undefined,
685        if self.pending_abort_request.borrow().is_some() {
686            // return stream.[[pendingAbortRequest]]'s promise.
687            return self
688                .pending_abort_request
689                .borrow()
690                .as_ref()
691                .expect("Pending abort request must be Some.")
692                .promise
693                .clone();
694        }
695
696        // Assert: state is "writable" or "erroring".
697        assert!(self.is_writable() || self.is_erroring());
698
699        // Let wasAlreadyErroring be false.
700        let mut was_already_erroring = false;
701        rooted!(&in(cx) let undefined_reason = UndefinedValue());
702
703        // If state is "erroring",
704        let reason = if self.is_erroring() {
705            // Set wasAlreadyErroring to true.
706            was_already_erroring = true;
707
708            // Set reason to undefined.
709            undefined_reason.handle()
710        } else {
711            // Use the provided reason.
712            provided_reason
713        };
714
715        // Let promise be a new promise.
716        let promise = Promise::new(cx, global);
717
718        // Set stream.[[pendingAbortRequest]] to a new pending abort request
719        // whose promise is promise,
720        // reason is reason,
721        // and was already erroring is wasAlreadyErroring.
722        *self.pending_abort_request.borrow_mut() = Some(PendingAbortRequest {
723            promise: promise.clone(),
724            reason: Heap::boxed(reason.get()),
725            was_already_erroring,
726        });
727
728        // If wasAlreadyErroring is false,
729        if !was_already_erroring {
730            // perform ! WritableStreamStartErroring(stream, reason)
731            self.start_erroring(cx, global, reason);
732        }
733
734        // Return promise.
735        promise
736    }
737
738    /// <https://streams.spec.whatwg.org/#writable-stream-close>
739    pub(crate) fn close(&self, cx: &mut JSContext, global: &GlobalScope) -> Rc<Promise> {
740        // Let state be stream.[[state]].
741        // If state is "closed" or "errored",
742        if self.is_closed() || self.is_errored() {
743            // return a promise rejected with a TypeError exception.
744            let promise = Promise::new(cx, global);
745            promise.reject_error(cx, Error::Type(c"Stream is closed or errored.".to_owned()));
746            return promise;
747        }
748
749        // Assert: state is "writable" or "erroring".
750        assert!(self.is_writable() || self.is_erroring());
751
752        // Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
753        assert!(!self.close_queued_or_in_flight());
754
755        // Let promise be a new promise.
756        let promise = Promise::new(cx, global);
757
758        // Set stream.[[closeRequest]] to promise.
759        *self.close_request.borrow_mut() = Some(promise.clone());
760
761        // Let writer be stream.[[writer]].
762        // If writer is not undefined,
763        if let Some(writer) = self.writer.get() {
764            // and stream.[[backpressure]] is true,
765            // and state is "writable",
766            if self.get_backpressure() && self.is_writable() {
767                // resolve writer.[[readyPromise]] with undefined.
768                writer.resolve_ready_promise_with_undefined(cx);
769            }
770        }
771
772        // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
773        let Some(controller) = self.controller.get() else {
774            unreachable!("Stream must have a controller.");
775        };
776        controller.close(cx, global);
777
778        // Return promise.
779        promise
780    }
781
782    /// <https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size>
783    /// Note: implement as a stream method, as opposed to a writer one, for convenience.
784    pub(crate) fn get_desired_size(&self) -> Option<f64> {
785        // Let stream be writer.[[stream]].
786        // Stream is `self`.
787
788        // Let state be stream.[[state]].
789        // If state is "errored" or "erroring", return null.
790        if self.is_errored() || self.is_erroring() {
791            return None;
792        }
793
794        // If state is "closed", return 0.
795        if self.is_closed() {
796            return Some(0.);
797        }
798
799        let Some(controller) = self.controller.get() else {
800            unreachable!("Stream must have a controller.");
801        };
802        Some(controller.get_desired_size())
803    }
804
805    /// <https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer>
806    pub(crate) fn aquire_default_writer(
807        &self,
808        cx: &mut CurrentRealm,
809        global: &GlobalScope,
810    ) -> Result<DomRoot<WritableStreamDefaultWriter>, Error> {
811        // Let writer be a new WritableStreamDefaultWriter object.
812        let writer = WritableStreamDefaultWriter::new(cx, global, None);
813
814        // Perform ? SetUpWritableStreamDefaultWriter(writer, stream).
815        writer.setup(cx, self)?;
816
817        // Return writer.
818        Ok(writer)
819    }
820
821    /// <https://streams.spec.whatwg.org/#writable-stream-update-backpressure>
822    pub(crate) fn update_backpressure(
823        &self,
824        cx: &mut JSContext,
825        backpressure: bool,
826        global: &GlobalScope,
827    ) {
828        // Assert: stream.[[state]] is "writable".
829        self.is_writable();
830
831        // Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
832        assert!(!self.close_queued_or_in_flight());
833
834        // Let writer be stream.[[writer]].
835        let writer = self.get_writer();
836
837        if let Some(writer) = writer {
838            // If writer is not undefined
839            if backpressure != self.get_backpressure() {
840                // and backpressure is not stream.[[backpressure]],
841                if backpressure {
842                    // If backpressure is true, set writer.[[readyPromise]] to a new promise.
843                    let promise = Promise::new(cx, global);
844                    writer.set_ready_promise(promise);
845                } else {
846                    // Otherwise,
847                    // Assert: backpressure is false.
848                    assert!(!backpressure);
849                    // Resolve writer.[[readyPromise]] with undefined.
850                    writer.resolve_ready_promise_with_undefined(cx);
851                }
852            }
853        }
854
855        // Set stream.[[backpressure]] to backpressure.
856        self.set_backpressure(backpressure);
857    }
858
859    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
860    pub(crate) fn setup_cross_realm_transform_writable(
861        &self,
862        cx: &mut JSContext,
863        port: &MessagePort,
864    ) {
865        let port_id = port.message_port_id();
866        let global = self.global();
867
868        // Perform ! InitializeWritableStream(stream).
869        // Done in `new_inherited`.
870
871        // Let sizeAlgorithm be an algorithm that returns 1.
872        // Re-ordered because of the need to pass it to `new`.
873        let size_algorithm = extract_size_algorithm(cx, &QueuingStrategy::default());
874
875        // Note: other algorithms defined in the controller at call site.
876
877        // Let backpressurePromise be a new promise.
878        let backpressure_promise = Rc::new(RefCell::new(Some(Promise::new(cx, &global))));
879
880        // Let controller be a new WritableStreamDefaultController.
881        let controller = WritableStreamDefaultController::new(
882            cx,
883            &global,
884            UnderlyingSinkType::Transfer {
885                backpressure_promise: backpressure_promise.clone(),
886                port: Dom::from_ref(port),
887            },
888            1.0,
889            size_algorithm,
890        );
891
892        // Add a handler for port’s message event with the following steps:
893        // Add a handler for port’s messageerror event with the following steps:
894        rooted!(&in(cx) let cross_realm_transform_writable = CrossRealmTransformWritable {
895            controller: Dom::from_ref(&controller),
896            backpressure_promise,
897        });
898        global.note_cross_realm_transform_writable(&cross_realm_transform_writable, port_id);
899
900        // Enable port’s port message queue.
901        port.Start(cx);
902
903        // Perform ! SetUpWritableStreamDefaultController
904        controller
905            .setup(cx, &global, self)
906            .expect("Setup for transfer cannot fail");
907    }
908    /// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink>
909    #[allow(clippy::too_many_arguments)]
910    fn setup_from_underlying_sink(
911        &self,
912        cx: &mut JSContext,
913        global: &GlobalScope,
914        stream: &WritableStream,
915        underlying_sink_obj: SafeHandleObject,
916        underlying_sink: &UnderlyingSink,
917        strategy_hwm: f64,
918        strategy_size: Rc<QueuingStrategySize>,
919    ) -> Result<(), Error> {
920        // Let controller be a new WritableStreamDefaultController.
921
922        // Let startAlgorithm be an algorithm that returns undefined.
923
924        // Let writeAlgorithm be an algorithm that returns a promise resolved with undefined.
925
926        // Let closeAlgorithm be an algorithm that returns a promise resolved with undefined.
927
928        // Let abortAlgorithm be an algorithm that returns a promise resolved with undefined.
929
930        // If underlyingSinkDict["start"] exists, then set startAlgorithm to an algorithm which
931        // returns the result of invoking underlyingSinkDict["start"] with argument
932        // list « controller », exception behavior "rethrow", and callback this value underlyingSink.
933
934        // If underlyingSinkDict["write"] exists, then set writeAlgorithm to an algorithm which
935        // takes an argument chunk and returns the result of invoking underlyingSinkDict["write"]
936        // with argument list « chunk, controller » and callback this value underlyingSink.
937
938        // If underlyingSinkDict["close"] exists, then set closeAlgorithm to an algorithm which
939        // returns the result of invoking underlyingSinkDict["close"] with argument
940        // list «» and callback this value underlyingSink.
941
942        // If underlyingSinkDict["abort"] exists, then set abortAlgorithm to an algorithm which
943        // takes an argument reason and returns the result of invoking underlyingSinkDict["abort"]
944        // with argument list « reason » and callback this value underlyingSink.
945        let controller = WritableStreamDefaultController::new(
946            cx,
947            global,
948            UnderlyingSinkType::new_js(
949                underlying_sink.abort.clone(),
950                underlying_sink.start.clone(),
951                underlying_sink.close.clone(),
952                underlying_sink.write.clone(),
953            ),
954            strategy_hwm,
955            strategy_size,
956        );
957
958        // Note: this must be done before `setup`,
959        // otherwise `thisOb` is null in the start callback.
960        controller.set_underlying_sink_this_object(underlying_sink_obj);
961
962        // Perform ? SetUpWritableStreamDefaultController
963        controller.setup(cx, global, stream)
964    }
965}
966
967/// <https://streams.spec.whatwg.org/#create-writable-stream>
968#[cfg_attr(crown, expect(crown::unrooted_must_root))]
969pub(crate) fn create_writable_stream(
970    cx: &mut JSContext,
971    global: &GlobalScope,
972    writable_high_water_mark: f64,
973    writable_size_algorithm: Rc<QueuingStrategySize>,
974    underlying_sink_type: UnderlyingSinkType,
975) -> Fallible<DomRoot<WritableStream>> {
976    // Assert: ! IsNonNegativeNumber(highWaterMark) is true.
977    assert!(writable_high_water_mark >= 0.0);
978
979    // Let stream be a new WritableStream.
980    // Perform ! InitializeWritableStream(stream).
981    let stream = WritableStream::new_with_proto(cx, global, None);
982
983    // Let controller be a new WritableStreamDefaultController.
984    let controller = WritableStreamDefaultController::new(
985        cx,
986        global,
987        underlying_sink_type,
988        writable_high_water_mark,
989        writable_size_algorithm,
990    );
991
992    // Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm,
993    // closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
994    controller.setup(cx, global, &stream)?;
995
996    // Return stream.
997    Ok(stream)
998}
999
1000impl WritableStreamMethods<crate::DomTypeHolder> for WritableStream {
1001    /// <https://streams.spec.whatwg.org/#ws-constructor>
1002    fn Constructor(
1003        cx: &mut JSContext,
1004        global: &GlobalScope,
1005        proto: Option<SafeHandleObject>,
1006        underlying_sink: Option<*mut JSObject>,
1007        strategy: &QueuingStrategy,
1008    ) -> Fallible<DomRoot<WritableStream>> {
1009        // If underlyingSink is missing, set it to null.
1010        rooted!(&in(cx) let underlying_sink_obj = underlying_sink.unwrap_or(ptr::null_mut()));
1011
1012        // Let underlyingSinkDict be underlyingSink,
1013        // converted to an IDL value of type UnderlyingSink.
1014        let underlying_sink_dict = if !underlying_sink_obj.is_null() {
1015            rooted!(&in(cx) let obj_val = ObjectValue(underlying_sink_obj.get()));
1016            match UnderlyingSink::new(cx, obj_val.handle()) {
1017                Ok(ConversionResult::Success(val)) => val,
1018                Ok(ConversionResult::Failure(error)) => {
1019                    return Err(Error::Type(error.into_owned()));
1020                },
1021                _ => {
1022                    return Err(Error::JSFailed);
1023                },
1024            }
1025        } else {
1026            UnderlyingSink::empty()
1027        };
1028
1029        if !underlying_sink_dict.type_.handle().is_undefined() {
1030            // If underlyingSinkDict["type"] exists, throw a RangeError exception.
1031            return Err(Error::Range(c"type is set".to_owned()));
1032        }
1033
1034        // Perform ! InitializeWritableStream(this).
1035        let stream = WritableStream::new_with_proto(cx, global, proto);
1036
1037        // Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
1038        let size_algorithm = extract_size_algorithm(cx, strategy);
1039
1040        // Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
1041        let high_water_mark = extract_high_water_mark(strategy, 1.0)?;
1042
1043        // Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink,
1044        // underlyingSinkDict, highWaterMark, sizeAlgorithm).
1045        stream.setup_from_underlying_sink(
1046            cx,
1047            global,
1048            &stream,
1049            underlying_sink_obj.handle(),
1050            &underlying_sink_dict,
1051            high_water_mark,
1052            size_algorithm,
1053        )?;
1054
1055        Ok(stream)
1056    }
1057
1058    /// <https://streams.spec.whatwg.org/#ws-locked>
1059    fn Locked(&self) -> bool {
1060        // Return ! IsWritableStreamLocked(this).
1061        self.is_locked()
1062    }
1063
1064    /// <https://streams.spec.whatwg.org/#ws-abort>
1065    fn Abort(&self, cx: &mut CurrentRealm, reason: SafeHandleValue) -> Rc<Promise> {
1066        let global = GlobalScope::from_current_realm(cx);
1067
1068        // If ! IsWritableStreamLocked(this) is true,
1069        if self.is_locked() {
1070            // return a promise rejected with a TypeError exception.
1071            let promise = Promise::new(cx, &global);
1072            promise.reject_error(cx, Error::Type(c"Stream is locked.".to_owned()));
1073            return promise;
1074        }
1075
1076        // Return ! WritableStreamAbort(this, reason).
1077        self.abort(cx, &global, reason)
1078    }
1079
1080    /// <https://streams.spec.whatwg.org/#ws-close>
1081    fn Close(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
1082        let global = GlobalScope::from_current_realm(cx);
1083
1084        // If ! IsWritableStreamLocked(this) is true,
1085        if self.is_locked() {
1086            // return a promise rejected with a TypeError exception.
1087            let promise = Promise::new(cx, &global);
1088            promise.reject_error(cx, Error::Type(c"Stream is locked.".to_owned()));
1089            return promise;
1090        }
1091
1092        // If ! WritableStreamCloseQueuedOrInFlight(this) is true
1093        if self.close_queued_or_in_flight() {
1094            // return a promise rejected with a TypeError exception.
1095            let promise = Promise::new(cx, &global);
1096            promise.reject_error(
1097                cx,
1098                Error::Type(c"Stream has closed queued or in-flight".to_owned()),
1099            );
1100            return promise;
1101        }
1102
1103        // Return ! WritableStreamClose(this).
1104        self.close(cx, &global)
1105    }
1106
1107    /// <https://streams.spec.whatwg.org/#ws-get-writer>
1108    fn GetWriter(
1109        &self,
1110        realm: &mut CurrentRealm,
1111    ) -> Result<DomRoot<WritableStreamDefaultWriter>, Error> {
1112        let global = GlobalScope::from_current_realm(realm);
1113
1114        // Return ? AcquireWritableStreamDefaultWriter(this).
1115        self.aquire_default_writer(realm, &global)
1116    }
1117}
1118
1119impl js::gc::Rootable for CrossRealmTransformWritable {}
1120
1121/// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
1122/// A wrapper to handle `message` and `messageerror` events
1123/// for the port used by the transfered stream.
1124#[derive(Clone, JSTraceable, MallocSizeOf)]
1125#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1126pub(crate) struct CrossRealmTransformWritable {
1127    /// The controller used in the algorithm.
1128    controller: Dom<WritableStreamDefaultController>,
1129
1130    /// The `backpressurePromise` used in the algorithm.
1131    #[ignore_malloc_size_of = "nested Rc"]
1132    backpressure_promise: Rc<RefCell<Option<Rc<Promise>>>>,
1133}
1134
1135impl CrossRealmTransformWritable {
1136    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
1137    /// Add a handler for port’s message event with the following steps:
1138    pub(crate) fn handle_message(
1139        &self,
1140        cx: &mut CurrentRealm,
1141        global: &GlobalScope,
1142        message: SafeHandleValue,
1143    ) {
1144        rooted!(&in(cx) let mut value = UndefinedValue());
1145        let type_string = get_type_and_value_from_message(cx, message, value.handle_mut());
1146
1147        // If type is "pull",
1148        // Done below as the steps are the same for both types.
1149
1150        // Otherwise, if type is "error",
1151        if type_string == "error" {
1152            // Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, value).
1153            self.controller.error_if_needed(cx, value.handle(), global);
1154        }
1155
1156        let backpressure_promise = self.backpressure_promise.borrow_mut().take();
1157
1158        // Note: the below steps are for both "pull" and "error" types.
1159        // If backpressurePromise is not undefined,
1160        if let Some(promise) = backpressure_promise {
1161            // Resolve backpressurePromise with undefined.
1162            promise.resolve_native(cx, &());
1163
1164            // Set backpressurePromise to undefined.
1165            // Done above with `take`.
1166        }
1167    }
1168
1169    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
1170    /// Add a handler for port’s messageerror event with the following steps:
1171    pub(crate) fn handle_error(
1172        &self,
1173        cx: &mut CurrentRealm,
1174        global: &GlobalScope,
1175        port: &MessagePort,
1176    ) {
1177        // Let error be a new "DataCloneError" DOMException.
1178        let error = DOMException::new(cx, global, DOMErrorName::DataCloneError);
1179        rooted!(&in(cx) let mut rooted_error = UndefinedValue());
1180        error.safe_to_jsval(cx, rooted_error.handle_mut());
1181
1182        // Perform ! CrossRealmTransformSendError(port, error).
1183        port.cross_realm_transform_send_error(cx, rooted_error.handle());
1184
1185        // Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, error).
1186        self.controller
1187            .error_if_needed(cx, rooted_error.handle(), global);
1188
1189        // Disentangle port.
1190        global.disentangle_port(cx, port);
1191    }
1192}
1193
1194/// <https://streams.spec.whatwg.org/#ws-transfer>
1195impl Transferable for WritableStream {
1196    type Index = MessagePortIndex;
1197    type Data = MessagePortImpl;
1198
1199    /// <https://streams.spec.whatwg.org/#ref-for-transfer-steps①>
1200    fn transfer(&self, cx: &mut JSContext) -> Fallible<(MessagePortId, MessagePortImpl)> {
1201        // Step 1. If ! IsWritableStreamLocked(value) is true, throw a
1202        // "DataCloneError" DOMException.
1203        if self.is_locked() {
1204            return Err(Error::DataClone(None));
1205        }
1206
1207        let global = self.global();
1208        let mut realm = enter_auto_realm(cx, &*global);
1209        let mut realm = realm.current_realm();
1210        let cx = &mut realm;
1211
1212        // Step 2. Let port1 be a new MessagePort in the current Realm.
1213        let port_1 = MessagePort::new(cx, &global);
1214        global.track_message_port(&port_1, None);
1215
1216        // Step 3. Let port2 be a new MessagePort in the current Realm.
1217        let port_2 = MessagePort::new(cx, &global);
1218        global.track_message_port(&port_2, None);
1219
1220        // Step 4. Entangle port1 and port2.
1221        global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id());
1222
1223        // Step 5. Let readable be a new ReadableStream in the current Realm.
1224        let readable = ReadableStream::new_with_proto(cx, &global, None);
1225
1226        // Step 6. Perform ! SetUpCrossRealmTransformReadable(readable, port1).
1227        readable.setup_cross_realm_transform_readable(cx, &port_1);
1228
1229        // Step 7. Let promise be ! ReadableStreamPipeTo(readable, value, false, false, false).
1230        let promise = readable.pipe_to(cx, &global, self, false, false, false, None);
1231
1232        // Step 8. Set promise.[[PromiseIsHandled]] to true.
1233        promise.set_promise_is_handled(cx);
1234
1235        // Step 9. Set dataHolder.[[port]] to ! StructuredSerializeWithTransfer(port2, « port2 »).
1236        port_2.transfer(cx)
1237    }
1238
1239    /// <https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps①>
1240    fn transfer_receive(
1241        cx: &mut JSContext,
1242        owner: &GlobalScope,
1243        id: MessagePortId,
1244        port_impl: MessagePortImpl,
1245    ) -> Result<DomRoot<Self>, ()> {
1246        // Their transfer-receiving steps, given dataHolder and value, are:
1247        // Note: dataHolder is used in `structuredclone.rs`, and value is created here.
1248        let value = WritableStream::new_with_proto(cx, owner, None);
1249
1250        // Step 1. Let deserializedRecord be !
1251        // StructuredDeserializeWithTransfer(dataHolder.[[port]], the current
1252        // Realm).
1253        // Done with the `Deserialize` derive of `MessagePortImpl`.
1254
1255        // Step 2. Let port be deserializedRecord.[[Deserialized]].
1256        let transferred_port = MessagePort::transfer_receive(cx, owner, id, port_impl)?;
1257
1258        // Step 3. Perform ! SetUpCrossRealmTransformWritable(value, port).
1259        value.setup_cross_realm_transform_writable(cx, &transferred_port);
1260        Ok(value)
1261    }
1262
1263    /// Note: we are relying on the port transfer, so the data returned here are related to the port.
1264    fn serialized_storage<'a>(
1265        data: StructuredData<'a, '_>,
1266    ) -> &'a mut Option<FxHashMap<MessagePortId, Self::Data>> {
1267        match data {
1268            StructuredData::Reader(r) => &mut r.port_impls,
1269            StructuredData::Writer(w) => &mut w.ports,
1270        }
1271    }
1272}