Skip to main content

script/dom/stream/
readablestream.rs

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