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