Skip to main content

script/dom/stream/
readablestream.rs

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