Skip to main content

script/dom/stream/
readablestream.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 http://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, RefCell};
6use std::collections::VecDeque;
7use std::ptr::{self};
8use std::rc::Rc;
9
10use dom_struct::dom_struct;
11use js::context::JSContext;
12use js::conversions::{FromJSValConvertible, ToJSValConvertible};
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 js::typedarray::{ArrayBufferViewU8, Uint8};
21use rustc_hash::FxHashMap;
22use servo_base::generic_channel::GenericSharedMemory;
23use servo_base::id::{MessagePortId, MessagePortIndex};
24use servo_constellation_traits::MessagePortImpl;
25
26use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
27use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{
28    ReadableStreamGetReaderOptions, ReadableStreamMethods, ReadableStreamReaderMode,
29    ReadableWritablePair, StreamPipeOptions,
30};
31use script_bindings::str::DOMString;
32
33use crate::dom::domexception::{DOMErrorName, DOMException};
34use crate::dom::encoding::textdecoderstream::TextDecoderStream;
35use script_bindings::codegen::GenericBindings::TextDecoderStreamBinding::TextDecoderStreamMethods;
36use script_bindings::conversions::{is_array_like, StringificationBehavior};
37use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
38use crate::dom::abortsignal::{AbortAlgorithm, AbortSignal};
39use crate::dom::bindings::codegen::Bindings::ReadableStreamDefaultReaderBinding::ReadableStreamDefaultReaderMethods;
40use crate::dom::bindings::codegen::Bindings::ReadableStreamDefaultControllerBinding::ReadableStreamDefaultController_Binding::ReadableStreamDefaultControllerMethods;
41use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource;
42use crate::dom::bindings::conversions::{ConversionBehavior, ConversionResult, get_property, get_property_jsval};
43use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
44use crate::dom::bindings::codegen::GenericBindings::WritableStreamDefaultWriterBinding::WritableStreamDefaultWriter_Binding::WritableStreamDefaultWriterMethods;
45use crate::dom::stream::writablestream::WritableStream;
46use crate::dom::bindings::codegen::UnionTypes::ReadableStreamDefaultReaderOrReadableStreamBYOBReader as ReadableStreamReader;
47use crate::dom::bindings::reflector::DomGlobal;
48use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
49use crate::dom::bindings::root::{DomRoot, MutNullableDom, Dom};
50use crate::dom::bindings::trace::RootedTraceableBox;
51use crate::dom::stream::byteteeunderlyingsource::{ByteTeeCancelAlgorithm, ByteTeePullAlgorithm, ByteTeeUnderlyingSource};
52use crate::dom::stream::countqueuingstrategy::{extract_high_water_mark, extract_size_algorithm};
53use crate::dom::stream::readablestreamgenericreader::ReadableStreamGenericReader;
54use crate::dom::globalscope::GlobalScope;
55use crate::dom::promise::{wait_for_all_promise, Promise};
56use crate::dom::stream::readablebytestreamcontroller::ReadableByteStreamController;
57use crate::dom::stream::readablestreambyobreader::ReadableStreamBYOBReader;
58use crate::dom::stream::readablestreamdefaultcontroller::ReadableStreamDefaultController;
59use crate::dom::stream::readablestreamdefaultreader::{ReadRequest, ReadableStreamDefaultReader};
60use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeCancelAlgorithm;
61use crate::dom::types::DefaultTeeUnderlyingSource;
62use crate::dom::stream::underlyingsourcecontainer::UnderlyingSourceType;
63use crate::dom::stream::writablestreamdefaultwriter::WritableStreamDefaultWriter;
64use script_bindings::codegen::GenericBindings::MessagePortBinding::MessagePortMethods;
65use crate::dom::messageport::MessagePort;
66use crate::realms::{enter_auto_realm};
67use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
68use crate::dom::bindings::transferable::Transferable;
69use crate::dom::bindings::structuredclone::StructuredData;
70
71use super::readablestreambyobreader::ReadIntoRequest;
72use crate::dom::bindings::buffer_source::{HeapBufferSource, create_buffer_source};
73
74/// State Machine for `PipeTo`.
75#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
76enum PipeToState {
77    /// The starting state
78    #[default]
79    Starting,
80    /// Waiting for the writer to be ready
81    PendingReady,
82    /// Waiting for a read to resolve.
83    PendingRead,
84    /// Waiting for all pending writes to finish,
85    /// as part of shutting down with an optional action.
86    ShuttingDownWithPendingWrites(Option<ShutdownAction>),
87    /// When shutting down with an action,
88    /// waiting for the action to complete,
89    /// at which point we can `finalize`.
90    ShuttingDownPendingAction,
91    /// The pipe has been finalized,
92    /// no further actions should be performed.
93    Finalized,
94}
95
96/// <https://streams.spec.whatwg.org/#rs-pipeTo-shutdown-with-action>
97#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
98enum ShutdownAction {
99    /// <https://streams.spec.whatwg.org/#writable-stream-abort>
100    WritableStreamAbort,
101    /// <https://streams.spec.whatwg.org/#readable-stream-cancel>
102    ReadableStreamCancel,
103    /// <https://streams.spec.whatwg.org/#writable-stream-default-writer-close-with-error-propagation>
104    WritableStreamDefaultWriterCloseWithErrorPropagation,
105    /// <https://streams.spec.whatwg.org/#ref-for-rs-pipeTo-shutdown-with-action>
106    Abort,
107}
108
109impl js::gc::Rootable for PipeTo {}
110
111/// The "in parallel, but not really" part of
112/// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
113///
114/// Note: the spec is flexible about how this is done, but requires the following constraints to apply:
115/// - Public API must not be used: we'll only use Rust.
116/// - Backpressure must be enforced: we'll only read from source when dest is ready.
117/// - Shutdown must stop activity: we'll do this together with the below.
118/// - Error and close states must be propagated: we'll do this by checking these states at every step.
119#[derive(Clone, JSTraceable, MallocSizeOf)]
120#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
121pub(crate) struct PipeTo {
122    /// <https://streams.spec.whatwg.org/#ref-for-readablestream%E2%91%A7%E2%91%A0>
123    reader: Dom<ReadableStreamDefaultReader>,
124
125    /// <https://streams.spec.whatwg.org/#ref-for-acquire-writable-stream-default-writer>
126    writer: Dom<WritableStreamDefaultWriter>,
127
128    /// Pending writes are needed when shutting down(with an action),
129    /// because we can only finalize when all writes are finished.
130    #[ignore_malloc_size_of = "nested Rc"]
131    pending_writes: Rc<RefCell<VecDeque<Rc<Promise>>>>,
132
133    /// The state machine.
134    #[conditional_malloc_size_of]
135    #[no_trace]
136    state: Rc<RefCell<PipeToState>>,
137
138    /// <https://streams.spec.whatwg.org/#readablestream-pipe-to-preventabort>
139    prevent_abort: bool,
140
141    /// <https://streams.spec.whatwg.org/#readablestream-pipe-to-preventcancel>
142    prevent_cancel: bool,
143
144    /// <https://streams.spec.whatwg.org/#readablestream-pipe-to-preventclose>
145    prevent_close: bool,
146
147    /// The `shuttingDown` variable of
148    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
149    #[conditional_malloc_size_of]
150    shutting_down: Rc<Cell<bool>>,
151
152    /// The abort reason of the abort signal,
153    /// stored here because we must keep it across a microtask.
154    #[ignore_malloc_size_of = "mozjs"]
155    abort_reason: Rc<Heap<JSVal>>,
156
157    /// The error potentially passed to shutdown,
158    /// stored here because we must keep it across a microtask.
159    #[ignore_malloc_size_of = "mozjs"]
160    shutdown_error: Rc<RefCell<Option<Heap<JSVal>>>>,
161
162    /// The promise returned by a shutdown action.
163    /// We keep it to only continue when it is not pending anymore.
164    #[ignore_malloc_size_of = "nested Rc"]
165    shutdown_action_promise: Rc<RefCell<Option<Rc<Promise>>>>,
166
167    /// The promise resolved or rejected at
168    /// <https://streams.spec.whatwg.org/#rs-pipeTo-finalize>
169    #[conditional_malloc_size_of]
170    result_promise: Rc<Promise>,
171}
172
173impl PipeTo {
174    /// Run the `abortAlgorithm` defined at
175    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
176    pub(crate) fn abort_with_reason(
177        &self,
178        cx: &mut CurrentRealm,
179        global: &GlobalScope,
180        reason: SafeHandleValue,
181    ) {
182        // Abort should do nothing if we are already shutting down.
183        if self.shutting_down.get() {
184            return;
185        }
186
187        // Let error be signal’s abort reason.
188        // Note: storing it because it may need to be kept across a microtask,
189        // and see the note below as to why it is kept separately from `shutdown_error`.
190        self.abort_reason.set(reason.get());
191
192        // Note: setting the error now,
193        // will result in a rejection of the pipe promise, with this error.
194        // Unless any shutdown action raise their own error,
195        // in which case this error will be overwritten by the shutdown action error.
196        self.set_shutdown_error(reason);
197
198        // Let actions be an empty ordered set.
199        // Note: the actions are defined, and performed, inside `shutdown_with_an_action`.
200
201        // Shutdown with an action consisting of getting a promise to wait for all of the actions in actions,
202        // and with error.
203        self.shutdown(cx, global, Some(ShutdownAction::Abort));
204    }
205}
206
207impl Callback for PipeTo {
208    /// The pipe makes progress one microtask at a time.
209    /// Note: we use one struct as the callback for all promises,
210    /// and for both of their reactions.
211    ///
212    /// The context of the callback is determined from:
213    /// - the current state.
214    /// - the type of `result`.
215    /// - the state of a stored promise(in some cases).
216    fn callback(&self, cx: &mut CurrentRealm, result: SafeHandleValue) {
217        let global = self.reader.global();
218
219        // Note: we only care about the result of writes when they are rejected,
220        // and the error is accessed not through handlers,
221        // but directly using `dest.get_stored_error`.
222        // So we must mark rejected promises as handled
223        // to prevent unhandled rejection errors.
224        self.pending_writes.borrow_mut().retain(|p| {
225            let pending = p.is_pending();
226            if !pending {
227                p.set_promise_is_handled(cx);
228            }
229            pending
230        });
231
232        // Note: cloning to prevent re-borrow in methods called below.
233        let state_before_checks = self.state.borrow().clone();
234
235        // Note: if we are in a `PendingRead` state,
236        // and the source is closed,
237        // we try to write chunks before doing any shutdown,
238        // which is necessary to implement the
239        // "If any chunks have been read but not yet written, write them to dest."
240        // part of shutdown.
241        if state_before_checks == PipeToState::PendingRead {
242            let source = self.reader.get_stream().expect("Source stream must be set");
243            if source.is_closed() {
244                let dest = self
245                    .writer
246                    .get_stream()
247                    .expect("Destination stream must be set");
248
249                // If dest.[[state]] is "writable",
250                // and ! WritableStreamCloseQueuedOrInFlight(dest) is false,
251                if dest.is_writable() && !dest.close_queued_or_in_flight() {
252                    let Ok(done) = get_read_promise_done(cx, &result) else {
253                        // This is the case that the microtask ran in reaction
254                        // to the closed promise of the reader,
255                        // so we should wait for subsequent chunks,
256                        // and skip the shutdown below
257                        // (reader is closed, but there are still pending reads).
258                        // Shutdown will happen when the last chunk has been received.
259                        return;
260                    };
261
262                    if !done {
263                        // If any chunks have been read but not yet written, write them to dest.
264                        self.write_chunk(cx, &global, result);
265                    }
266                }
267            }
268        }
269
270        self.check_and_propagate_errors_forward(cx, &global);
271        self.check_and_propagate_errors_backward(cx, &global);
272        self.check_and_propagate_closing_forward(cx, &global);
273        self.check_and_propagate_closing_backward(cx, &global);
274
275        // Note: cloning to prevent re-borrow in methods called below.
276        let state = self.state.borrow().clone();
277
278        // If we switched to a shutdown state,
279        // return.
280        // Progress will be made at the next tick.
281        if state != state_before_checks {
282            return;
283        }
284
285        match state {
286            PipeToState::Starting => unreachable!("PipeTo should not be in the Starting state."),
287            PipeToState::PendingReady => {
288                // Read a chunk.
289                self.read_chunk(cx, &global);
290            },
291            PipeToState::PendingRead => {
292                // Write the chunk.
293                self.write_chunk(cx, &global, result);
294
295                // An early return is necessary if the write algorithm aborted the pipe.
296                if self.shutting_down.get() {
297                    return;
298                }
299
300                // Wait for the writer to be ready again.
301                self.wait_for_writer_ready(cx, &global);
302            },
303            PipeToState::ShuttingDownWithPendingWrites(action) => {
304                // Wait until every chunk that has been read has been written
305                // (i.e. the corresponding promises have settled).
306                if let Some(write) = self.pending_writes.borrow_mut().front().cloned() {
307                    self.wait_on_pending_write(cx, &global, write);
308                    return;
309                }
310
311                // Note: error is stored in `self.shutdown_error`.
312                if let Some(action) = action {
313                    // Let p be the result of performing action.
314                    self.perform_action(cx, &global, action);
315                } else {
316                    // Finalize, passing along error if it was given.
317                    self.finalize(cx, &global);
318                }
319            },
320            PipeToState::ShuttingDownPendingAction => {
321                let Some(ref promise) = *self.shutdown_action_promise.borrow() else {
322                    unreachable!();
323                };
324                if promise.is_pending() {
325                    // While waiting for the action to complete,
326                    // we may get callbacks for other promises(closed, ready),
327                    // and we should ignore those.
328                    return;
329                }
330
331                let is_array_like = {
332                    if !result.is_object() {
333                        false
334                    } else {
335                        is_array_like::<crate::DomTypeHolder>(cx, result)
336                    }
337                };
338
339                // Finalize, passing along error if it was given.
340                if !result.is_undefined() && !is_array_like {
341                    // Most actions either resolve with undefined,
342                    // or reject with an error,
343                    // and the error should be used when finalizing.
344                    // One exception is the `Abort` action,
345                    // which resolves with a list of undefined values.
346
347                    // If `result` isn't undefined or array-like,
348                    // then it is an error
349                    // and should overwrite the current shutdown error.
350                    self.set_shutdown_error(result);
351                }
352                self.finalize(cx, &global);
353            },
354            PipeToState::Finalized => {},
355        }
356    }
357}
358
359impl PipeTo {
360    /// Setting shutdown error in a way that ensures it isn't
361    /// moved after it has been set.
362    fn set_shutdown_error(&self, error: SafeHandleValue) {
363        *self.shutdown_error.borrow_mut() = Some(Heap::default());
364        let Some(ref heap) = *self.shutdown_error.borrow() else {
365            unreachable!("Option set to Some(heap) above.");
366        };
367        heap.set(error.get())
368    }
369
370    /// Wait for the writer to be ready,
371    /// which implements the constraint that backpressure must be enforced.
372    fn wait_for_writer_ready(&self, cx: &mut CurrentRealm, global: &GlobalScope) {
373        {
374            let mut state = self.state.borrow_mut();
375            *state = PipeToState::PendingReady;
376        }
377
378        let ready_promise = self.writer.Ready();
379        if ready_promise.is_fulfilled() {
380            self.read_chunk(cx, global);
381        } else {
382            let handler = PromiseNativeHandler::new(
383                cx,
384                global,
385                Some(Box::new(self.clone())),
386                Some(Box::new(self.clone())),
387            );
388            ready_promise.append_native_handler(cx, &handler);
389
390            // Note: if the writer is not ready,
391            // in order to ensure progress we must
392            // also react to the closure of the source(because source may close empty).
393            let closed_promise = self.reader.Closed();
394            closed_promise.append_native_handler(cx, &handler);
395        }
396    }
397
398    /// Read a chunk
399    fn read_chunk(&self, cx: &mut CurrentRealm, global: &GlobalScope) {
400        *self.state.borrow_mut() = PipeToState::PendingRead;
401        let chunk_promise = self.reader.Read(cx);
402        let handler = PromiseNativeHandler::new(
403            cx,
404            global,
405            Some(Box::new(self.clone())),
406            Some(Box::new(self.clone())),
407        );
408        chunk_promise.append_native_handler(cx, &handler);
409
410        // Note: in order to ensure progress we must
411        // also react to the closure of the destination.
412        let ready_promise = self.writer.Closed();
413        ready_promise.append_native_handler(cx, &handler);
414    }
415
416    /// Try to write a chunk using the jsval, and returns wether it succeeded
417    // It will fail if it is the last `done` chunk, or if it is not a chunk at all.
418    fn write_chunk(
419        &self,
420        cx: &mut JSContext,
421        global: &GlobalScope,
422        chunk: SafeHandleValue,
423    ) -> bool {
424        if chunk.is_object() {
425            rooted!(&in(cx) let object = chunk.to_object());
426            rooted!(&in(cx) let mut bytes = UndefinedValue());
427            get_property_jsval(cx, object.handle(), c"value", bytes.handle_mut())
428                .expect("Chunk should have a value.");
429
430            // Write the chunk.
431            let write_promise = self.writer.write(cx, global, bytes.handle());
432            self.pending_writes.borrow_mut().push_back(write_promise);
433            return true;
434        }
435        false
436    }
437
438    /// Only as part of shutting-down do we wait on pending writes
439    /// (backpressure is communicated not through pending writes
440    /// but through the readiness of the writer).
441    fn wait_on_pending_write(
442        &self,
443        cx: &mut CurrentRealm,
444        global: &GlobalScope,
445        promise: Rc<Promise>,
446    ) {
447        let handler = PromiseNativeHandler::new(
448            cx,
449            global,
450            Some(Box::new(self.clone())),
451            Some(Box::new(self.clone())),
452        );
453        promise.append_native_handler(cx, &handler);
454    }
455
456    /// Errors must be propagated forward part of
457    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
458    fn check_and_propagate_errors_forward(&self, cx: &mut CurrentRealm, global: &GlobalScope) {
459        // An early return is necessary if we are shutting down,
460        // because in that case the source can already have been set to none.
461        if self.shutting_down.get() {
462            return;
463        }
464
465        // if source.[[state]] is or becomes "errored", then
466        let source = self
467            .reader
468            .get_stream()
469            .expect("Reader should still have a stream");
470        if source.is_errored() {
471            rooted!(&in(cx) let mut source_error = UndefinedValue());
472            source.get_stored_error(source_error.handle_mut());
473            self.set_shutdown_error(source_error.handle());
474
475            // If preventAbort is false,
476            if !self.prevent_abort {
477                // shutdown with an action of ! WritableStreamAbort(dest, source.[[storedError]])
478                // and with source.[[storedError]].
479                self.shutdown(cx, global, Some(ShutdownAction::WritableStreamAbort))
480            } else {
481                // Otherwise, shutdown with source.[[storedError]].
482                self.shutdown(cx, global, None);
483            }
484        }
485    }
486
487    /// Errors must be propagated backward part of
488    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
489    fn check_and_propagate_errors_backward(&self, cx: &mut CurrentRealm, global: &GlobalScope) {
490        // An early return is necessary if we are shutting down,
491        // because in that case the destination can already have been set to none.
492        if self.shutting_down.get() {
493            return;
494        }
495
496        // if dest.[[state]] is or becomes "errored", then
497        let dest = self
498            .writer
499            .get_stream()
500            .expect("Writer should still have a stream");
501        if dest.is_errored() {
502            rooted!(&in(cx) let mut dest_error = UndefinedValue());
503            dest.get_stored_error(dest_error.handle_mut());
504            self.set_shutdown_error(dest_error.handle());
505
506            // If preventCancel is false,
507            if !self.prevent_cancel {
508                // shutdown with an action of ! ReadableStreamCancel(source, dest.[[storedError]])
509                // and with dest.[[storedError]].
510                self.shutdown(cx, global, Some(ShutdownAction::ReadableStreamCancel))
511            } else {
512                // Otherwise, shutdown with dest.[[storedError]].
513                self.shutdown(cx, global, None);
514            }
515        }
516    }
517
518    /// Closing must be propagated forward part of
519    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
520    fn check_and_propagate_closing_forward(&self, cx: &mut CurrentRealm, global: &GlobalScope) {
521        // An early return is necessary if we are shutting down,
522        // because in that case the source can already have been set to none.
523        if self.shutting_down.get() {
524            return;
525        }
526
527        // if source.[[state]] is or becomes "closed", then
528        let source = self
529            .reader
530            .get_stream()
531            .expect("Reader should still have a stream");
532        if source.is_closed() {
533            // If preventClose is false,
534            if !self.prevent_close {
535                // shutdown with an action of ! WritableStreamAbort(dest, source.[[storedError]])
536                // and with source.[[storedError]].
537                self.shutdown(
538                    cx,
539                    global,
540                    Some(ShutdownAction::WritableStreamDefaultWriterCloseWithErrorPropagation),
541                )
542            } else {
543                // Otherwise, shutdown.
544                self.shutdown(cx, global, None);
545            }
546        }
547    }
548
549    /// Closing must be propagated backward part of
550    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
551    fn check_and_propagate_closing_backward(&self, cx: &mut CurrentRealm, global: &GlobalScope) {
552        // An early return is necessary if we are shutting down,
553        // because in that case the destination can already have been set to none.
554        if self.shutting_down.get() {
555            return;
556        }
557
558        // if ! WritableStreamCloseQueuedOrInFlight(dest) is true
559        // or dest.[[state]] is "closed"
560        let dest = self
561            .writer
562            .get_stream()
563            .expect("Writer should still have a stream");
564        if dest.close_queued_or_in_flight() || dest.is_closed() {
565            // Assert: no chunks have been read or written.
566            // Note: unclear how to perform this assertion.
567
568            // Let destClosed be a new TypeError.
569            rooted!(&in(cx) let mut dest_closed = UndefinedValue());
570            let error =
571                Error::Type(c"Destination is closed or has closed queued or in flight".to_owned());
572            error.to_jsval(cx, global, dest_closed.handle_mut());
573            self.set_shutdown_error(dest_closed.handle());
574
575            // If preventCancel is false,
576            if !self.prevent_cancel {
577                // shutdown with an action of ! ReadableStreamCancel(source, destClosed)
578                // and with destClosed.
579                self.shutdown(cx, global, Some(ShutdownAction::ReadableStreamCancel))
580            } else {
581                // Otherwise, shutdown with destClosed.
582                self.shutdown(cx, global, None);
583            }
584        }
585    }
586
587    /// <https://streams.spec.whatwg.org/#rs-pipeTo-shutdown-with-action>
588    /// <https://streams.spec.whatwg.org/#rs-pipeTo-shutdown>
589    /// Combined into one method with an optional action.
590    fn shutdown(
591        &self,
592        cx: &mut CurrentRealm,
593        global: &GlobalScope,
594        action: Option<ShutdownAction>,
595    ) {
596        // If shuttingDown is true, abort these substeps.
597        // Set shuttingDown to true.
598        if !self.shutting_down.replace(true) {
599            let dest = self.writer.get_stream().expect("Stream must be set");
600            // If dest.[[state]] is "writable",
601            // and ! WritableStreamCloseQueuedOrInFlight(dest) is false,
602            if dest.is_writable() && !dest.close_queued_or_in_flight() {
603                // If any chunks have been read but not yet written, write them to dest.
604                // Done at the top of `Callback`.
605
606                // Wait until every chunk that has been read has been written
607                // (i.e. the corresponding promises have settled).
608                if let Some(write) = self.pending_writes.borrow_mut().front() {
609                    *self.state.borrow_mut() = PipeToState::ShuttingDownWithPendingWrites(action);
610                    self.wait_on_pending_write(cx, global, write.clone());
611                    return;
612                }
613            }
614
615            // Note: error is stored in `self.shutdown_error`.
616            if let Some(action) = action {
617                // Let p be the result of performing action.
618                self.perform_action(cx, global, action);
619            } else {
620                // Finalize, passing along error if it was given.
621                self.finalize(cx, global);
622            }
623        }
624    }
625
626    /// The perform action part of
627    /// <https://streams.spec.whatwg.org/#rs-pipeTo-shutdown-with-action>
628    fn perform_action(&self, cx: &mut CurrentRealm, global: &GlobalScope, action: ShutdownAction) {
629        rooted!(&in(cx) let mut error = UndefinedValue());
630        if let Some(shutdown_error) = self.shutdown_error.borrow().as_ref() {
631            error.set(shutdown_error.get());
632        }
633
634        *self.state.borrow_mut() = PipeToState::ShuttingDownPendingAction;
635
636        // Let p be the result of performing action.
637        let promise = match action {
638            ShutdownAction::WritableStreamAbort => {
639                let dest = self.writer.get_stream().expect("Stream must be set");
640                dest.abort(cx, global, error.handle())
641            },
642            ShutdownAction::ReadableStreamCancel => {
643                let source = self
644                    .reader
645                    .get_stream()
646                    .expect("Reader should have a stream.");
647                source.cancel(cx, global, error.handle())
648            },
649            ShutdownAction::WritableStreamDefaultWriterCloseWithErrorPropagation => {
650                self.writer.close_with_error_propagation(cx, global)
651            },
652            ShutdownAction::Abort => {
653                // Note: implementation of the `abortAlgorithm`
654                // of the signal associated with this piping operation.
655
656                // Let error be signal’s abort reason.
657                rooted!(&in(cx) let mut error = UndefinedValue());
658                error.set(self.abort_reason.get());
659
660                // Let actions be an empty ordered set.
661                let mut actions = vec![];
662
663                // If preventAbort is false, append the following action to actions:
664                if !self.prevent_abort {
665                    let dest = self
666                        .writer
667                        .get_stream()
668                        .expect("Destination stream must be set");
669
670                    // If dest.[[state]] is "writable",
671                    let promise = if dest.is_writable() {
672                        // return ! WritableStreamAbort(dest, error)
673                        dest.abort(cx, global, error.handle())
674                    } else {
675                        // Otherwise, return a promise resolved with undefined.
676                        Promise::new_resolved(cx, global, ())
677                    };
678                    actions.push(promise);
679                }
680
681                // If preventCancel is false, append the following action action to actions:
682                if !self.prevent_cancel {
683                    let source = self.reader.get_stream().expect("Source stream must be set");
684
685                    // If source.[[state]] is "readable",
686                    let promise = if source.is_readable() {
687                        // return ! ReadableStreamCancel(source, error).
688                        source.cancel(cx, global, error.handle())
689                    } else {
690                        // Otherwise, return a promise resolved with undefined.
691                        Promise::new_resolved(cx, global, ())
692                    };
693                    actions.push(promise);
694                }
695
696                // Shutdown with an action consisting
697                // of getting a promise to wait for all of the actions in actions,
698                // and with error.
699                wait_for_all_promise(cx, global, actions)
700            },
701        };
702
703        // Upon fulfillment of p, finalize, passing along originalError if it was given.
704        // Upon rejection of p with reason newError, finalize with newError.
705        let handler = PromiseNativeHandler::new(
706            cx,
707            global,
708            Some(Box::new(self.clone())),
709            Some(Box::new(self.clone())),
710        );
711        promise.append_native_handler(cx, &handler);
712        *self.shutdown_action_promise.borrow_mut() = Some(promise);
713    }
714
715    /// <https://streams.spec.whatwg.org/#rs-pipeTo-finalize>
716    fn finalize(&self, cx: &mut JSContext, global: &GlobalScope) {
717        *self.state.borrow_mut() = PipeToState::Finalized;
718
719        // Perform ! WritableStreamDefaultWriterRelease(writer).
720        self.writer.release(cx, global);
721
722        // If reader implements ReadableStreamBYOBReader,
723        // perform ! ReadableStreamBYOBReaderRelease(reader).
724        // TODO.
725
726        // Otherwise, perform ! ReadableStreamDefaultReaderRelease(reader).
727        self.reader
728            .release(cx)
729            .expect("Releasing the reader should not fail");
730
731        // If signal is not undefined, remove abortAlgorithm from signal.
732        // Note: since `self.shutdown` is true at this point,
733        // the abort algorithm is a no-op,
734        // so for now not implementing this step.
735
736        if let Some(shutdown_error) = self.shutdown_error.borrow().as_ref() {
737            rooted!(&in(cx) let mut error = UndefinedValue());
738            error.set(shutdown_error.get());
739            // If error was given, reject promise with error.
740            self.result_promise.reject_native(cx, &error.handle());
741        } else {
742            // Otherwise, resolve promise with undefined.
743            self.result_promise.resolve_native(cx, &());
744        }
745    }
746}
747
748/// The fulfillment handler for the reacting to sourceCancelPromise part of
749/// <https://streams.spec.whatwg.org/#readable-stream-cancel>.
750#[derive(Clone, JSTraceable, MallocSizeOf)]
751struct SourceCancelPromiseFulfillmentHandler {
752    #[conditional_malloc_size_of]
753    result: Rc<Promise>,
754}
755
756impl Callback for SourceCancelPromiseFulfillmentHandler {
757    /// The fulfillment handler for the reacting to sourceCancelPromise part of
758    /// <https://streams.spec.whatwg.org/#readable-stream-cancel>.
759    /// An implementation of <https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled>
760    fn callback(&self, cx: &mut CurrentRealm, _v: SafeHandleValue) {
761        self.result.resolve_native(cx, &());
762    }
763}
764
765/// The rejection handler for the reacting to sourceCancelPromise part of
766/// <https://streams.spec.whatwg.org/#readable-stream-cancel>.
767#[derive(Clone, JSTraceable, MallocSizeOf)]
768struct SourceCancelPromiseRejectionHandler {
769    #[conditional_malloc_size_of]
770    result: Rc<Promise>,
771}
772
773impl Callback for SourceCancelPromiseRejectionHandler {
774    /// The rejection handler for the reacting to sourceCancelPromise part of
775    /// <https://streams.spec.whatwg.org/#readable-stream-cancel>.
776    /// An implementation of <https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled>
777    fn callback(&self, cx: &mut CurrentRealm, v: SafeHandleValue) {
778        self.result.reject_native(cx, &v);
779    }
780}
781
782/// <https://streams.spec.whatwg.org/#readablestream-state>
783#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf, PartialEq)]
784pub(crate) enum ReadableStreamState {
785    #[default]
786    Readable,
787    Closed,
788    Errored,
789}
790
791/// <https://streams.spec.whatwg.org/#readablestream-controller>
792#[derive(JSTraceable, MallocSizeOf)]
793#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
794pub(crate) enum ControllerType {
795    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller>
796    Byte(MutNullableDom<ReadableByteStreamController>),
797    /// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller>
798    Default(MutNullableDom<ReadableStreamDefaultController>),
799}
800
801/// <https://streams.spec.whatwg.org/#readablestream-readerr>
802#[derive(JSTraceable, MallocSizeOf)]
803#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
804pub(crate) enum ReaderType {
805    /// <https://streams.spec.whatwg.org/#readablestreambyobreader>
806    #[allow(clippy::upper_case_acronyms)]
807    BYOB(MutNullableDom<ReadableStreamBYOBReader>),
808    /// <https://streams.spec.whatwg.org/#readablestreamdefaultreader>
809    Default(MutNullableDom<ReadableStreamDefaultReader>),
810}
811
812impl js::gc::Rootable for ReaderType {}
813
814impl Eq for ReaderType {}
815impl PartialEq for ReaderType {
816    fn eq(&self, other: &Self) -> bool {
817        matches!(
818            (self, other),
819            (ReaderType::BYOB(_), ReaderType::BYOB(_)) |
820                (ReaderType::Default(_), ReaderType::Default(_))
821        )
822    }
823}
824
825/// <https://streams.spec.whatwg.org/#create-readable-stream>
826pub(crate) fn create_readable_stream(
827    cx: &mut JSContext,
828    global: &GlobalScope,
829    underlying_source_type: UnderlyingSourceType,
830    queuing_strategy: Option<Rc<QueuingStrategySize>>,
831    high_water_mark: Option<f64>,
832) -> DomRoot<ReadableStream> {
833    // If highWaterMark was not passed, set it to 1.
834    let high_water_mark = high_water_mark.unwrap_or(1.0);
835
836    // If sizeAlgorithm was not passed, set it to an algorithm that returns 1.
837    let size_algorithm =
838        queuing_strategy.unwrap_or(extract_size_algorithm(cx, &QueuingStrategy::empty()));
839
840    // Assert: ! IsNonNegativeNumber(highWaterMark) is true.
841    assert!(high_water_mark >= 0.0);
842
843    // Let stream be a new ReadableStream.
844    // Perform ! InitializeReadableStream(stream).
845    let stream = ReadableStream::new_with_proto(cx, global, None);
846
847    // Let controller be a new ReadableStreamDefaultController.
848    let controller = ReadableStreamDefaultController::new(
849        cx,
850        global,
851        underlying_source_type,
852        high_water_mark,
853        size_algorithm,
854    );
855
856    // Perform ? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm,
857    // pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm).
858    controller
859        .setup(cx, &stream)
860        .expect("Setup of default controller cannot fail");
861
862    // Return stream.
863    stream
864}
865
866/// <https://streams.spec.whatwg.org/#abstract-opdef-createreadablebytestream>
867fn readable_byte_stream_tee(
868    cx: &mut JSContext,
869    global: &GlobalScope,
870    underlying_source_type: UnderlyingSourceType,
871) -> DomRoot<ReadableStream> {
872    // Let stream be a new ReadableStream.
873    // Perform ! InitializeReadableStream(stream).
874    let tee_stream = ReadableStream::new_with_proto(cx, global, None);
875
876    // Let controller be a new ReadableByteStreamController.
877    let controller = ReadableByteStreamController::new(cx, underlying_source_type, 0.0, global);
878
879    // Perform ? SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, undefined).
880    controller
881        .setup(cx, global, &tee_stream)
882        .expect("Setup of byte stream controller cannot fail");
883
884    // Return stream.
885    tee_stream
886}
887
888/// <https://streams.spec.whatwg.org/#rs-class>
889#[dom_struct]
890pub(crate) struct ReadableStream {
891    reflector_: Reflector,
892
893    /// <https://streams.spec.whatwg.org/#readablestream-controller>
894    /// Note: the inner `MutNullableDom` should really be an `Option<Dom>`,
895    /// because it is never unset once set.
896    controller: RefCell<Option<ControllerType>>,
897
898    /// <https://streams.spec.whatwg.org/#readablestream-storederror>
899    #[ignore_malloc_size_of = "mozjs"]
900    stored_error: Heap<JSVal>,
901
902    /// <https://streams.spec.whatwg.org/#readablestream-disturbed>
903    disturbed: Cell<bool>,
904
905    /// <https://streams.spec.whatwg.org/#readablestream-reader>
906    reader: RefCell<Option<ReaderType>>,
907
908    /// <https://streams.spec.whatwg.org/#readablestream-state>
909    state: Cell<ReadableStreamState>,
910}
911
912impl ReadableStream {
913    /// <https://streams.spec.whatwg.org/#initialize-readable-stream>
914    fn new_inherited() -> ReadableStream {
915        ReadableStream {
916            reflector_: Reflector::new(),
917            controller: RefCell::new(None),
918            stored_error: Heap::default(),
919            disturbed: Default::default(),
920            reader: RefCell::new(None),
921            state: Cell::new(Default::default()),
922        }
923    }
924
925    pub(crate) fn new_with_proto(
926        cx: &mut JSContext,
927        global: &GlobalScope,
928        proto: Option<SafeHandleObject>,
929    ) -> DomRoot<ReadableStream> {
930        reflect_dom_object_with_proto(cx, Box::new(ReadableStream::new_inherited()), global, proto)
931    }
932
933    /// Used as part of
934    /// <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
935    pub(crate) fn set_default_controller(&self, controller: &ReadableStreamDefaultController) {
936        *self.controller.borrow_mut() = Some(ControllerType::Default(MutNullableDom::new(Some(
937            controller,
938        ))));
939    }
940
941    /// Used as part of
942    /// <https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller>
943    pub(crate) fn set_byte_controller(&self, controller: &ReadableByteStreamController) {
944        *self.controller.borrow_mut() =
945            Some(ControllerType::Byte(MutNullableDom::new(Some(controller))));
946    }
947
948    /// Used as part of
949    /// <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
950    pub(crate) fn assert_no_controller(&self) {
951        let has_no_controller = self.controller.borrow().is_none();
952        assert!(has_no_controller);
953    }
954
955    /// Build a stream backed by a Rust source that has already been read into memory.
956    pub(crate) fn new_from_bytes(
957        cx: &mut JSContext,
958        global: &GlobalScope,
959        bytes: Vec<u8>,
960    ) -> Fallible<DomRoot<ReadableStream>> {
961        let stream = ReadableStream::new_with_external_underlying_source(
962            cx,
963            global,
964            UnderlyingSourceType::Memory(bytes.len()),
965        )?;
966        stream.enqueue_native(cx, bytes);
967        stream.controller_close_native(cx);
968        Ok(stream)
969    }
970
971    /// Build an empty stream.
972    /// Used as step 2 of <https://fetch.spec.whatwg.org/#dom-body-textstream>
973    pub(crate) fn new_empty(
974        cx: &mut JSContext,
975        global: &GlobalScope,
976    ) -> Fallible<DomRoot<ReadableStream>> {
977        // Step 1. Let emptyStream be a new ReadableStream in this’s relevant realm.
978        // Step 2. Set up emptyStream.
979        let empty_stream = ReadableStream::new_with_external_underlying_source(
980            cx,
981            global,
982            UnderlyingSourceType::Memory(0),
983        )?;
984        // Step 3. Close emptyStream.
985        empty_stream.controller_close_native(cx);
986        // Step 4. Return emptyStream.
987        Ok(empty_stream)
988    }
989
990    /// <https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support>
991    pub(crate) fn new_from_bytes_with_byte_reading_support(
992        cx: &mut JSContext,
993        global: &GlobalScope,
994        bytes: Vec<u8>,
995    ) -> Fallible<DomRoot<ReadableStream>> {
996        let stream = ReadableStream::new_with_external_underlying_byte_source(
997            cx,
998            global,
999            UnderlyingSourceType::Memory(bytes.len()),
1000        )?;
1001        stream.enqueue_native(cx, bytes);
1002        stream.controller_close_native(cx);
1003        Ok(stream)
1004    }
1005
1006    /// Build a stream backed by a Rust underlying source.
1007    /// Note: external sources are always paired with a default controller.
1008    pub(crate) fn new_with_external_underlying_source(
1009        cx: &mut JSContext,
1010        global: &GlobalScope,
1011        source: UnderlyingSourceType,
1012    ) -> Fallible<DomRoot<ReadableStream>> {
1013        assert!(source.is_native());
1014        let stream = ReadableStream::new_with_proto(cx, global, None);
1015        let strategy_size = extract_size_algorithm(cx, &QueuingStrategy::empty());
1016        let controller =
1017            ReadableStreamDefaultController::new(cx, global, source, 1.0, strategy_size);
1018        controller.setup(cx, &stream)?;
1019        Ok(stream)
1020    }
1021
1022    /// <https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support>
1023    pub(crate) fn new_with_external_underlying_byte_source(
1024        cx: &mut JSContext,
1025        global: &GlobalScope,
1026        source: UnderlyingSourceType,
1027    ) -> Fallible<DomRoot<ReadableStream>> {
1028        assert!(source.is_native());
1029        let stream = ReadableStream::new_with_proto(cx, global, None);
1030        let controller = ReadableByteStreamController::new(cx, source, 0.0, global);
1031        controller.setup(cx, global, &stream)?;
1032        Ok(stream)
1033    }
1034
1035    /// Call into the release steps of the controller,
1036    pub(crate) fn perform_release_steps(&self) -> Fallible<()> {
1037        match self.controller.borrow().as_ref() {
1038            Some(ControllerType::Default(controller)) => {
1039                let controller = controller
1040                    .get()
1041                    .ok_or_else(|| Error::Type(c"Stream should have controller.".to_owned()))?;
1042                controller.perform_release_steps()
1043            },
1044            Some(ControllerType::Byte(controller)) => {
1045                let controller = controller
1046                    .get()
1047                    .ok_or_else(|| Error::Type(c"Stream should have controller.".to_owned()))?;
1048                controller.perform_release_steps()
1049            },
1050            None => Err(Error::Type(c"Stream should have controller.".to_owned())),
1051        }
1052    }
1053
1054    /// Call into the pull steps of the controller,
1055    /// as part of
1056    /// <https://streams.spec.whatwg.org/#readable-stream-default-reader-read>
1057    pub(crate) fn perform_pull_steps(&self, cx: &mut JSContext, read_request: &ReadRequest) {
1058        match self.controller.borrow().as_ref() {
1059            Some(ControllerType::Default(controller)) => controller
1060                .get()
1061                .expect("Stream should have controller.")
1062                .perform_pull_steps(cx, read_request),
1063            Some(ControllerType::Byte(controller)) => controller
1064                .get()
1065                .expect("Stream should have controller.")
1066                .perform_pull_steps(cx, read_request),
1067            None => {
1068                unreachable!("Stream does not have a controller.");
1069            },
1070        }
1071    }
1072
1073    /// Call into the pull steps of the controller,
1074    /// as part of
1075    /// <https://streams.spec.whatwg.org/#readable-stream-byob-reader-read>
1076    pub(crate) fn perform_pull_into(
1077        &self,
1078        cx: &mut JSContext,
1079        read_into_request: &ReadIntoRequest,
1080        view: &HeapBufferSource<ArrayBufferViewU8>,
1081        min: u64,
1082    ) {
1083        match self.controller.borrow().as_ref() {
1084            Some(ControllerType::Byte(controller)) => controller
1085                .get()
1086                .expect("Stream should have controller.")
1087                .perform_pull_into(cx, read_into_request, view, min),
1088            _ => {
1089                unreachable!(
1090                    "Pulling a chunk from a stream with a default controller using a BYOB reader"
1091                )
1092            },
1093        }
1094    }
1095
1096    /// <https://streams.spec.whatwg.org/#readable-stream-add-read-request>
1097    pub(crate) fn add_read_request(&self, read_request: &ReadRequest) {
1098        match self.reader.borrow().as_ref() {
1099            Some(ReaderType::Default(reader)) => {
1100                let Some(reader) = reader.get() else {
1101                    panic!("Attempt to add a read request without having first acquired a reader.");
1102                };
1103
1104                // Assert: stream.[[state]] is "readable".
1105                assert!(self.is_readable());
1106
1107                // Append readRequest to stream.[[reader]].[[readRequests]].
1108                reader.add_read_request(read_request);
1109            },
1110            _ => {
1111                unreachable!("Adding a read request can only be done on a default reader.")
1112            },
1113        }
1114    }
1115
1116    /// <https://streams.spec.whatwg.org/#readable-stream-add-read-into-request>
1117    pub(crate) fn add_read_into_request(&self, read_request: &ReadIntoRequest) {
1118        match self.reader.borrow().as_ref() {
1119            // Assert: stream.[[reader]] implements ReadableStreamBYOBReader.
1120            Some(ReaderType::BYOB(reader)) => {
1121                let Some(reader) = reader.get() else {
1122                    unreachable!(
1123                        "Attempt to add a read into request without having first acquired a reader."
1124                    );
1125                };
1126
1127                // Assert: stream.[[state]] is "readable" or "closed".
1128                assert!(self.is_readable() || self.is_closed());
1129
1130                // Append readRequest to stream.[[reader]].[[readIntoRequests]].
1131                reader.add_read_into_request(read_request);
1132            },
1133            _ => {
1134                unreachable!("Adding a read into request can only be done on a BYOB reader.")
1135            },
1136        }
1137    }
1138
1139    /// <https://streams.spec.whatwg.org/#readablestream-enqueue>
1140    pub(crate) fn enqueue_native(&self, cx: &mut JSContext, bytes: Vec<u8>) {
1141        match self.controller.borrow().as_ref() {
1142            Some(ControllerType::Default(controller)) => controller
1143                .get()
1144                .expect("Stream should have controller.")
1145                .enqueue_native(cx, bytes),
1146            Some(ControllerType::Byte(controller)) => {
1147                if bytes.is_empty() {
1148                    return;
1149                }
1150
1151                let controller = controller.get().expect("Stream should have controller.");
1152                rooted!(&in(cx) let mut chunk_object = ptr::null_mut::<JSObject>());
1153                create_buffer_source::<Uint8>(cx, &bytes, chunk_object.handle_mut())
1154                    .expect("failed to create buffer source for native byte chunk.");
1155
1156                let chunk = RootedTraceableBox::new(HeapBufferSource::<ArrayBufferViewU8>::new(
1157                    chunk_object.handle(),
1158                ));
1159                controller
1160                    .enqueue(cx, chunk)
1161                    .expect("Enqueuing a native byte chunk should not fail.");
1162            },
1163            _ => {
1164                unreachable!("Enqueueing chunk to a stream from Rust without a controller");
1165            },
1166        }
1167    }
1168
1169    /// <https://streams.spec.whatwg.org/#readable-stream-error>
1170    pub(crate) fn error(&self, cx: &mut JSContext, e: SafeHandleValue) {
1171        // Assert: stream.[[state]] is "readable".
1172        assert!(self.is_readable());
1173
1174        // Set stream.[[state]] to "errored".
1175        self.state.set(ReadableStreamState::Errored);
1176
1177        // Set stream.[[storedError]] to e.
1178        self.stored_error.set(e.get());
1179
1180        // Let reader be stream.[[reader]].
1181
1182        let default_reader = {
1183            let reader_ref = self.reader.borrow();
1184            match reader_ref.as_ref() {
1185                Some(ReaderType::Default(reader)) => reader.get(),
1186                _ => None,
1187            }
1188        };
1189
1190        if let Some(reader) = default_reader {
1191            // Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
1192            reader.error(cx, e);
1193            return;
1194        }
1195
1196        let byob_reader = {
1197            let reader_ref = self.reader.borrow();
1198            match reader_ref.as_ref() {
1199                Some(ReaderType::BYOB(reader)) => reader.get(),
1200                _ => None,
1201            }
1202        };
1203
1204        if let Some(reader) = byob_reader {
1205            // Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
1206            reader.error_read_into_requests(cx, e);
1207        }
1208
1209        // If reader is undefined, return.
1210    }
1211
1212    /// <https://streams.spec.whatwg.org/#readablestream-storederror>
1213    pub(crate) fn get_stored_error(&self, mut handle_mut: SafeMutableHandleValue) {
1214        handle_mut.set(self.stored_error.get());
1215    }
1216
1217    /// <https://streams.spec.whatwg.org/#readable-stream-error>
1218    /// Note: in other use cases this call happens via the controller.
1219    pub(crate) fn error_native(&self, cx: &mut JSContext, error: Error) {
1220        rooted!(&in(cx) let mut error_val = UndefinedValue());
1221        error.to_jsval(cx, &self.global(), error_val.handle_mut());
1222        self.error(cx, error_val.handle());
1223    }
1224
1225    /// Call into the controller's `Close` method.
1226    /// <https://streams.spec.whatwg.org/#readablestream-close>
1227    pub(crate) fn controller_close_native(&self, cx: &mut JSContext) {
1228        match self.controller.borrow().as_ref() {
1229            Some(ControllerType::Default(controller)) => {
1230                let _ = controller
1231                    .get()
1232                    .expect("Stream should have controller.")
1233                    .Close(cx);
1234            },
1235            Some(ControllerType::Byte(controller)) => {
1236                let _ = controller
1237                    .get()
1238                    .expect("Stream should have controller.")
1239                    .close(cx);
1240            },
1241            _ => {
1242                unreachable!("Native closing requires a stream controller.")
1243            },
1244        }
1245    }
1246
1247    /// Returns a boolean reflecting whether the stream has all data in memory.
1248    /// Useful for native source integration only.
1249    pub(crate) fn in_memory(&self) -> bool {
1250        match self.controller.borrow().as_ref() {
1251            Some(ControllerType::Default(controller)) => controller
1252                .get()
1253                .expect("Stream should have controller.")
1254                .in_memory(),
1255            Some(ControllerType::Byte(controller)) => controller
1256                .get()
1257                .expect("Stream should have controller.")
1258                .in_memory(),
1259            _ => unreachable!("Checking if source is in memory for a stream without a controller"),
1260        }
1261    }
1262
1263    /// Return bytes for synchronous use, if the stream has all data in memory.
1264    /// Useful for native source integration only.
1265    pub(crate) fn get_in_memory_bytes(&self, cx: &mut JSContext) -> Option<GenericSharedMemory> {
1266        match self.controller.borrow().as_ref() {
1267            Some(ControllerType::Default(controller)) => controller
1268                .get()
1269                .expect("Stream should have controller.")
1270                .get_in_memory_bytes()
1271                .map(GenericSharedMemory::from_vec),
1272            Some(ControllerType::Byte(controller)) => controller
1273                .get()
1274                .expect("Stream should have controller.")
1275                .get_in_memory_bytes(cx)
1276                .map(GenericSharedMemory::from_vec),
1277            _ => unreachable!("Getting in-memory bytes for a stream without a controller"),
1278        }
1279    }
1280
1281    /// Acquires a reader and locks the stream,
1282    /// must be done before `read_a_chunk`.
1283    /// Native call to
1284    /// <https://streams.spec.whatwg.org/#acquire-readable-stream-reader>
1285    pub(crate) fn acquire_default_reader(
1286        &self,
1287        cx: &mut JSContext,
1288    ) -> Fallible<DomRoot<ReadableStreamDefaultReader>> {
1289        // Let reader be a new ReadableStreamDefaultReader.
1290        let reader = ReadableStreamDefaultReader::new(cx, &self.global());
1291
1292        // Perform ? SetUpReadableStreamDefaultReader(reader, stream).
1293        reader.set_up(cx, self, &self.global())?;
1294
1295        // Return reader.
1296        Ok(reader)
1297    }
1298
1299    /// <https://streams.spec.whatwg.org/#acquire-readable-stream-byob-reader>
1300    pub(crate) fn acquire_byob_reader(
1301        &self,
1302        cx: &mut JSContext,
1303    ) -> Fallible<DomRoot<ReadableStreamBYOBReader>> {
1304        // Let reader be a new ReadableStreamBYOBReader.
1305        let reader = ReadableStreamBYOBReader::new(cx, &self.global());
1306        // Perform ? SetUpReadableStreamBYOBReader(reader, stream).
1307        reader.set_up(cx, self, &self.global())?;
1308
1309        // Return reader.
1310        Ok(reader)
1311    }
1312
1313    pub(crate) fn get_default_controller(&self) -> DomRoot<ReadableStreamDefaultController> {
1314        match self.controller.borrow().as_ref() {
1315            Some(ControllerType::Default(controller)) => {
1316                controller.get().expect("Stream should have controller.")
1317            },
1318            _ => {
1319                unreachable!(
1320                    "Getting default controller for a stream with a non-default controller"
1321                )
1322            },
1323        }
1324    }
1325
1326    pub(crate) fn get_byte_controller(&self) -> DomRoot<ReadableByteStreamController> {
1327        match self.controller.borrow().as_ref() {
1328            Some(ControllerType::Byte(controller)) => {
1329                controller.get().expect("Stream should have controller.")
1330            },
1331            _ => {
1332                unreachable!("Getting byte controller for a stream with a non-byte controller")
1333            },
1334        }
1335    }
1336
1337    pub(crate) fn get_default_reader(&self) -> DomRoot<ReadableStreamDefaultReader> {
1338        match self.reader.borrow().as_ref() {
1339            Some(ReaderType::Default(reader)) => reader.get().expect("Stream should have reader."),
1340            _ => {
1341                unreachable!("Getting default reader for a stream with a non-default reader")
1342            },
1343        }
1344    }
1345
1346    /// Read a chunk from the stream,
1347    /// must be called after `start_reading`,
1348    /// and before `stop_reading`.
1349    /// Native call to
1350    /// <https://streams.spec.whatwg.org/#readable-stream-default-reader-read>
1351    pub(crate) fn read_a_chunk(&self, cx: &mut JSContext) -> Rc<Promise> {
1352        match self.reader.borrow().as_ref() {
1353            Some(ReaderType::Default(reader)) => {
1354                let Some(reader) = reader.get() else {
1355                    unreachable!(
1356                        "Attempt to read stream chunk without having first acquired a reader."
1357                    );
1358                };
1359                reader.Read(cx)
1360            },
1361            _ => {
1362                unreachable!("Native reading of a chunk can only be done with a default reader.")
1363            },
1364        }
1365    }
1366
1367    /// Releases the lock on the reader,
1368    /// must be done after `start_reading`.
1369    /// Native call to
1370    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease>
1371    pub(crate) fn stop_reading(&self, cx: &mut JSContext) {
1372        let reader_ref = self.reader.borrow();
1373
1374        match reader_ref.as_ref() {
1375            Some(ReaderType::Default(reader)) => {
1376                let Some(reader) = reader.get() else {
1377                    unreachable!("Attempt to stop reading without having first acquired a reader.");
1378                };
1379
1380                drop(reader_ref);
1381                reader.release(cx).expect("Reader release cannot fail.");
1382            },
1383            _ => {
1384                unreachable!("Native stop reading can only be done with a default reader.")
1385            },
1386        }
1387    }
1388
1389    /// <https://streams.spec.whatwg.org/#is-readable-stream-locked>
1390    pub(crate) fn is_locked(&self) -> bool {
1391        match self.reader.borrow().as_ref() {
1392            Some(ReaderType::Default(reader)) => reader.get().is_some(),
1393            Some(ReaderType::BYOB(reader)) => reader.get().is_some(),
1394            None => false,
1395        }
1396    }
1397
1398    pub(crate) fn is_disturbed(&self) -> bool {
1399        self.disturbed.get()
1400    }
1401
1402    pub(crate) fn set_is_disturbed(&self, disturbed: bool) {
1403        self.disturbed.set(disturbed);
1404    }
1405
1406    pub(crate) fn is_closed(&self) -> bool {
1407        self.state.get() == ReadableStreamState::Closed
1408    }
1409
1410    pub(crate) fn is_errored(&self) -> bool {
1411        self.state.get() == ReadableStreamState::Errored
1412    }
1413
1414    pub(crate) fn is_readable(&self) -> bool {
1415        self.state.get() == ReadableStreamState::Readable
1416    }
1417
1418    pub(crate) fn has_default_reader(&self) -> bool {
1419        match self.reader.borrow().as_ref() {
1420            Some(ReaderType::Default(reader)) => reader.get().is_some(),
1421            _ => false,
1422        }
1423    }
1424
1425    pub(crate) fn has_byob_reader(&self) -> bool {
1426        match self.reader.borrow().as_ref() {
1427            Some(ReaderType::BYOB(reader)) => reader.get().is_some(),
1428            _ => false,
1429        }
1430    }
1431
1432    pub(crate) fn has_byte_controller(&self) -> bool {
1433        match self.controller.borrow().as_ref() {
1434            Some(ControllerType::Byte(controller)) => controller.get().is_some(),
1435            _ => false,
1436        }
1437    }
1438
1439    /// <https://streams.spec.whatwg.org/#readable-stream-get-num-read-requests>
1440    pub(crate) fn get_num_read_requests(&self) -> usize {
1441        match self.reader.borrow().as_ref() {
1442            Some(ReaderType::Default(reader)) => {
1443                let reader = reader
1444                    .get()
1445                    .expect("Stream must have a reader when getting the number of read requests.");
1446                reader.get_num_read_requests()
1447            },
1448            _ => unreachable!(
1449                "Stream must have a default reader when get num read requests is called into."
1450            ),
1451        }
1452    }
1453
1454    /// <https://streams.spec.whatwg.org/#readable-stream-get-num-read-into-requests>
1455    pub(crate) fn get_num_read_into_requests(&self) -> usize {
1456        assert!(self.has_byob_reader());
1457
1458        match self.reader.borrow().as_ref() {
1459            Some(ReaderType::BYOB(reader)) => {
1460                let Some(reader) = reader.get() else {
1461                    unreachable!(
1462                        "Stream must have a reader when get num read into requests is called into."
1463                    );
1464                };
1465                reader.get_num_read_into_requests()
1466            },
1467            _ => {
1468                unreachable!(
1469                    "Stream must have a BYOB reader when get num read into requests is called into."
1470                );
1471            },
1472        }
1473    }
1474
1475    /// <https://streams.spec.whatwg.org/#readable-stream-fulfill-read-request>
1476    pub(crate) fn fulfill_read_request(
1477        &self,
1478        cx: &mut JSContext,
1479        chunk: SafeHandleValue,
1480        done: bool,
1481    ) {
1482        // step 1 - Assert: ! ReadableStreamHasDefaultReader(stream) is true.
1483        assert!(self.has_default_reader());
1484
1485        match self.reader.borrow().as_ref() {
1486            Some(ReaderType::Default(reader)) => {
1487                // step 2 - Let reader be stream.[[reader]].
1488                let reader = reader
1489                    .get()
1490                    .expect("Stream must have a reader when a read request is fulfilled.");
1491                // step 3 - Assert: reader.[[readRequests]] is not empty.
1492                assert_ne!(reader.get_num_read_requests(), 0);
1493                // step 4 & 5
1494                // Let readRequest be reader.[[readRequests]][0]. & Remove readRequest from reader.[[readRequests]].
1495                let request = reader.remove_read_request();
1496
1497                if done {
1498                    // step 6 - If done is true, perform readRequest’s close steps.
1499                    request.close_steps(cx);
1500                } else {
1501                    // step 7 - Otherwise, perform readRequest’s chunk steps, given chunk.
1502                    let result = RootedTraceableBox::new(Heap::default());
1503                    result.set(*chunk);
1504                    request.chunk_steps(cx, result, &self.global());
1505                }
1506            },
1507            _ => {
1508                unreachable!(
1509                    "Stream must have a default reader when fulfill read requests is called into."
1510                );
1511            },
1512        }
1513    }
1514
1515    /// <https://streams.spec.whatwg.org/#readable-stream-fulfill-read-into-request>
1516    pub(crate) fn fulfill_read_into_request(
1517        &self,
1518        cx: &mut JSContext,
1519        chunk: SafeHandleValue,
1520        done: bool,
1521    ) {
1522        // Assert: ! ReadableStreamHasBYOBReader(stream) is true.
1523        assert!(self.has_byob_reader());
1524
1525        // Let reader be stream.[[reader]].
1526        match self.reader.borrow().as_ref() {
1527            Some(ReaderType::BYOB(reader)) => {
1528                let Some(reader) = reader.get() else {
1529                    unreachable!(
1530                        "Stream must have a reader when a read into request is fulfilled."
1531                    );
1532                };
1533
1534                // Assert: reader.[[readIntoRequests]] is not empty.
1535                assert!(reader.get_num_read_into_requests() > 0);
1536
1537                // Let readIntoRequest be reader.[[readIntoRequests]][0].
1538                // Remove readIntoRequest from reader.[[readIntoRequests]].
1539                let read_into_request = reader.remove_read_into_request();
1540
1541                // If done is true, perform readIntoRequest’s close steps, given chunk.
1542                let result = RootedTraceableBox::new(Heap::default());
1543                if done {
1544                    result.set(*chunk);
1545                    read_into_request.close_steps(cx, Some(result));
1546                } else {
1547                    // Otherwise, perform readIntoRequest’s chunk steps, given chunk.
1548                    result.set(*chunk);
1549                    read_into_request.chunk_steps(cx, result);
1550                }
1551            },
1552            _ => {
1553                unreachable!(
1554                    "Stream must have a BYOB reader when fulfill read into requests is called into."
1555                );
1556            },
1557        };
1558    }
1559
1560    /// <https://streams.spec.whatwg.org/#readable-stream-close>
1561    pub(crate) fn close(&self, cx: &mut JSContext) {
1562        // Assert: stream.[[state]] is "readable".
1563        assert!(self.is_readable());
1564        // Set stream.[[state]] to "closed".
1565        self.state.set(ReadableStreamState::Closed);
1566        // Let reader be stream.[[reader]].
1567
1568        // NOTE: do not hold the RefCell borrow across reader.close(),
1569        // or release() will panic when it tries to mut-borrow stream.reader.
1570        // So we pull out the underlying DOM reader in a local, then drop the borrow.
1571        let default_reader = {
1572            let reader_ref = self.reader.borrow();
1573            match reader_ref.as_ref() {
1574                Some(ReaderType::Default(reader)) => reader.get(),
1575                _ => None,
1576            }
1577        };
1578
1579        if let Some(reader) = default_reader {
1580            // steps 5 & 6 for a default reader
1581            reader.close(cx);
1582            return;
1583        }
1584
1585        // Same for BYOB reader.
1586        let byob_reader = {
1587            let reader_ref = self.reader.borrow();
1588            match reader_ref.as_ref() {
1589                Some(ReaderType::BYOB(reader)) => reader.get(),
1590                _ => None,
1591            }
1592        };
1593
1594        if let Some(reader) = byob_reader {
1595            // steps 5 & 6 for a BYOB reader
1596            reader.close(cx);
1597        }
1598
1599        // If reader is undefined, return.
1600    }
1601
1602    /// <https://streams.spec.whatwg.org/#readable-stream-cancel>
1603    pub(crate) fn cancel(
1604        &self,
1605        cx: &mut JSContext,
1606        global: &GlobalScope,
1607        reason: SafeHandleValue,
1608    ) -> Rc<Promise> {
1609        // Set stream.[[disturbed]] to true.
1610        self.disturbed.set(true);
1611
1612        // If stream.[[state]] is "closed", return a promise resolved with undefined.
1613        if self.is_closed() {
1614            return Promise::new_resolved(cx, global, ());
1615        }
1616        // If stream.[[state]] is "errored", return a promise rejected with stream.[[storedError]].
1617        if self.is_errored() {
1618            let promise = Promise::new(cx, global);
1619            rooted!(&in(cx) let mut rval = UndefinedValue());
1620            self.stored_error.safe_to_jsval(cx, rval.handle_mut());
1621            promise.reject_native(cx, &rval.handle());
1622            return promise;
1623        }
1624        // Perform ! ReadableStreamClose(stream).
1625        self.close(cx);
1626
1627        // If reader is not undefined and reader implements ReadableStreamBYOBReader,
1628        let byob_reader = {
1629            let reader_ref = self.reader.borrow();
1630            match reader_ref.as_ref() {
1631                Some(ReaderType::BYOB(reader)) => reader.get(),
1632                _ => None,
1633            }
1634        };
1635
1636        if let Some(reader) = byob_reader {
1637            // step 6.1, 6.2 & 6.3 of https://streams.spec.whatwg.org/#readable-stream-cancel
1638            reader.cancel(cx);
1639        }
1640
1641        // Let sourceCancelPromise be ! stream.[[controller]].[[CancelSteps]](reason).
1642
1643        let source_cancel_promise = match self.controller.borrow().as_ref() {
1644            Some(ControllerType::Default(controller)) => controller
1645                .get()
1646                .expect("Stream should have controller.")
1647                .perform_cancel_steps(cx, global, reason),
1648            Some(ControllerType::Byte(controller)) => controller
1649                .get()
1650                .expect("Stream should have controller.")
1651                .perform_cancel_steps(cx, global, reason),
1652            None => {
1653                panic!("Stream does not have a controller.");
1654            },
1655        };
1656
1657        // Create a new promise,
1658        // and setup a handler in order to react to the fulfillment of sourceCancelPromise.
1659        let global = self.global();
1660        let result_promise = Promise::new(cx, &global);
1661        let fulfillment_handler = Box::new(SourceCancelPromiseFulfillmentHandler {
1662            result: result_promise.clone(),
1663        });
1664        let rejection_handler = Box::new(SourceCancelPromiseRejectionHandler {
1665            result: result_promise.clone(),
1666        });
1667        let handler = PromiseNativeHandler::new(
1668            cx,
1669            &global,
1670            Some(fulfillment_handler),
1671            Some(rejection_handler),
1672        );
1673        let mut realm = enter_auto_realm(cx, &*global);
1674        let cx = &mut realm.current_realm();
1675        source_cancel_promise.append_native_handler(cx, &handler);
1676
1677        // Return the result of reacting to sourceCancelPromise
1678        // with a fulfillment step that returns undefined.
1679        result_promise
1680    }
1681
1682    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1683    pub(crate) fn set_reader(&self, new_reader: Option<ReaderType>) {
1684        *self.reader.borrow_mut() = new_reader;
1685    }
1686
1687    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1688    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamtee>
1689    fn byte_tee(&self, cx: &mut JSContext) -> Fallible<Vec<DomRoot<ReadableStream>>> {
1690        // Assert: stream implements ReadableStream.
1691        // Assert: stream.[[controller]] implements ReadableByteStreamController.
1692
1693        // Let reader be ? AcquireReadableStreamDefaultReader(stream).
1694        let reader = self.acquire_default_reader(cx)?;
1695        let reader = Rc::new(RefCell::new(ReaderType::Default(MutNullableDom::new(
1696            Some(&reader),
1697        ))));
1698
1699        // Let reading be false.
1700        let reading = Rc::new(Cell::new(false));
1701
1702        // Let readAgainForBranch1 be false.
1703        let read_again_for_branch_1 = Rc::new(Cell::new(false));
1704
1705        // Let readAgainForBranch2 be false.
1706        let read_again_for_branch_2 = Rc::new(Cell::new(false));
1707
1708        // Let canceled1 be false.
1709        let canceled_1 = Rc::new(Cell::new(false));
1710
1711        // Let canceled2 be false.
1712        let canceled_2 = Rc::new(Cell::new(false));
1713
1714        // Let reason1 be undefined.
1715        let reason_1 = Rc::new(Heap::default());
1716
1717        // Let reason2 be undefined.
1718        let reason_2 = Rc::new(Heap::default());
1719
1720        // Let cancelPromise be a new promise.
1721        let cancel_promise = Promise::new(cx, &self.global());
1722        let reader_version = Rc::new(Cell::new(0));
1723
1724        let byte_tee_source_1 = ByteTeeUnderlyingSource::new(
1725            cx,
1726            reader.clone(),
1727            self,
1728            reading.clone(),
1729            read_again_for_branch_1.clone(),
1730            read_again_for_branch_2.clone(),
1731            canceled_1.clone(),
1732            canceled_2.clone(),
1733            reason_1.clone(),
1734            reason_2.clone(),
1735            cancel_promise.clone(),
1736            reader_version.clone(),
1737            ByteTeeCancelAlgorithm::Cancel1Algorithm,
1738            ByteTeePullAlgorithm::Pull1Algorithm,
1739        );
1740
1741        let byte_tee_source_2 = ByteTeeUnderlyingSource::new(
1742            cx,
1743            reader.clone(),
1744            self,
1745            reading,
1746            read_again_for_branch_1,
1747            read_again_for_branch_2,
1748            canceled_1,
1749            canceled_2,
1750            reason_1,
1751            reason_2,
1752            cancel_promise,
1753            reader_version,
1754            ByteTeeCancelAlgorithm::Cancel2Algorithm,
1755            ByteTeePullAlgorithm::Pull2Algorithm,
1756        );
1757
1758        // Set branch1 to ! CreateReadableByteStream(startAlgorithm, pull1Algorithm, cancel1Algorithm).
1759        let branch_1 = readable_byte_stream_tee(
1760            cx,
1761            &self.global(),
1762            UnderlyingSourceType::TeeByte(&byte_tee_source_1),
1763        );
1764        byte_tee_source_1.set_branch_1(&branch_1);
1765        byte_tee_source_2.set_branch_1(&branch_1);
1766
1767        // Set branch2 to ! CreateReadableByteStream(startAlgorithm, pull2Algorithm, cancel2Algorithm).
1768        let branch_2 = readable_byte_stream_tee(
1769            cx,
1770            &self.global(),
1771            UnderlyingSourceType::TeeByte(&byte_tee_source_2),
1772        );
1773        byte_tee_source_1.set_branch_2(&branch_2);
1774        byte_tee_source_2.set_branch_2(&branch_2);
1775
1776        // Perform forwardReaderError, given reader.
1777        byte_tee_source_1.forward_reader_error(cx, reader.clone());
1778        byte_tee_source_2.forward_reader_error(cx, reader);
1779
1780        // Return « branch1, branch2 ».
1781        Ok(vec![branch_1, branch_2])
1782    }
1783
1784    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
1785    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1786    fn default_tee(
1787        &self,
1788        cx: &mut JSContext,
1789        clone_for_branch_2: bool,
1790    ) -> Fallible<Vec<DomRoot<ReadableStream>>> {
1791        // Assert: stream implements ReadableStream.
1792
1793        // Assert: cloneForBranch2 is a boolean.
1794        let clone_for_branch_2 = Rc::new(Cell::new(clone_for_branch_2));
1795
1796        // Let reader be ? AcquireReadableStreamDefaultReader(stream).
1797        let reader = self.acquire_default_reader(cx)?;
1798
1799        // Let reading be false.
1800        let reading = Rc::new(Cell::new(false));
1801        // Let readAgain be false.
1802        let read_again = Rc::new(Cell::new(false));
1803        // Let canceled1 be false.
1804        let canceled_1 = Rc::new(Cell::new(false));
1805        // Let canceled2 be false.
1806        let canceled_2 = Rc::new(Cell::new(false));
1807
1808        // Let reason1 be undefined.
1809        let reason_1 = Rc::new(Heap::default());
1810        // Let reason2 be undefined.
1811        let reason_2 = Rc::new(Heap::default());
1812        // Let cancelPromise be a new promise.
1813        let cancel_promise = Promise::new(cx, &self.global());
1814
1815        let tee_source_1 = DefaultTeeUnderlyingSource::new(
1816            cx,
1817            &reader,
1818            self,
1819            reading.clone(),
1820            read_again.clone(),
1821            canceled_1.clone(),
1822            canceled_2.clone(),
1823            clone_for_branch_2.clone(),
1824            reason_1.clone(),
1825            reason_2.clone(),
1826            cancel_promise.clone(),
1827            DefaultTeeCancelAlgorithm::Cancel1Algorithm,
1828        );
1829
1830        let underlying_source_type_branch_1 = UnderlyingSourceType::Tee(&tee_source_1);
1831
1832        let tee_source_2 = DefaultTeeUnderlyingSource::new(
1833            cx,
1834            &reader,
1835            self,
1836            reading,
1837            read_again,
1838            canceled_1.clone(),
1839            canceled_2.clone(),
1840            clone_for_branch_2,
1841            reason_1,
1842            reason_2,
1843            cancel_promise.clone(),
1844            DefaultTeeCancelAlgorithm::Cancel2Algorithm,
1845        );
1846
1847        let underlying_source_type_branch_2 = UnderlyingSourceType::Tee(&tee_source_2);
1848
1849        // Set branch_1 to ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancel1Algorithm).
1850        let branch_1 = create_readable_stream(
1851            cx,
1852            &self.global(),
1853            underlying_source_type_branch_1,
1854            None,
1855            None,
1856        );
1857        tee_source_1.set_branch_1(&branch_1);
1858        tee_source_2.set_branch_1(&branch_1);
1859
1860        // Set branch_2 to ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancel2Algorithm).
1861        let branch_2 = create_readable_stream(
1862            cx,
1863            &self.global(),
1864            underlying_source_type_branch_2,
1865            None,
1866            None,
1867        );
1868        tee_source_1.set_branch_2(&branch_2);
1869        tee_source_2.set_branch_2(&branch_2);
1870
1871        // Upon rejection of reader.[[closedPromise]] with reason r,
1872        reader.default_tee_append_native_handler_to_closed_promise(
1873            cx,
1874            &branch_1,
1875            &branch_2,
1876            canceled_1,
1877            canceled_2,
1878            cancel_promise,
1879        );
1880
1881        // Return « branch_1, branch_2 ».
1882        Ok(vec![branch_1, branch_2])
1883    }
1884
1885    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
1886    #[allow(clippy::too_many_arguments)]
1887    pub(crate) fn pipe_to(
1888        &self,
1889        cx: &mut CurrentRealm,
1890        global: &GlobalScope,
1891        dest: &WritableStream,
1892        prevent_close: bool,
1893        prevent_abort: bool,
1894        prevent_cancel: bool,
1895        signal: Option<&AbortSignal>,
1896    ) -> Rc<Promise> {
1897        // Assert: source implements ReadableStream.
1898        // Assert: dest implements WritableStream.
1899        // Assert: prevent_close, prevent_abort, and prevent_cancel are all booleans.
1900        // Done with method signature types.
1901
1902        // If signal was not given, let signal be undefined.
1903        // Assert: either signal is undefined, or signal implements AbortSignal.
1904        // Note: done with the `signal` argument.
1905
1906        // Assert: ! IsReadableStreamLocked(source) is false.
1907        assert!(!self.is_locked());
1908
1909        // Assert: ! IsWritableStreamLocked(dest) is false.
1910        assert!(!dest.is_locked());
1911
1912        // If source.[[controller]] implements ReadableByteStreamController,
1913        // let reader be either ! AcquireReadableStreamBYOBReader(source)
1914        // or ! AcquireReadableStreamDefaultReader(source),
1915        // at the user agent’s discretion.
1916        // Note: for now only using default readers.
1917
1918        // Otherwise, let reader be ! AcquireReadableStreamDefaultReader(source).
1919        let reader = self
1920            .acquire_default_reader(cx)
1921            .expect("Acquiring a default reader for pipe_to cannot fail");
1922
1923        // Let writer be ! AcquireWritableStreamDefaultWriter(dest).
1924        let writer = dest
1925            .aquire_default_writer(cx, global)
1926            .expect("Acquiring a default writer for pipe_to cannot fail");
1927
1928        // Set source.[[disturbed]] to true.
1929        self.disturbed.set(true);
1930
1931        // Let shuttingDown be false.
1932        // Done below with default.
1933
1934        // Let promise be a new promise.
1935        let promise = Promise::new(cx, global);
1936
1937        // In parallel, but not really, using reader and writer, read all chunks from source and write them to dest.
1938        rooted!(&in(cx) let pipe_to = PipeTo {
1939            reader: Dom::from_ref(&reader),
1940            writer: Dom::from_ref(&writer),
1941            pending_writes: Default::default(),
1942            state: Default::default(),
1943            prevent_abort,
1944            prevent_cancel,
1945            prevent_close,
1946            shutting_down: Default::default(),
1947            abort_reason: Default::default(),
1948            shutdown_error: Default::default(),
1949            shutdown_action_promise:  Default::default(),
1950            result_promise: promise.clone(),
1951        });
1952
1953        // If signal is not undefined,
1954        // Note: moving the steps to here, so that the `PipeTo` is available.
1955        if let Some(signal) = signal {
1956            // Let abortAlgorithm be the following steps:
1957            // Note: steps are implemented at call site.
1958            rooted!(&in(cx) let abort_algorithm = AbortAlgorithm::StreamPiping(pipe_to.clone()));
1959
1960            // If signal is aborted, perform abortAlgorithm and return promise.
1961            if signal.aborted() {
1962                signal.run_abort_algorithm(cx, global, &abort_algorithm);
1963                return promise;
1964            }
1965
1966            // Add abortAlgorithm to signal.
1967            signal.add(&abort_algorithm);
1968        }
1969
1970        // Note: perfom checks now, since streams can start as closed or errored.
1971        pipe_to.check_and_propagate_errors_forward(cx, global);
1972        pipe_to.check_and_propagate_errors_backward(cx, global);
1973        pipe_to.check_and_propagate_closing_forward(cx, global);
1974        pipe_to.check_and_propagate_closing_backward(cx, global);
1975
1976        // If we are not closed or errored,
1977        if *pipe_to.state.borrow() == PipeToState::Starting {
1978            // Start the pipe, by waiting on the writer being ready for a chunk.
1979            pipe_to.wait_for_writer_ready(cx, global);
1980        }
1981
1982        // Return promise.
1983        promise
1984    }
1985
1986    /// <https://streams.spec.whatwg.org/#readable-stream-tee>
1987    pub(crate) fn tee(
1988        &self,
1989        cx: &mut JSContext,
1990        clone_for_branch_2: bool,
1991    ) -> Fallible<Vec<DomRoot<ReadableStream>>> {
1992        // Assert: stream implements ReadableStream.
1993        // Assert: cloneForBranch2 is a boolean.
1994
1995        match self.controller.borrow().as_ref() {
1996            Some(ControllerType::Default(_)) => {
1997                // Return ? ReadableStreamDefaultTee(stream, cloneForBranch2).
1998                self.default_tee(cx, clone_for_branch_2)
1999            },
2000            Some(ControllerType::Byte(_)) => {
2001                // If stream.[[controller]] implements ReadableByteStreamController,
2002                // return ? ReadableByteStreamTee(stream).
2003                self.byte_tee(cx)
2004            },
2005            None => {
2006                unreachable!("Stream should have a controller.");
2007            },
2008        }
2009    }
2010
2011    /// <https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller-from-underlying-source>
2012    fn set_up_byte_controller(
2013        &self,
2014        cx: &mut JSContext,
2015        global: &GlobalScope,
2016        underlying_source_dict: JsUnderlyingSource,
2017        underlying_source_handle: SafeHandleObject,
2018        stream: &ReadableStream,
2019        strategy_hwm: f64,
2020    ) -> Fallible<()> {
2021        // Let pullAlgorithm be an algorithm that returns a promise resolved with undefined.
2022        // Let cancelAlgorithm be an algorithm that returns a promise resolved with undefined.
2023        // If underlyingSourceDict["start"] exists, then set startAlgorithm to an algorithm which returns the result
2024        // of invoking underlyingSourceDict["start"] with argument list « controller »
2025        // and callback this value underlyingSource.
2026        // If underlyingSourceDict["pull"] exists, then set pullAlgorithm to an algorithm which returns the result
2027        // of invoking underlyingSourceDict["pull"] with argument list « controller »
2028        // and callback this value underlyingSource.
2029        // If underlyingSourceDict["cancel"] exists, then set cancelAlgorithm to an algorithm which takes an
2030        // argument reason and returns the result of invoking underlyingSourceDict["cancel"] with argument list
2031        // « reason » and callback this value underlyingSource.
2032
2033        // Let autoAllocateChunkSize be underlyingSourceDict["autoAllocateChunkSize"],
2034        // if it exists, or undefined otherwise.
2035        // If autoAllocateChunkSize is 0, then throw a TypeError exception.
2036        if let Some(0) = underlying_source_dict.autoAllocateChunkSize {
2037            return Err(Error::Type(c"autoAllocateChunkSize cannot be 0".to_owned()));
2038        }
2039
2040        let controller = ReadableByteStreamController::new(
2041            cx,
2042            UnderlyingSourceType::Js(underlying_source_dict),
2043            strategy_hwm,
2044            global,
2045        );
2046
2047        // Note: this must be done before `setup`,
2048        // otherwise `thisOb` is null in the start callback.
2049        controller.set_underlying_source_this_object(underlying_source_handle);
2050
2051        // Perform ? SetUpReadableByteStreamController(stream, controller, startAlgorithm,
2052        // pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize).
2053        controller.setup(cx, global, stream)
2054    }
2055
2056    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
2057    pub(crate) fn setup_cross_realm_transform_readable(
2058        &self,
2059        cx: &mut JSContext,
2060        port: &MessagePort,
2061    ) {
2062        let port_id = port.message_port_id();
2063        let global = self.global();
2064
2065        // Perform ! InitializeReadableStream(stream).
2066        // Done in `new_inherited`.
2067
2068        // Let sizeAlgorithm be an algorithm that returns 1.
2069        let size_algorithm = extract_size_algorithm(cx, &QueuingStrategy::default());
2070
2071        // Note: other algorithms defined in the underlying source container.
2072
2073        // Let controller be a new ReadableStreamDefaultController.
2074        let controller = ReadableStreamDefaultController::new(
2075            cx,
2076            &self.global(),
2077            UnderlyingSourceType::Transfer(port),
2078            0.,
2079            size_algorithm,
2080        );
2081
2082        // Add a handler for port’s message event with the following steps:
2083        // Add a handler for port’s messageerror event with the following steps:
2084        rooted!(&in(cx) let cross_realm_transform_readable = CrossRealmTransformReadable {
2085            controller: Dom::from_ref(&controller),
2086        });
2087        global.note_cross_realm_transform_readable(&cross_realm_transform_readable, port_id);
2088
2089        // Enable port’s port message queue.
2090        port.Start(cx);
2091
2092        // Perform ! SetUpReadableStreamDefaultController
2093        controller
2094            .setup(cx, self)
2095            .expect("Setting up controller for transfer cannot fail.");
2096    }
2097}
2098
2099impl ReadableStreamMethods<crate::DomTypeHolder> for ReadableStream {
2100    /// <https://streams.spec.whatwg.org/#rs-constructor>
2101    fn Constructor(
2102        cx: &mut JSContext,
2103        global: &GlobalScope,
2104        proto: Option<SafeHandleObject>,
2105        underlying_source: Option<*mut JSObject>,
2106        strategy: &QueuingStrategy,
2107    ) -> Fallible<DomRoot<Self>> {
2108        // If underlyingSource is missing, set it to null.
2109        rooted!(&in(cx) let underlying_source_obj = underlying_source.unwrap_or(ptr::null_mut()));
2110        // Let underlyingSourceDict be underlyingSource,
2111        // converted to an IDL value of type UnderlyingSource.
2112        let underlying_source_dict = if !underlying_source_obj.is_null() {
2113            rooted!(&in(cx) let obj_val = ObjectValue(underlying_source_obj.get()));
2114            match JsUnderlyingSource::new(cx, obj_val.handle()) {
2115                Ok(ConversionResult::Success(val)) => val,
2116                Ok(ConversionResult::Failure(error)) => {
2117                    return Err(Error::Type(error.into_owned()));
2118                },
2119                _ => {
2120                    return Err(Error::JSFailed);
2121                },
2122            }
2123        } else {
2124            JsUnderlyingSource::empty()
2125        };
2126
2127        // Perform ! InitializeReadableStream(this).
2128        let stream = ReadableStream::new_with_proto(cx, global, proto);
2129
2130        if underlying_source_dict.type_.is_some() {
2131            // If strategy["size"] exists, throw a RangeError exception.
2132            if strategy.size.is_some() {
2133                return Err(Error::Range(
2134                    c"size is not supported for byte streams".to_owned(),
2135                ));
2136            }
2137
2138            // Let highWaterMark be ? ExtractHighWaterMark(strategy, 0).
2139            let strategy_hwm = extract_high_water_mark(strategy, 0.0)?;
2140
2141            // Perform ? SetUpReadableByteStreamControllerFromUnderlyingSource(this,
2142            // underlyingSource, underlyingSourceDict, highWaterMark).
2143            stream.set_up_byte_controller(
2144                cx,
2145                global,
2146                underlying_source_dict,
2147                underlying_source_obj.handle(),
2148                &stream,
2149                strategy_hwm,
2150            )?;
2151        } else {
2152            // Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
2153            let high_water_mark = extract_high_water_mark(strategy, 1.0)?;
2154
2155            // Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
2156            let size_algorithm = extract_size_algorithm(cx, strategy);
2157
2158            let controller = ReadableStreamDefaultController::new(
2159                cx,
2160                global,
2161                UnderlyingSourceType::Js(underlying_source_dict),
2162                high_water_mark,
2163                size_algorithm,
2164            );
2165
2166            // Note: this must be done before `setup`,
2167            // otherwise `thisOb` is null in the start callback.
2168            controller.set_underlying_source_this_object(underlying_source_obj.handle());
2169
2170            // Perform ? SetUpReadableStreamDefaultControllerFromUnderlyingSource
2171            controller.setup(cx, &stream)?;
2172        };
2173
2174        Ok(stream)
2175    }
2176
2177    /// <https://streams.spec.whatwg.org/#rs-locked>
2178    fn Locked(&self) -> bool {
2179        self.is_locked()
2180    }
2181
2182    /// <https://streams.spec.whatwg.org/#rs-cancel>
2183    fn Cancel(&self, cx: &mut JSContext, reason: SafeHandleValue) -> Rc<Promise> {
2184        let global = self.global();
2185        if self.is_locked() {
2186            // If ! IsReadableStreamLocked(this) is true,
2187            // return a promise rejected with a TypeError exception.
2188            let promise = Promise::new(cx, &global);
2189            promise.reject_error(cx, Error::Type(c"stream is locked".to_owned()));
2190            promise
2191        } else {
2192            // Return ! ReadableStreamCancel(this, reason).
2193            self.cancel(cx, &global, reason)
2194        }
2195    }
2196
2197    /// <https://streams.spec.whatwg.org/#rs-get-reader>
2198    fn GetReader(
2199        &self,
2200        cx: &mut JSContext,
2201        options: &ReadableStreamGetReaderOptions,
2202    ) -> Fallible<ReadableStreamReader> {
2203        // 1, If options["mode"] does not exist, return ? AcquireReadableStreamDefaultReader(this).
2204        if options.mode.is_none() {
2205            return Ok(ReadableStreamReader::ReadableStreamDefaultReader(
2206                self.acquire_default_reader(cx)?,
2207            ));
2208        }
2209        // 2. Assert: options["mode"] is "byob".
2210        assert!(options.mode.unwrap() == ReadableStreamReaderMode::Byob);
2211
2212        // 3. Return ? AcquireReadableStreamBYOBReader(this).
2213        Ok(ReadableStreamReader::ReadableStreamBYOBReader(
2214            self.acquire_byob_reader(cx)?,
2215        ))
2216    }
2217
2218    /// <https://streams.spec.whatwg.org/#rs-tee>
2219    fn Tee(&self, cx: &mut JSContext) -> Fallible<Vec<DomRoot<ReadableStream>>> {
2220        // Return ? ReadableStreamTee(this, false).
2221        self.tee(cx, false)
2222    }
2223
2224    /// <https://streams.spec.whatwg.org/#rs-pipe-to>
2225    fn PipeTo(
2226        &self,
2227        cx: &mut CurrentRealm,
2228        destination: &WritableStream,
2229        options: &StreamPipeOptions,
2230    ) -> Rc<Promise> {
2231        let global = self.global();
2232
2233        // If ! IsReadableStreamLocked(this) is true,
2234        if self.is_locked() {
2235            // return a promise rejected with a TypeError exception.
2236            let promise = Promise::new(cx, &global);
2237            promise.reject_error(cx, Error::Type(c"Source stream is locked".to_owned()));
2238            return promise;
2239        }
2240
2241        // If ! IsWritableStreamLocked(destination) is true,
2242        if destination.is_locked() {
2243            // return a promise rejected with a TypeError exception.
2244            let promise = Promise::new(cx, &global);
2245            promise.reject_error(cx, Error::Type(c"Destination stream is locked".to_owned()));
2246            return promise;
2247        }
2248
2249        // Let signal be options["signal"] if it exists, or undefined otherwise.
2250        let signal = options.signal.as_deref();
2251
2252        // Return ! ReadableStreamPipeTo.
2253        self.pipe_to(
2254            cx,
2255            &global,
2256            destination,
2257            options.preventClose,
2258            options.preventAbort,
2259            options.preventCancel,
2260            signal,
2261        )
2262    }
2263
2264    /// <https://streams.spec.whatwg.org/#rs-pipe-through>
2265    fn PipeThrough(
2266        &self,
2267        cx: &mut CurrentRealm,
2268        transform: &ReadableWritablePair,
2269        options: &StreamPipeOptions,
2270    ) -> Fallible<DomRoot<ReadableStream>> {
2271        let global = self.global();
2272
2273        // If ! IsReadableStreamLocked(this) is true, throw a TypeError exception.
2274        if self.is_locked() {
2275            return Err(Error::Type(c"Source stream is locked".to_owned()));
2276        }
2277
2278        // If ! IsWritableStreamLocked(transform["writable"]) is true, throw a TypeError exception.
2279        if transform.writable.is_locked() {
2280            return Err(Error::Type(c"Destination stream is locked".to_owned()));
2281        }
2282
2283        // Let signal be options["signal"] if it exists, or undefined otherwise.
2284        let signal = options.signal.as_deref();
2285
2286        // Let promise be ! ReadableStreamPipeTo(this, transform["writable"],
2287        // options["preventClose"], options["preventAbort"], options["preventCancel"], signal).
2288        let promise = self.pipe_to(
2289            cx,
2290            &global,
2291            &transform.writable,
2292            options.preventClose,
2293            options.preventAbort,
2294            options.preventCancel,
2295            signal,
2296        );
2297
2298        // Set promise.[[PromiseIsHandled]] to true.
2299        promise.set_promise_is_handled(cx);
2300
2301        // Return transform["readable"].
2302        Ok(transform.readable.clone())
2303    }
2304}
2305
2306/// The initial steps for the message handler for both readable and writable cross realm transforms.
2307/// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
2308/// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
2309pub(crate) fn get_type_and_value_from_message(
2310    cx: &mut JSContext,
2311    data: SafeHandleValue,
2312    value: SafeMutableHandleValue,
2313) -> DOMString {
2314    // Let data be the data of the message.
2315    // Note: we are passed the data as argument,
2316    // which originates in the return value of `structuredclone::read`.
2317
2318    // Assert: data is an Object.
2319    assert!(data.is_object());
2320    rooted!(&in(cx) let data_object = data.to_object());
2321
2322    // Let type be ! Get(data, "type").
2323    let type_ = get_property::<DOMString>(
2324        cx,
2325        data_object.handle(),
2326        c"type",
2327        StringificationBehavior::Empty,
2328    );
2329
2330    // Let value be ! Get(data, "value").
2331    get_property_jsval(cx, data_object.handle(), c"value", value)
2332        .expect("Getting the value should not fail.");
2333
2334    // Assert: type is a String.
2335    type_
2336        .expect("The type of the message should be a string")
2337        .expect("Property should be present")
2338}
2339
2340impl js::gc::Rootable for CrossRealmTransformReadable {}
2341
2342/// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
2343/// A wrapper to handle `message` and `messageerror` events
2344/// for the port used by the transfered stream.
2345#[derive(Clone, JSTraceable, MallocSizeOf)]
2346#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
2347pub(crate) struct CrossRealmTransformReadable {
2348    /// The controller used in the algorithm.
2349    controller: Dom<ReadableStreamDefaultController>,
2350}
2351
2352impl CrossRealmTransformReadable {
2353    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable>
2354    /// Add a handler for port’s message event with the following steps:
2355    pub(crate) fn handle_message(
2356        &self,
2357        cx: &mut CurrentRealm,
2358        global: &GlobalScope,
2359        port: &MessagePort,
2360        message: SafeHandleValue,
2361    ) {
2362        rooted!(&in(cx) let mut value = UndefinedValue());
2363        let type_string = get_type_and_value_from_message(cx, message, value.handle_mut());
2364
2365        // If type is "chunk",
2366        if type_string == "chunk" {
2367            // Perform ! ReadableStreamDefaultControllerEnqueue(controller, value).
2368            self.controller
2369                .enqueue(cx, value.handle())
2370                .expect("Enqueing a chunk should not fail.");
2371        }
2372
2373        // Otherwise, if type is "close",
2374        if type_string == "close" {
2375            // Perform ! ReadableStreamDefaultControllerClose(controller).
2376            self.controller.close(cx);
2377
2378            // Disentangle port.
2379            global.disentangle_port(cx, port);
2380        }
2381
2382        // Otherwise, if type is "error",
2383        if type_string == "error" {
2384            // Perform ! ReadableStreamDefaultControllerError(controller, value).
2385            self.controller.error(cx, value.handle());
2386
2387            // Disentangle port.
2388            global.disentangle_port(cx, port);
2389        }
2390    }
2391
2392    /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
2393    /// Add a handler for port’s messageerror event with the following steps:
2394    pub(crate) fn handle_error(
2395        &self,
2396        cx: &mut CurrentRealm,
2397        global: &GlobalScope,
2398        port: &MessagePort,
2399    ) {
2400        // Let error be a new "DataCloneError" DOMException.
2401        let error = DOMException::new(cx, global, DOMErrorName::DataCloneError);
2402        rooted!(&in(cx) let mut rooted_error = UndefinedValue());
2403        error.safe_to_jsval(cx, rooted_error.handle_mut());
2404
2405        // Perform ! CrossRealmTransformSendError(port, error).
2406        port.cross_realm_transform_send_error(cx, rooted_error.handle());
2407
2408        // Perform ! ReadableStreamDefaultControllerError(controller, error).
2409        self.controller.error(cx, rooted_error.handle());
2410
2411        // Disentangle port.
2412        global.disentangle_port(cx, port);
2413    }
2414}
2415
2416/// Get the `done` property of an object that a read promise resolved to.
2417pub(crate) fn get_read_promise_done(
2418    cx: &mut JSContext,
2419    v: &SafeHandleValue,
2420) -> Result<bool, Error> {
2421    if !v.is_object() {
2422        return Err(Error::Type(c"Unknown format for done property.".to_owned()));
2423    }
2424
2425    rooted!(&in(cx) let object = v.to_object());
2426    get_property::<bool>(cx, object.handle(), c"done", ())?
2427        .ok_or(Error::Type(c"Promise has no done property.".to_owned()))
2428}
2429
2430/// Get the `value` property of an object that a read promise resolved to.
2431pub(crate) fn get_read_promise_bytes(
2432    cx: &mut JSContext,
2433    v: &SafeHandleValue,
2434) -> Result<Vec<u8>, Error> {
2435    if !v.is_object() {
2436        return Err(Error::Type(
2437            c"Unknown format for for bytes read.".to_owned(),
2438        ));
2439    }
2440
2441    rooted!(&in(cx) let object = v.to_object());
2442    get_property::<Vec<u8>>(
2443        cx,
2444        object.handle(),
2445        c"value",
2446        ConversionBehavior::EnforceRange,
2447    )?
2448    .ok_or(Error::Type(c"Promise has no value property.".to_owned()))
2449}
2450
2451/// Convert a raw stream `chunk` JS value to `Vec<u8>`.
2452/// This mirrors the conversion used inside `get_read_promise_bytes`,
2453/// but operates on the raw chunk (no `{ value, done }` wrapper).
2454pub(crate) fn bytes_from_chunk_jsval(
2455    cx: &mut JSContext,
2456    chunk: &RootedTraceableBox<Heap<JSVal>>,
2457) -> Result<Vec<u8>, Error> {
2458    match Vec::<u8>::safe_from_jsval(cx, chunk.handle(), ConversionBehavior::EnforceRange) {
2459        Ok(ConversionResult::Success(vec)) => Ok(vec),
2460        Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())),
2461        _ => Err(Error::Type(c"Unknown format for bytes read.".to_owned())),
2462    }
2463}
2464
2465/// <https://streams.spec.whatwg.org/#rs-transfer>
2466impl Transferable for ReadableStream {
2467    type Index = MessagePortIndex;
2468    type Data = MessagePortImpl;
2469
2470    /// <https://streams.spec.whatwg.org/#ref-for-transfer-steps>
2471    fn transfer(&self, cx: &mut JSContext) -> Fallible<(MessagePortId, MessagePortImpl)> {
2472        // Step 1. If ! IsReadableStreamLocked(value) is true, throw a
2473        // "DataCloneError" DOMException.
2474        if self.is_locked() {
2475            return Err(Error::DataClone(None));
2476        }
2477
2478        let global = self.global();
2479        let mut realm = enter_auto_realm(cx, &*global);
2480        let mut realm = realm.current_realm();
2481        let cx = &mut realm;
2482
2483        // Step 2. Let port1 be a new MessagePort in the current Realm.
2484        let port_1 = MessagePort::new(cx, &global);
2485        global.track_message_port(&port_1, None);
2486
2487        // Step 3. Let port2 be a new MessagePort in the current Realm.
2488        let port_2 = MessagePort::new(cx, &global);
2489        global.track_message_port(&port_2, None);
2490
2491        // Step 4. Entangle port1 and port2.
2492        global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id());
2493
2494        // Step 5. Let writable be a new WritableStream in the current Realm.
2495        let writable = WritableStream::new_with_proto(cx, &global, None);
2496
2497        // Step 6. Perform ! SetUpCrossRealmTransformWritable(writable, port1).
2498        writable.setup_cross_realm_transform_writable(cx, &port_1);
2499
2500        // Step 7. Let promise be ! ReadableStreamPipeTo(value, writable, false, false, false).
2501        let promise = self.pipe_to(cx, &global, &writable, false, false, false, None);
2502
2503        // Step 8. Set promise.[[PromiseIsHandled]] to true.
2504        promise.set_promise_is_handled(cx);
2505
2506        // Step 9. Set dataHolder.[[port]] to ! StructuredSerializeWithTransfer(port2, « port2 »).
2507        port_2.transfer(cx)
2508    }
2509
2510    /// <https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps>
2511    fn transfer_receive(
2512        cx: &mut JSContext,
2513        owner: &GlobalScope,
2514        id: MessagePortId,
2515        port_impl: MessagePortImpl,
2516    ) -> Result<DomRoot<Self>, ()> {
2517        // Their transfer-receiving steps, given dataHolder and value, are:
2518        // Note: dataHolder is used in `structuredclone.rs`, and value is created here.
2519        let value = ReadableStream::new_with_proto(cx, owner, None);
2520
2521        // Step 1. Let deserializedRecord be !
2522        // StructuredDeserializeWithTransfer(dataHolder.[[port]], the current
2523        // Realm).
2524        // Done with the `Deserialize` derive of `MessagePortImpl`.
2525
2526        // Step 2. Let port be deserializedRecord.[[Deserialized]].
2527        let transferred_port = MessagePort::transfer_receive(cx, owner, id, port_impl)?;
2528
2529        // Step 3. Perform ! SetUpCrossRealmTransformReadable(value, port).
2530        value.setup_cross_realm_transform_readable(cx, &transferred_port);
2531        Ok(value)
2532    }
2533
2534    /// Note: we are relying on the port transfer, so the data returned here are related to the port.
2535    fn serialized_storage<'a>(
2536        data: StructuredData<'a, '_>,
2537    ) -> &'a mut Option<FxHashMap<MessagePortId, Self::Data>> {
2538        match data {
2539            StructuredData::Reader(r) => &mut r.port_impls,
2540            StructuredData::Writer(w) => &mut w.ports,
2541        }
2542    }
2543}
2544
2545/// <https://streams.spec.whatwg.org/#readablestream-pipe-through>
2546/// Pipe a ReadableStream through a transform and return the readable side.
2547/// Note: Unlike [`ReadableStream::PipeThrough`], this is not failliable.
2548///
2549/// Note: Spec says it takes same options as [`ReadableStream::PipeThrough`],
2550/// however all usages use default `false`.
2551pub(crate) fn pipe_through(
2552    source: &ReadableStream,
2553    cx: &mut JSContext,
2554    global: &GlobalScope,
2555    transform: &TextDecoderStream,
2556) -> DomRoot<ReadableStream> {
2557    // Step 1. Assert: `! IsReadableStreamLocked(readable)` is false.
2558
2559    // Step 2. Assert: `! IsWritableStreamLocked(transform.[[writable]])` is false.
2560
2561    // Above is done in `pipe_to` below.
2562    let mut realm = CurrentRealm::assert(cx);
2563    // Step 4. Let promise be ! ReadableStreamPipeTo(readable,
2564    // transform.[[writable]], preventClose, preventAbort, preventCancel, signalArg).
2565    let promise = source.pipe_to(
2566        &mut realm,
2567        global,
2568        &transform.Writable(),
2569        false, // preventClose
2570        false, // preventAbort
2571        false, // preventCancel
2572        None,  // signal
2573    );
2574
2575    // Step 5. Set promise.[[PromiseIsHandled]] to true.
2576    promise.set_promise_is_handled(cx);
2577    // Step 6. Return transform.[[readable]].
2578    transform.Readable()
2579}