Skip to main content

script/dom/stream/
readablestream.rs

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