Dom: Re-implement ReadableStream Part 1 : Default Reader and Controller (#34064)

* Re-implement readablestream: basics and default reader and controller

---------

Co-authored-by: Jason Tsai <jason@pews.dev>
Signed-off-by: Wu Wayne <yuweiwu@pm.me>

Add remaining WebIDLs of ReadableStream (#32605)

* Add Reader's WebIDL files

* Add necessary methods in ReadableStream.webidl

Signed-off-by: Wu Wayne <yuweiwu@pm.me>

Create safe wrapper for JSFunctions (#32620)

* Create safe wrapper for JSFunctions

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Add assert to check if the  name ends in a null character

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Create macro to wrap unsafe extern "C" function calls

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Remove WRAPPER_FN

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Add macro example documentation

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Use  C-string literals

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Ensure name is Cstr type

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Scope #[allow(unsafe_code)]

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

---------

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>
Signed-off-by: Wu Wayne <yuweiwu@pm.me>

Start implementation of default controller and reader

Start implementation of default controller and reader

* implement basic internal slots, with todos

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* enum for controller

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* re-implement native controller methods

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add calling into pull algo

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* more details on chunk enqueuing

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add fulfill read request, clean-up warnings

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* read request and reader typing

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* allow for more than one non-native underlying source type

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add todo for should pull

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add underlying source dom struct container

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* remove rc around source type

* add default controller init in stream constructor

* setup source container with prototype of source dict

* clean-up docs, dispatch of controller in pull algo call

* turn off SM streams

* remove prototype setting on underlying source container

* fix read request promise resolving

* tidy

* clean-up js conversions in read req handlers

* add queue with sizes concept

* use dom in pull promise handlers

* Demonstrate using dictionary as callback this object.

* move value with size to a struct

* fmt

* put readable stream state in a cell

* nits in expectations

* remove allow unroot by passing read result directly to promise resolving

* tidy

* root default controller inside call_pull_if_needed

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
Signed-off-by: Wu Wayne <yuweiwu@pm.me>

ReadableStream: implement Cancel and Locked (#33136)

* implement Locked

* implement Cancel and close

Signed-off-by: Wu Wayne <yuweiwu@pm.me>

Add GetPromiseIsHandled and SetAnyPromiseIsHandled to Promise

Signed-off-by: Taym <haddadi.taym@gmail.com>

mach fmt

Signed-off-by: Taym <haddadi.taym@gmail.com>

Readablestream default controller: get desired size (#33497)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

stream: implement controller close (#33498)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

implement stream default controller error (#33503)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

Readablestream default controller: enqueue (#33528)

* Implement ReadableStreamDefaultControllerMethods::Enqueue

Signed-off-by: Wu Wayne <yuweiwu@pm.me>

* Add spec comments

Signed-off-by: Wu Wayne <yuweiwu@pm.me>

---------

Signed-off-by: Wu Wayne <yuweiwu@pm.me>

readablestream default controller: fulfill read requests (#33542)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

Fix extract_size_algorithm (#33561)

Signed-off-by: Wu Wayne <yuweiwu@pm.me>

Readablestream default controller: use strategy size (#33551)

* readablestream default controller: use strategy size, fallible enqueue

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

docs

* readablestream default controller: clear strategy size

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* prevent potential re-borrow panics when calling into the strategy size

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* document readablestream constructor

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

Readablestream: impl default controller should pull, start algo (#33586)

* implement should-pull algo for default controller

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add start algorithm setup for default controller

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

implement promise native handling for start and pull algorithms (#33603)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

Implement ReadableStreamDefaultReader (#33160)

* Implement ReadableStreamDefaultReader

 Make the stream mutable
 readable-stream-reader-generic-release
 Proper error types when releasing
 Closed
 Cancel

Signed-off-by: Taym <haddadi.taym@gmail.com>

* follow the spec more closely

Signed-off-by: Taym <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>

Implement ReadableStreamDefaultReader read (#34007)

* Implement ReadableStreamDefaultReader read

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Perform readRequest’s error steps with stream.stored_error

Signed-off-by: Taym <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>

Improve ReadableStreamDefaultReader close (#34014)

* improve ReadableStreamDefaultReader close

Signed-off-by: Taym <haddadi.taym@gmail.com>

* remove resolve_closed_promise

Signed-off-by: Taym <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>

Use Rc<Box<[u8]>> for queue to optimize get_in_memory_bytes

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>

* Improve read_a_chunk and stop_reading implemntation (#34077)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Implement ReadableStreamDefaultReader::Constructor  (#34056)

* Implement ReadableStreamDefaultReader::Constructor

Signed-off-by: Taym <haddadi.taym@gmail.com>

* make start_reading returns ReadableStreamDefaultReader

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Fix can_gc
Signed-off-by: Taym <haddadi.taym@gmail.com>

* Add canGc to ReadableStream::GetReader

Signed-off-by: Taym <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Readablestream fix CanGc (#34080)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* correct ReadableStream::error_native implementation and fix clippy warnings (#34088)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* turn assertion of stream present on controller on a early return with false (#34097)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix already mutably borrowed crash (#34105)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Refactor `get_in_memory_bytes` to return `Option<Vec<u8>> and avoid `unreachable!` panic (#34123)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Set ReadableStream ReadableStreamDefaultReader in start_reading (#34125)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix  Unhandled rejection with value: object `TypeError: stream is not locked` (#34204)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix  assert!(self.is_readable()) crash in ReadableStream::close (#34207)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix call to to_js_object in underlying source algos (#34098)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* do not assume presence of a stream when performing pull steps (#34244)

* do not assume presence of a stream when performing pull steps

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add doc comments

Co-authored-by: Taym Haddadi <haddadi.taym@gmail.com>
Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com>
Co-authored-by: Taym Haddadi <haddadi.taym@gmail.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* gracefully handle failure of underlying source algorithms (#34243)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* ensure result of calling start algo is an object (#34245)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* return js failed error if underlying source constructor threw (#34246)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Use JSVal for ValueWithSize::value (#34259)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix release reader lock, (#34255)

fix setting stream on controller in new,
fix matching fallthrough,
reduce visibility of controller error method

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* in stream cancel, reject promist if locked (#34271)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix UnderlyingSourceContainer::call_start_algorithm (#34277)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* implement controller cancel steps, fix stream cancel method (#34301)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix conditional in perform pull steps (#34324)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* set reader closed promise to one resolved with undefined if stream closed on init (#34321)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix init of stream and controller (#34323)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Stream: Fix reborrow in controller enqueue, and fix error and exception handling. (#34338)

* fix re-borrow in controller enqueue

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* do not call to_jsval on JSFailed error in enqueue

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* fix error and exception handling in controller enqueue

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* remove TODO about correctness of stored error, since this was done as part of the switch to a js val.

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Stream: Fix incorrect "this" object in underlying source callbacks (#34368)

* in controller close, throw type error if stream cannot be closed

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* store original js object for underlying source, for use as this object in callbacks

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix conditional logic in enqueue to ensure pull is called into (#34375)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Stream: Fix bytelength queueing strategy (#34376)

* fix handling of value that is not an object in bytelength queuing strategy

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* return type error if strategy size call fails, to prevent panic because no exception is pending

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* set correct  default count queuing size strategy (#34389)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* use proto in stream constructor (#34441)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix edge cases in get_desired_size (#34440)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Stream: fix algo and strategy calls error handling. (#34424)

* fix error handling in cancel steps

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* in pull steps, reject promise if pull algo throws

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* if start algorithm fails, rethrow the error

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* when the strategy size fails, directly get the pending exception and use it to error the stream

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* add error handling to enqueue value with size

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* when enqueueing a value errors, ensure we error and stream with the same error used to throw an exception

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix native use of streams (#34468)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Implement readablestreamdefaulttee (#34405)

* Implement readablestreamdefaulttee

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Create UnderlyingSourceType::Tee each stream

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Use Dom instead of DomRoot

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Queue a microtask for readRequest chunk steps

Signed-off-by: Taym <haddadi.taym@gmail.com>

* fix create_readable_stream

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Remove unnecessary Rc

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Use correct doc link

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Add #[allow(crown::unrooted_must_root)]

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Fix crash in ClosedPromiseRejectionHandler

Signed-off-by: Taym <haddadi.taym@gmail.com>

* reflect TeeReadRequest and TeeUnderlyingSource
Signed-off-by: Taym <haddadi.taym@gmail.com>

* fix can_gc

Signed-off-by: Taym <haddadi.taym@gmail.com>

* reflect tee source, and fix use of mutable dom for tee source and request

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* Fix typo that resolves multiple test failures in 'Tee' tests

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Fix readable-streams/tee.any.js test

Signed-off-by: Taym <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Co-authored-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Align ReadableStreamDefaultReader with spec and fix additional tests in default-reader.any.js (#34531)

And fix crate::DomTypeHolder usage

* Align ReadableStreamDefaultReader with spec and fix additional tests in default-reader.any.js

Signed-off-by: Taym <haddadi.taym@gmail.com>

* make reader rooted in Constructor and acquire_default_reader

Signed-off-by: Taym <haddadi.taym@gmail.com>

* Remove spaces

Signed-off-by: Taym <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Streams: fetch stream chunks should be uint8 arrays (#34553)

* fetch stream chunks should be uint8 arrays

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

* fix clippy

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>
Co-authored-by: Taym Haddadi <haddadi.taym@gmail.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Update wpt test for ReadableStream reimplementation (#34579)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix ignore_malloc_size_of in readablestream tee (#34578)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Remove incorrect use of handle array, fail test safely by giving only one reason (#34560)

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Update more wpt test for ReadableStream reimplementation (#34598)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix doc and rename Tee to DefaultTee (#34612)

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix: Address review comments

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Update response-stream-with-broken-then.any.js.ini test expectation

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* fix reflect_dom_object can_gc

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Fix compositeReason for DefaultTeeUnderlyingSource (#34627)

* Fix compositeReason for DefaultTeeUnderlyingSource

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Update test

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

---------

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>

* Last fixes stream (#34636)

* remove now unsused from_js method of readable stream

* fix documenation of error steps

* return type error instread of panicking on a todo, when trying to construct a stream of type bytes

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com>

* fix crown rooting related errors (#34662)

Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com>

---------

Signed-off-by: Taym <haddadi.taym@gmail.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>
Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com>
Co-authored-by: Wu Wayne <yuweiwu@pm.me>
Co-authored-by: Taym Haddadi <haddadi.taym@gmail.com>
This commit is contained in:
Gregory Terzian 2024-12-18 05:14:00 +08:00 committed by GitHub
parent 026d371717
commit 379bbb41dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 3792 additions and 548 deletions

View file

@ -72,7 +72,7 @@ image = { workspace = true }
indexmap = { workspace = true }
ipc-channel = { workspace = true }
itertools = { workspace = true }
js = { package = "mozjs", git = "https://github.com/servo/mozjs", features = ["streams", "crown"] }
js = { package = "mozjs", git = "https://github.com/servo/mozjs", features = ["crown"] }
jstraceable_derive = { path = "../jstraceable_derive" }
keyboard-types = { workspace = true }
libc = { workspace = true }

View file

@ -187,7 +187,8 @@ impl TransmitBodyConnectHandler {
// TODO: Step 2, If body is null.
// Step 3, get a reader for stream.
rooted_stream.start_reading().expect("Couldn't acquire a reader for the body stream.");
rooted_stream.acquire_default_reader(CanGc::note())
.expect("Couldn't acquire a reader for the body stream.");
// Note: this algorithm continues when the first chunk is requested by `net`.
}),
@ -242,7 +243,7 @@ impl TransmitBodyConnectHandler {
let global = rooted_stream.global();
// Step 4, the result of reading a chunk from bodys stream with reader.
let promise = rooted_stream.read_a_chunk();
let promise = rooted_stream.read_a_chunk(CanGc::note());
// Step 5, the parallel steps waiting for and handling the result of the read promise,
// are a combination of the promise native handler here,
@ -692,7 +693,7 @@ impl Callback for ConsumeBodyPromiseHandler {
let global = stream.global();
// Run the above step again.
let read_promise = stream.read_a_chunk();
let read_promise = stream.read_a_chunk(can_gc);
let promise_handler = Box::new(ConsumeBodyPromiseHandler {
result_promise: self.result_promise.clone(),
@ -763,7 +764,7 @@ fn consume_body_with_promise<T: BodyMixin + DomObject>(
};
// Step 3.
if stream.start_reading().is_err() {
if stream.acquire_default_reader(can_gc).is_err() {
return promise.reject_error(Error::Type(
"The response's stream is disturbed or locked".to_string(),
));
@ -774,7 +775,7 @@ fn consume_body_with_promise<T: BodyMixin + DomObject>(
// Step 1 of
// https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
let read_promise = stream.read_a_chunk();
let read_promise = stream.read_a_chunk(can_gc);
let promise_handler = Box::new(ConsumeBodyPromiseHandler {
result_promise: promise.clone(),

View file

@ -15,7 +15,7 @@ use js::jsapi::{
};
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::rust::wrappers::{JS_GetProperty, JS_WrapObject};
use js::rust::{MutableHandleObject, Runtime};
use js::rust::{HandleObject, MutableHandleObject, Runtime};
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
use crate::dom::bindings::error::{report_pending_exception, Error, Fallible};
@ -205,9 +205,29 @@ impl CallbackInterface {
}
}
pub trait ThisReflector {
fn jsobject(&self) -> *mut JSObject;
}
impl<T: DomObject> ThisReflector for T {
fn jsobject(&self) -> *mut JSObject {
self.reflector().get_jsobject().get()
}
}
impl<'a> ThisReflector for HandleObject<'a> {
fn jsobject(&self) -> *mut JSObject {
self.get()
}
}
/// Wraps the reflector for `p` into the realm of `cx`.
pub fn wrap_call_this_object<T: DomObject>(cx: JSContext, p: &T, mut rval: MutableHandleObject) {
rval.set(p.reflector().get_jsobject().get());
pub fn wrap_call_this_object<T: ThisReflector>(
cx: JSContext,
p: &T,
mut rval: MutableHandleObject,
) {
rval.set(p.jsobject());
assert!(!rval.get().is_null());
unsafe {

View file

@ -538,6 +538,22 @@ DOMInterfaces = {
'canGc': ['SimulateDeviceConnection', 'DisconnectAllDevices'],
},
'ReadableStream': {
'canGc': ['GetReader', 'Cancel', 'Tee'],
},
"ReadableStreamDefaultController": {
"canGc": ["Enqueue"]
},
"ReadableStreamBYOBReader": {
"canGc": ["Read", "Closed", "Cancel"]
},
"ReadableStreamDefaultReader": {
"canGc": ["Read", "Cancel"]
},
}
Dictionaries = {

View file

@ -933,6 +933,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
"""
{
use crate::realms::{AlreadyInRealm, InRealm};
use crate::dom::readablestream::ReadableStream;
let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
match ReadableStream::from_js(cx, $${val}.get().to_object(), InRealm::Already(&in_realm_proof)) {
Ok(val) => val,
@ -949,7 +950,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
templateBody = wrapObjectTemplate(templateBody, "None",
isDefinitelyObject, type, failureCode)
declType = CGGeneric("DomRoot<ReadableStream>")
declType = CGGeneric("DomRoot<dom::readablestream::ReadableStream>")
return handleOptional(templateBody, declType,
handleDefault("None"))
@ -7718,7 +7719,7 @@ class CGCallback(CGClass):
f"unsafe {{ self.{method.name}({', '.join(argnamesWithoutThis)}) }}")
return [ClassMethod(f'{method.name}_', method.returnType, args,
bodyInHeader=True,
templateArgs=["T: DomObject"],
templateArgs=["T: ThisReflector"],
body=bodyWithThis,
visibility='pub'),
ClassMethod(f'{method.name}__', method.returnType, argsWithoutThis,

View file

@ -30,7 +30,7 @@ def main():
from Configuration import Configuration
from CodegenRust import CGBindingRoot
parser = WebIDL.Parser(make_dir(os.path.join(out_dir, "cache")))
parser = WebIDL.Parser(make_dir(os.path.join(out_dir, "cache")), use_builtin_readable_stream=False)
webidls = [name for name in os.listdir(webidls_dir) if name.endswith(".webidl")]
for webidl in webidls:
filename = os.path.join(webidls_dir, webidl)

View file

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Defines a macro `native_fn!` to create a JavaScript function from a Rust function pointer.
/// # Example
/// ```
/// let js_function: Rc<Function> = native_fn!(my_rust_function, c"myFunction", 2, 0);
/// ```
#[macro_export]
macro_rules! native_fn {
($call:expr, $name:expr, $nargs:expr, $flags:expr) => {{
let cx = $crate::dom::types::GlobalScope::get_cx();
let fun_obj = $crate::native_raw_obj_fn!(cx, $call, $name, $nargs, $flags);
#[allow(unsafe_code)]
unsafe {
Function::new(cx, fun_obj)
}
}};
}
/// Defines a macro `native_raw_obj_fn!` to create a raw JavaScript function object.
/// # Example
/// ```
/// let raw_function_obj: *mut JSObject = native_raw_obj_fn!(cx, my_rust_function, c"myFunction", 2, 0);
/// ```
#[macro_export]
macro_rules! native_raw_obj_fn {
($cx:expr, $call:expr, $name:expr, $nargs:expr, $flags:expr) => {{
#[allow(unsafe_code)]
#[allow(clippy::macro_metavars_in_unsafe)]
unsafe extern "C" fn wrapper(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
$call(cx, argc, vp)
}
#[allow(unsafe_code)]
#[allow(clippy::macro_metavars_in_unsafe)]
unsafe {
let name: &std::ffi::CStr = $name;
let raw_fun = $crate::dom::bindings::import::module::jsapi::JS_NewFunction(
*$cx,
Some(wrapper),
$nargs,
$flags,
name.as_ptr() as *const std::ffi::c_char,
);
assert!(!raw_fun.is_null());
$crate::dom::bindings::import::module::jsapi::JS_GetFunctionObject(raw_fun)
}
}};
}

View file

@ -19,7 +19,7 @@ pub mod base {
pub use crate::dom::bindings::callback::{
wrap_call_this_object, CallSetup, CallbackContainer, CallbackFunction, CallbackInterface,
CallbackObject, ExceptionHandling,
CallbackObject, ExceptionHandling, ThisReflector,
};
pub use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
ChannelCountMode, ChannelCountModeValues, ChannelInterpretation,

View file

@ -146,7 +146,6 @@ pub unsafe fn create_global_object(
let mut options = RealmOptions::default();
options.creationOptions_.traceGlobal_ = Some(trace);
options.creationOptions_.sharedMemoryAndAtomics_ = false;
options.creationOptions_.streams_ = true;
select_compartment(cx, &mut options);
let principal = ServoJSPrincipals::new(origin);

View file

@ -143,6 +143,7 @@ pub mod conversions;
pub mod error;
pub mod finalize;
pub mod frozenarray;
pub mod function;
pub mod guard;
pub mod import;
pub mod inheritance;

View file

@ -0,0 +1,113 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::rc::Rc;
use dom_struct::dom_struct;
use js::gc::{HandleValue, MutableHandleValue};
use js::jsapi::{CallArgs, JSContext};
use js::jsval::JSVal;
use js::rust::HandleObject;
use super::bindings::codegen::Bindings::FunctionBinding::Function;
use super::bindings::codegen::Bindings::QueuingStrategyBinding::{
ByteLengthQueuingStrategyMethods, QueuingStrategyInit,
};
use super::bindings::import::module::{DomObject, DomRoot, Fallible, Reflector};
use super::bindings::reflector::reflect_dom_object_with_proto;
use super::types::GlobalScope;
use crate::dom::bindings::import::module::get_dictionary_property;
use crate::native_fn;
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct ByteLengthQueuingStrategy {
reflector_: Reflector,
high_water_mark: f64,
}
impl ByteLengthQueuingStrategy {
pub fn new_inherited(init: f64) -> Self {
Self {
reflector_: Reflector::new(),
high_water_mark: init,
}
}
pub fn new(
global: &GlobalScope,
proto: Option<HandleObject>,
init: f64,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object_with_proto(Box::new(Self::new_inherited(init)), global, proto, can_gc)
}
}
impl ByteLengthQueuingStrategyMethods<crate::DomTypeHolder> for ByteLengthQueuingStrategy {
/// <https://streams.spec.whatwg.org/#blqs-constructor>
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
init: &QueuingStrategyInit,
) -> DomRoot<Self> {
Self::new(global, proto, init.highWaterMark, can_gc)
}
/// <https://streams.spec.whatwg.org/#blqs-high-water-mark>
fn HighWaterMark(&self) -> f64 {
self.high_water_mark
}
/// <https://streams.spec.whatwg.org/#blqs-size>
fn GetSize(&self) -> Fallible<Rc<Function>> {
let global = self.reflector_.global();
// Return this's relevant global object's byte length queuing strategy
// size function.
if let Some(fun) = global.get_byte_length_queuing_strategy_size() {
return Ok(fun);
}
// Step 1. Let steps be the following steps, given chunk
// Note: See ByteLengthQueuingStrategySize instead.
// Step 2. Let F be !CreateBuiltinFunction(steps, 1, "size", « »,
// globalObjects relevant Realm).
let fun = native_fn!(byte_length_queuing_strategy_size, c"size", 1, 0);
// Step 3. Set globalObjects byte length queuing strategy size function to
// a Function that represents a reference to F,
// with callback context equal to globalObjects relevant settings object.
global.set_byte_length_queuing_strategy_size(fun.clone());
Ok(fun)
}
}
/// <https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function>
#[allow(unsafe_code)]
pub unsafe fn byte_length_queuing_strategy_size(
cx: *mut JSContext,
argc: u32,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, argc);
// Step 1.1: Return ? GetV(chunk, "byteLength").
let val = HandleValue::from_raw(args.get(0));
// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-getv
// Let O be ? ToObject(V).
if !val.is_object() {
return false;
}
rooted!(in(cx) let object = val.to_object());
// Return ? O.[[Get]](P, V).
get_dictionary_property(
cx,
object.handle(),
"byteLength",
MutableHandleValue::from_raw(args.rval()),
)
.unwrap_or(false)
}

View file

@ -0,0 +1,127 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::{CallArgs, JSContext};
use js::jsval::{Int32Value, JSVal};
use js::rust::HandleObject;
use super::bindings::codegen::Bindings::FunctionBinding::Function;
use super::bindings::codegen::Bindings::QueuingStrategyBinding::{
CountQueuingStrategyMethods, QueuingStrategy, QueuingStrategyInit, QueuingStrategySize,
};
use super::bindings::import::module::{DomObject, DomRoot, Error, Fallible, Reflector};
use super::bindings::reflector::reflect_dom_object_with_proto;
use super::types::GlobalScope;
use crate::script_runtime::CanGc;
use crate::{native_fn, native_raw_obj_fn};
#[dom_struct]
pub struct CountQueuingStrategy {
reflector_: Reflector,
high_water_mark: f64,
}
impl CountQueuingStrategy {
pub fn new_inherited(init: f64) -> Self {
Self {
reflector_: Reflector::new(),
high_water_mark: init,
}
}
pub fn new(
global: &GlobalScope,
proto: Option<HandleObject>,
init: f64,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object_with_proto(Box::new(Self::new_inherited(init)), global, proto, can_gc)
}
}
impl CountQueuingStrategyMethods<crate::DomTypeHolder> for CountQueuingStrategy {
/// <https://streams.spec.whatwg.org/#cqs-constructor>
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
init: &QueuingStrategyInit,
) -> DomRoot<Self> {
Self::new(global, proto, init.highWaterMark, can_gc)
}
/// <https://streams.spec.whatwg.org/#cqs-high-water-mark>
fn HighWaterMark(&self) -> f64 {
self.high_water_mark
}
/// <https://streams.spec.whatwg.org/#cqs-size>
fn GetSize(&self) -> Fallible<Rc<Function>> {
let global = self.reflector_.global();
// Return this's relevant global object's count queuing strategy
// size function.
if let Some(fun) = global.get_count_queuing_strategy_size() {
return Ok(fun);
}
// Step 1. Let steps be the following steps, given chunk
// Note: See ByteLengthQueuingStrategySize instead.
// Step 2. Let F be !CreateBuiltinFunction(steps, 1, "size", « »,
// globalObjects relevant Realm).
let fun = native_fn!(count_queuing_strategy_size, c"size", 0, 0);
// Step 3. Set globalObjects count queuing strategy size function to
// a Function that represents a reference to F,
// with callback context equal to globalObjects relevant settings object.
global.set_count_queuing_strategy_size(fun.clone());
Ok(fun)
}
}
/// <https://streams.spec.whatwg.org/#count-queuing-strategy-size-function>
#[allow(unsafe_code)]
pub unsafe fn count_queuing_strategy_size(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
// Step 1.1. Return 1.
args.rval().set(Int32Value(1));
true
}
/// Extract the high water mark from a QueuingStrategy.
/// If the high water mark is not set, return the default value.
///
/// <https://streams.spec.whatwg.org/#validate-and-normalize-high-water-mark>
pub fn extract_high_water_mark(strategy: &QueuingStrategy, default_hwm: f64) -> Result<f64, Error> {
if strategy.highWaterMark.is_none() {
return Ok(default_hwm);
}
let high_water_mark = strategy.highWaterMark.unwrap();
if high_water_mark.is_nan() || high_water_mark < 0.0 {
return Err(Error::Range(
"High water mark must be a non-negative number.".to_string(),
));
}
Ok(high_water_mark)
}
/// Extract the size algorithm from a QueuingStrategy.
/// If the size algorithm is not set, return a fallback function which always returns 1.
///
/// <https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function>
pub fn extract_size_algorithm(strategy: &QueuingStrategy) -> Rc<QueuingStrategySize> {
if strategy.size.is_none() {
let cx = GlobalScope::get_cx();
let fun_obj = native_raw_obj_fn!(cx, count_queuing_strategy_size, c"size", 0, 0);
#[allow(unsafe_code)]
unsafe {
return QueuingStrategySize::new(cx, fun_obj);
};
}
strategy.size.as_ref().unwrap().clone()
}

View file

@ -0,0 +1,238 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue};
use js::rust::HandleValue as SafeHandleValue;
use super::bindings::reflector::reflect_dom_object;
use super::bindings::root::DomRoot;
use super::bindings::structuredclone;
use crate::dom::bindings::reflector::{DomObject, Reflector};
use crate::dom::bindings::root::Dom;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestream::ReadableStream;
use crate::microtask::Microtask;
use crate::script_runtime::CanGc;
#[derive(JSTraceable, MallocSizeOf)]
#[allow(crown::unrooted_must_root)]
pub struct DefaultTeeReadRequestMicrotask {
#[ignore_malloc_size_of = "mozjs"]
chunk: Box<Heap<JSVal>>,
tee_read_request: Dom<DefaultTeeReadRequest>,
}
impl DefaultTeeReadRequestMicrotask {
pub fn microtask_chunk_steps(&self, can_gc: CanGc) {
self.tee_read_request.chunk_steps(&self.chunk, can_gc)
}
}
#[dom_struct]
/// <https://streams.spec.whatwg.org/#ref-for-read-request%E2%91%A2>
pub struct DefaultTeeReadRequest {
reflector_: Reflector,
stream: Dom<ReadableStream>,
branch_1: Dom<ReadableStream>,
branch_2: Dom<ReadableStream>,
#[ignore_malloc_size_of = "Rc"]
reading: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
read_again: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_1: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
clone_for_branch_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
cancel_promise: Rc<Promise>,
tee_underlying_source: Dom<DefaultTeeUnderlyingSource>,
}
impl DefaultTeeReadRequest {
#[allow(clippy::too_many_arguments)]
#[allow(crown::unrooted_must_root)]
pub fn new(
stream: &ReadableStream,
branch_1: &ReadableStream,
branch_2: &ReadableStream,
reading: Rc<Cell<bool>>,
read_again: Rc<Cell<bool>>,
canceled_1: Rc<Cell<bool>>,
canceled_2: Rc<Cell<bool>>,
clone_for_branch_2: Rc<Cell<bool>>,
cancel_promise: Rc<Promise>,
tee_underlying_source: &DefaultTeeUnderlyingSource,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(DefaultTeeReadRequest {
reflector_: Reflector::new(),
stream: Dom::from_ref(stream),
branch_1: Dom::from_ref(branch_1),
branch_2: Dom::from_ref(branch_2),
reading,
read_again,
canceled_1,
canceled_2,
clone_for_branch_2,
cancel_promise,
tee_underlying_source: Dom::from_ref(tee_underlying_source),
}),
&*stream.global(),
can_gc,
)
}
/// Call into cancel of the stream,
/// <https://streams.spec.whatwg.org/#readable-stream-cancel>
pub fn stream_cancel(&self, reason: SafeHandleValue, can_gc: CanGc) {
self.stream.cancel(reason, can_gc);
}
/// Enqueue a microtask to perform the chunk steps
/// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A2>
pub fn enqueue_chunk_steps(&self, chunk: RootedTraceableBox<Heap<JSVal>>) {
// Queue a microtask to perform the following steps:
let tee_read_request_chunk = DefaultTeeReadRequestMicrotask {
chunk: Heap::boxed(*chunk.handle()),
tee_read_request: Dom::from_ref(self),
};
let global = self.stream.global();
let microtask_queue = global.microtask_queue();
let cx = GlobalScope::get_cx();
microtask_queue.enqueue(
Microtask::ReadableStreamTeeReadRequest(tee_read_request_chunk),
cx,
);
}
/// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A2>
#[allow(unsafe_code)]
#[allow(clippy::borrowed_box)]
pub fn chunk_steps(&self, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
// Set readAgain to false.
self.read_again.set(false);
// Let chunk1 and chunk2 be chunk.
let chunk1 = chunk;
let chunk2 = chunk;
// If canceled_2 is false and cloneForBranch2 is true,
if !self.canceled_2.get() && self.clone_for_branch_2.get() {
let cx = GlobalScope::get_cx();
// Let cloneResult be StructuredClone(chunk2).
rooted!(in(*cx) let mut clone_result = UndefinedValue());
let data = structuredclone::write(
cx,
unsafe { SafeHandleValue::from_raw(chunk2.handle()) },
None,
)
.unwrap();
// If cloneResult is an abrupt completion,
if structuredclone::read(&self.stream.global(), data, clone_result.handle_mut())
.is_err()
{
// Perform ! ReadableStreamDefaultControllerError(branch_1.[[controller]], cloneResult.[[Value]]).
self.readable_stream_default_controller_error(
&self.branch_1,
clone_result.handle(),
);
// Perform ! ReadableStreamDefaultControllerError(branch_2.[[controller]], cloneResult.[[Value]]).
self.readable_stream_default_controller_error(
&self.branch_2,
clone_result.handle(),
);
// Resolve cancelPromise with ! ReadableStreamCancel(stream, cloneResult.[[Value]]).
self.stream_cancel(clone_result.handle(), can_gc);
// Return.
return;
} else {
// Otherwise, set chunk2 to cloneResult.[[Value]].
chunk2.set(*clone_result);
}
}
// If canceled_1 is false, perform ! ReadableStreamDefaultControllerEnqueue(branch_1.[[controller]], chunk1).
if !self.canceled_1.get() {
self.readable_stream_default_controller_enqueue(
&self.branch_1,
unsafe { SafeHandleValue::from_raw(chunk1.handle()) },
can_gc,
);
}
// If canceled_2 is false, perform ! ReadableStreamDefaultControllerEnqueue(branch_2.[[controller]], chunk2).
if !self.canceled_2.get() {
self.readable_stream_default_controller_enqueue(
&self.branch_2,
unsafe { SafeHandleValue::from_raw(chunk2.handle()) },
can_gc,
);
}
// Set reading to false.
self.reading.set(false);
// If readAgain is true, perform pullAlgorithm.
if self.read_again.get() {
self.pull_algorithm(can_gc);
}
}
/// <https://streams.spec.whatwg.org/#read-request-close-steps>
pub fn close_steps(&self) {
// Set reading to false.
self.reading.set(false);
// If canceled_1 is false, perform ! ReadableStreamDefaultControllerClose(branch_1.[[controller]]).
if !self.canceled_1.get() {
self.readable_stream_default_controller_close(&self.branch_1);
}
// If canceled_2 is false, perform ! ReadableStreamDefaultControllerClose(branch_2.[[controller]]).
if !self.canceled_2.get() {
self.readable_stream_default_controller_close(&self.branch_2);
}
// If canceled_1 is false or canceled_2 is false, resolve cancelPromise with undefined.
if !self.canceled_1.get() || !self.canceled_2.get() {
self.cancel_promise.resolve_native(&());
}
}
/// <https://streams.spec.whatwg.org/#read-request-error-steps>
pub fn error_steps(&self) {
// Set reading to false.
self.reading.set(false);
}
/// Call into enqueue of the default controller of a stream,
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-enqueue>
fn readable_stream_default_controller_enqueue(
&self,
stream: &ReadableStream,
chunk: SafeHandleValue,
can_gc: CanGc,
) {
stream
.get_default_controller()
.enqueue(GlobalScope::get_cx(), chunk, can_gc)
.expect("enqueue failed for stream controller in DefaultTeeReadRequest");
}
/// Call into close of the default controller of a stream,
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-close>
fn readable_stream_default_controller_close(&self, stream: &ReadableStream) {
stream.get_default_controller().close();
}
/// Call into error of the default controller of stream,
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-error>
fn readable_stream_default_controller_error(
&self,
stream: &ReadableStream,
error: SafeHandleValue,
) {
stream.get_default_controller().error(error);
}
pub fn pull_algorithm(&self, can_gc: CanGc) {
self.tee_underlying_source.pull_algorithm(can_gc);
}
}

View file

@ -0,0 +1,221 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::{HandleValueArray, Heap, NewArrayObject, Value};
use js::jsval::{ObjectValue, UndefinedValue};
use js::rust::HandleValue as SafeHandleValue;
use super::bindings::root::{DomRoot, MutNullableDom};
use super::types::{ReadableStream, ReadableStreamDefaultReader};
use crate::dom::bindings::import::module::Error;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::Dom;
use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestreamdefaultreader::ReadRequest;
use crate::script_runtime::CanGc;
#[derive(JSTraceable, MallocSizeOf)]
pub enum TeeCancelAlgorithm {
Cancel1Algorithm,
Cancel2Algorithm,
}
#[dom_struct]
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
pub struct DefaultTeeUnderlyingSource {
reflector_: Reflector,
reader: Dom<ReadableStreamDefaultReader>,
stream: Dom<ReadableStream>,
branch_1: MutNullableDom<ReadableStream>,
branch_2: MutNullableDom<ReadableStream>,
#[ignore_malloc_size_of = "Rc"]
reading: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
read_again: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_1: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
clone_for_branch_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
#[allow(clippy::redundant_allocation)]
reason_1: Rc<Box<Heap<Value>>>,
#[ignore_malloc_size_of = "Rc"]
#[allow(clippy::redundant_allocation)]
reason_2: Rc<Box<Heap<Value>>>,
#[ignore_malloc_size_of = "Rc"]
cancel_promise: Rc<Promise>,
tee_cancel_algorithm: TeeCancelAlgorithm,
}
impl DefaultTeeUnderlyingSource {
#[allow(clippy::too_many_arguments)]
#[allow(clippy::redundant_allocation)]
#[allow(crown::unrooted_must_root)]
pub fn new(
reader: &ReadableStreamDefaultReader,
stream: &ReadableStream,
reading: Rc<Cell<bool>>,
read_again: Rc<Cell<bool>>,
canceled_1: Rc<Cell<bool>>,
canceled_2: Rc<Cell<bool>>,
clone_for_branch_2: Rc<Cell<bool>>,
reason_1: Rc<Box<Heap<Value>>>,
reason_2: Rc<Box<Heap<Value>>>,
cancel_promise: Rc<Promise>,
tee_cancel_algorithm: TeeCancelAlgorithm,
can_gc: CanGc,
) -> DomRoot<DefaultTeeUnderlyingSource> {
reflect_dom_object(
Box::new(DefaultTeeUnderlyingSource {
reflector_: Reflector::new(),
reader: Dom::from_ref(reader),
stream: Dom::from_ref(stream),
branch_1: MutNullableDom::new(None),
branch_2: MutNullableDom::new(None),
reading,
read_again,
canceled_1,
canceled_2,
clone_for_branch_2,
reason_1,
reason_2,
cancel_promise,
tee_cancel_algorithm,
}),
&*stream.global(),
can_gc,
)
}
pub fn set_branch_1(&self, stream: &ReadableStream) {
self.branch_1.set(Some(stream));
}
pub fn set_branch_2(&self, stream: &ReadableStream) {
self.branch_2.set(Some(stream));
}
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
/// Let pullAlgorithm be the following steps:
#[allow(crown::unrooted_must_root)]
pub fn pull_algorithm(&self, can_gc: CanGc) -> Option<Result<Rc<Promise>, Error>> {
// If reading is true,
if self.reading.get() {
// Set readAgain to true.
self.read_again.set(true);
// Return a promise resolved with undefined.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
return Some(Promise::new_resolved(
&self.stream.global(),
cx,
rval.handle(),
));
}
// Set reading to true.
self.reading.set(true);
// Let readRequest be a read request with the following items:
let tee_read_request = DefaultTeeReadRequest::new(
&self.stream,
&self.branch_1.get().expect("Branch 1 should be set."),
&self.branch_2.get().expect("Branch 2 should be set."),
self.reading.clone(),
self.read_again.clone(),
self.canceled_1.clone(),
self.canceled_2.clone(),
self.clone_for_branch_2.clone(),
self.cancel_promise.clone(),
self,
can_gc,
);
// Rooting: the tee read request is rooted above.
let read_request = ReadRequest::DefaultTee {
tee_read_request: Dom::from_ref(&tee_read_request),
};
// Perform ! ReadableStreamDefaultReaderRead(reader, readRequest).
self.reader.read(&read_request, can_gc);
// Return a promise resolved with undefined.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
Some(Promise::new_resolved(
&self.stream.global(),
GlobalScope::get_cx(),
rval.handle(),
))
}
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
/// Let cancel1Algorithm be the following steps, taking a reason argument
/// and
/// Let cancel2Algorithm be the following steps, taking a reason argument
#[allow(unsafe_code)]
pub fn cancel_algorithm(
&self,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
match self.tee_cancel_algorithm {
TeeCancelAlgorithm::Cancel1Algorithm => {
// Set canceled_1 to true.
self.canceled_1.set(true);
// Set reason_1 to reason.
self.reason_1.set(reason.get());
// If canceled_2 is true,
if self.canceled_2.get() {
self.resolve_cancel_promise(can_gc);
}
// Return cancelPromise.
Some(Ok(self.cancel_promise.clone()))
},
TeeCancelAlgorithm::Cancel2Algorithm => {
// Set canceled_2 to true.
self.canceled_2.set(true);
// Set reason_2 to reason.
self.reason_2.set(reason.get());
// If canceled_1 is true,
if self.canceled_1.get() {
self.resolve_cancel_promise(can_gc);
}
// Return cancelPromise.
Some(Ok(self.cancel_promise.clone()))
},
}
}
#[allow(unsafe_code)]
fn resolve_cancel_promise(&self, can_gc: CanGc) {
// Let compositeReason be ! CreateArrayFromList(« reason_1, reason_2 »).
let cx = GlobalScope::get_cx();
rooted_vec!(let mut reasons_values);
reasons_values.push(self.reason_1.get());
reasons_values.push(self.reason_2.get());
let reasons_values_array = HandleValueArray::from(&reasons_values);
rooted!(in(*cx) let reasons = unsafe { NewArrayObject(*cx, &reasons_values_array) });
rooted!(in(*cx) let reasons_value = ObjectValue(reasons.get()));
// Let cancelResult be ! ReadableStreamCancel(stream, compositeReason).
let cancel_result = self.stream.cancel(reasons_value.handle(), can_gc);
// Resolve cancelPromise with cancelResult.
self.cancel_promise.resolve_native(&cancel_result);
}
}

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::cell::Cell;
use std::cell::{Cell, OnceCell};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::ops::Index;
@ -73,6 +73,7 @@ use super::bindings::trace::{HashMapTracedValues, RootedTraceableBox};
use crate::dom::bindings::cell::{DomRefCell, RefMut};
use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods;
use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSource_Binding::EventSourceMethods;
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
ImageBitmapOptions, ImageBitmapSource,
};
@ -92,6 +93,7 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::CustomTraceable;
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
use crate::dom::blob::Blob;
use crate::dom::broadcastchannel::BroadcastChannel;
@ -114,9 +116,10 @@ use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::performance::Performance;
use crate::dom::performanceobserver::VALID_ENTRY_TYPES;
use crate::dom::promise::Promise;
use crate::dom::readablestream::{ExternalUnderlyingSource, ReadableStream};
use crate::dom::readablestream::ReadableStream;
use crate::dom::serviceworker::ServiceWorker;
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
#[cfg(feature = "webgpu")]
use crate::dom::webgpu::gpudevice::GPUDevice;
#[cfg(feature = "webgpu")]
@ -367,6 +370,20 @@ pub struct GlobalScope {
/// Directory to store unminified scripts for this window if unminify-js
/// opt is enabled.
unminified_js_dir: Option<String>,
/// The byte length queuing strategy size function that will be initialized once
/// `size` getter of `ByteLengthQueuingStrategy` is called.
///
/// <https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function>
#[ignore_malloc_size_of = "Rc<T> is hard"]
byte_length_queuing_strategy_size_function: OnceCell<Rc<Function>>,
/// The count queuing strategy size function that will be initialized once
/// `size` getter of `CountQueuingStrategy` is called.
///
/// <https://streams.spec.whatwg.org/#count-queuing-strategy-size-function>
#[ignore_malloc_size_of = "Rc<T> is hard"]
count_queuing_strategy_size_function: OnceCell<Rc<Function>>,
}
/// A wrapper for glue-code between the ipc router and the event-loop.
@ -629,10 +646,10 @@ impl MessageListener {
}
/// Callback used to enqueue file chunks to streams as part of FileListener.
fn stream_handle_incoming(stream: &ReadableStream, bytes: Fallible<Vec<u8>>, can_gc: CanGc) {
fn stream_handle_incoming(stream: &ReadableStream, bytes: Fallible<Vec<u8>>) {
match bytes {
Ok(b) => {
stream.enqueue_native(b, can_gc);
stream.enqueue_native(b);
},
Err(e) => {
stream.error_native(e);
@ -642,7 +659,7 @@ fn stream_handle_incoming(stream: &ReadableStream, bytes: Fallible<Vec<u8>>, can
/// Callback used to close streams as part of FileListener.
fn stream_handle_eof(stream: &ReadableStream) {
stream.close_native();
stream.controller_close_native();
}
impl FileListener {
@ -655,7 +672,7 @@ impl FileListener {
let task = task!(enqueue_stream_chunk: move || {
let stream = trusted.root();
stream_handle_incoming(&stream, Ok(blob_buf.bytes), CanGc::note());
stream_handle_incoming(&stream, Ok(blob_buf.bytes));
});
let _ = self
@ -679,7 +696,7 @@ impl FileListener {
let task = task!(enqueue_stream_chunk: move || {
let stream = trusted.root();
stream_handle_incoming(&stream, Ok(bytes_in), CanGc::note());
stream_handle_incoming(&stream, Ok(bytes_in));
});
let _ = self
@ -745,7 +762,7 @@ impl FileListener {
let _ = self.task_source.queue_with_canceller(
task!(error_stream: move || {
let stream = trusted_stream.root();
stream_handle_incoming(&stream, error, CanGc::note());
stream_handle_incoming(&stream, error);
}),
&self.task_canceller,
);
@ -820,6 +837,8 @@ impl GlobalScope {
dynamic_modules: DomRefCell::new(DynamicModuleList::new()),
inherited_secure_context,
unminified_js_dir: unminify_js.then(|| unminified_path("unminified-js")),
byte_length_queuing_strategy_size_function: OnceCell::new(),
count_queuing_strategy_size_function: OnceCell::new(),
}
}
@ -2016,7 +2035,8 @@ impl GlobalScope {
let stream = ReadableStream::new_with_external_underlying_source(
self,
ExternalUnderlyingSource::Blob(size),
UnderlyingSourceType::Blob(size),
can_gc,
);
let recv = self.send_msg(file_id);
@ -3447,6 +3467,36 @@ impl GlobalScope {
pub fn unminified_js_dir(&self) -> Option<String> {
self.unminified_js_dir.clone()
}
pub(crate) fn set_byte_length_queuing_strategy_size(&self, function: Rc<Function>) {
if self
.byte_length_queuing_strategy_size_function
.set(function)
.is_err()
{
warn!("byte length queuing strategy size function is set twice.");
};
}
pub(crate) fn get_byte_length_queuing_strategy_size(&self) -> Option<Rc<Function>> {
self.byte_length_queuing_strategy_size_function
.get()
.cloned()
}
pub(crate) fn set_count_queuing_strategy_size(&self, function: Rc<Function>) {
if self
.count_queuing_strategy_size_function
.set(function)
.is_err()
{
warn!("count queuing strategy size function is set twice.");
};
}
pub(crate) fn get_count_queuing_strategy_size(&self) -> Option<Rc<Function>> {
self.count_queuing_strategy_size_function.get().cloned()
}
}
/// Returns the Rust global scope from a JS global object.

View file

@ -243,6 +243,7 @@ pub mod bluetoothremotegattserver;
pub mod bluetoothremotegattservice;
pub mod bluetoothuuid;
pub mod broadcastchannel;
pub mod bytelengthqueuingstrategy;
pub mod canvasgradient;
pub mod canvaspattern;
pub mod canvasrenderingcontext2d;
@ -256,6 +257,7 @@ pub mod comment;
pub mod compositionevent;
pub mod console;
pub mod constantsourcenode;
pub mod countqueuingstrategy;
mod create;
pub mod crypto;
pub mod cryptokey;
@ -283,6 +285,8 @@ pub mod datatransfer;
pub mod datatransferitem;
pub mod datatransferitemlist;
pub mod dedicatedworkerglobalscope;
pub mod defaultteereadrequest;
pub mod defaultteeunderlyingsource;
pub mod dissimilaroriginlocation;
pub mod dissimilaroriginwindow;
pub mod document;
@ -475,7 +479,12 @@ pub mod promiserejectionevent;
pub mod radionodelist;
pub mod range;
pub mod raredata;
pub mod readablebytestreamcontroller;
pub mod readablestream;
pub mod readablestreambyobreader;
pub mod readablestreambyobrequest;
pub mod readablestreamdefaultcontroller;
pub mod readablestreamdefaultreader;
pub mod request;
pub mod resizeobserver;
pub mod resizeobserverentry;
@ -540,6 +549,7 @@ pub mod trackevent;
pub mod transitionevent;
pub mod treewalker;
pub mod uievent;
pub mod underlyingsourcecontainer;
pub mod url;
pub mod urlhelper;
pub mod urlsearchparams;

View file

@ -24,9 +24,9 @@ use js::jsapi::{
};
use js::jsval::{Int32Value, JSVal, ObjectValue, UndefinedValue};
use js::rust::wrappers::{
AddPromiseReactions, CallOriginalPromiseReject, CallOriginalPromiseResolve, GetPromiseState,
IsPromiseObject, NewPromiseObject, RejectPromise, ResolvePromise,
SetPromiseUserInputEventHandlingState,
AddPromiseReactions, CallOriginalPromiseReject, CallOriginalPromiseResolve,
GetPromiseIsHandled, GetPromiseState, IsPromiseObject, NewPromiseObject, RejectPromise,
ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState,
};
use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
@ -285,6 +285,17 @@ impl Promise {
assert!(ok);
}
}
#[allow(unsafe_code)]
pub fn get_promise_is_handled(&self) -> bool {
unsafe { GetPromiseIsHandled(self.reflector().get_jsobject()) }
}
#[allow(unsafe_code)]
pub fn set_promise_is_handled(&self) -> bool {
let cx = GlobalScope::get_cx();
unsafe { SetAnyPromiseIsHandled(*cx, self.reflector().get_jsobject()) }
}
}
#[allow(unsafe_code)]

View file

@ -0,0 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use js::rust::HandleValue as SafeHandleValue;
use crate::dom::bindings::codegen::Bindings::ReadableByteStreamControllerBinding::ReadableByteStreamControllerMethods;
use crate::dom::bindings::import::module::{Error, Fallible};
use crate::dom::bindings::reflector::Reflector;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::readablestream::ReadableStream;
use crate::script_runtime::JSContext as SafeJSContext;
/// <https://streams.spec.whatwg.org/#readablebytestreamcontroller>
#[dom_struct]
pub struct ReadableByteStreamController {
reflector_: Reflector,
stream: MutNullableDom<ReadableStream>,
}
impl ReadableByteStreamController {
pub fn set_stream(&self, stream: &ReadableStream) {
self.stream.set(Some(stream))
}
}
impl ReadableByteStreamControllerMethods<crate::DomTypeHolder> for ReadableByteStreamController {
/// <https://streams.spec.whatwg.org/#rbs-controller-byob-request>
fn GetByobRequest(
&self,
) -> Fallible<Option<DomRoot<super::readablestreambyobrequest::ReadableStreamBYOBRequest>>>
{
// TODO
Err(Error::NotFound)
}
/// <https://streams.spec.whatwg.org/#rbs-controller-desired-size>
fn GetDesiredSize(&self) -> Option<f64> {
// TODO
None
}
/// <https://streams.spec.whatwg.org/#rbs-controller-close>
fn Close(&self) -> Fallible<()> {
// TODO
Err(Error::NotFound)
}
/// <https://streams.spec.whatwg.org/#rbs-controller-enqueue>
fn Enqueue(
&self,
_chunk: js::gc::CustomAutoRooterGuard<js::typedarray::ArrayBufferView>,
) -> Fallible<()> {
// TODO
Err(Error::NotFound)
}
/// <https://streams.spec.whatwg.org/#rbs-controller-error>
fn Error(&self, _cx: SafeJSContext, _e: SafeHandleValue) -> Fallible<()> {
// TODO
Err(Error::NotFound)
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,81 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(dead_code)]
use std::rc::Rc;
use dom_struct::dom_struct;
use js::gc::CustomAutoRooterGuard;
use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
use js::typedarray::ArrayBufferView;
use crate::dom::bindings::codegen::Bindings::ReadableStreamBYOBReaderBinding::ReadableStreamBYOBReaderMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::import::module::Fallible;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestream::ReadableStream;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// <https://streams.spec.whatwg.org/#readablestreambyobreader>
#[dom_struct]
pub struct ReadableStreamBYOBReader {
reflector_: Reflector,
}
impl ReadableStreamBYOBReader {
fn new_inherited() -> ReadableStreamBYOBReader {
ReadableStreamBYOBReader {
reflector_: Reflector::new(),
}
}
fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<ReadableStreamBYOBReader> {
reflect_dom_object(
Box::new(ReadableStreamBYOBReader::new_inherited()),
global,
can_gc,
)
}
}
impl ReadableStreamBYOBReaderMethods<crate::DomTypeHolder> for ReadableStreamBYOBReader {
/// <https://streams.spec.whatwg.org/#byob-reader-constructor>
fn Constructor(
_global: &GlobalScope,
_proto: Option<SafeHandleObject>,
_can_gc: CanGc,
_stream: &ReadableStream,
) -> Fallible<DomRoot<Self>> {
// TODO
Err(Error::NotFound)
}
/// <https://streams.spec.whatwg.org/#byob-reader-read>
fn Read(&self, _view: CustomAutoRooterGuard<ArrayBufferView>, can_gc: CanGc) -> Rc<Promise> {
// TODO
Promise::new(&self.reflector_.global(), can_gc)
}
/// <https://streams.spec.whatwg.org/#byob-reader-release-lock>
fn ReleaseLock(&self) -> Fallible<()> {
// TODO
Err(Error::NotFound)
}
/// <https://streams.spec.whatwg.org/#generic-reader-closed>
fn Closed(&self, can_gc: CanGc) -> Rc<Promise> {
// TODO
Promise::new(&self.reflector_.global(), can_gc)
}
/// <https://streams.spec.whatwg.org/#generic-reader-cancel>
fn Cancel(&self, _cx: SafeJSContext, _reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
// TODO
Promise::new(&self.reflector_.global(), can_gc)
}
}

View file

@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::ReadableStreamBYOBRequestBinding::ReadableStreamBYOBRequestMethods;
use crate::dom::bindings::import::module::{Error, Fallible};
use crate::dom::bindings::reflector::Reflector;
use crate::script_runtime::JSContext as SafeJSContext;
/// <https://streams.spec.whatwg.org/#readablestreambyobrequest>
#[dom_struct]
pub struct ReadableStreamBYOBRequest {
reflector_: Reflector,
}
impl ReadableStreamBYOBRequestMethods<crate::DomTypeHolder> for ReadableStreamBYOBRequest {
/// <https://streams.spec.whatwg.org/#rs-byob-request-view>
fn GetView(&self, _cx: SafeJSContext) -> Option<js::typedarray::ArrayBufferView> {
// TODO
None
}
/// <https://streams.spec.whatwg.org/#rs-byob-request-respond>
fn Respond(&self, _bytes_written: u64) -> Fallible<()> {
// TODO
Err(Error::NotFound)
}
/// <https://streams.spec.whatwg.org/#rs-byob-request-respond-with-new-view>
fn RespondWithNewView(
&self,
_view: js::gc::CustomAutoRooterGuard<js::typedarray::ArrayBufferView>,
) -> Fallible<()> {
// TODO
Err(Error::NotFound)
}
}

View file

@ -0,0 +1,878 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::ptr;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::wrappers::JS_GetPendingException;
use js::rust::{HandleObject, HandleValue as SafeHandleValue, HandleValue, MutableHandleValue};
use js::typedarray::Uint8;
use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::codegen::Bindings::ReadableStreamDefaultControllerBinding::ReadableStreamDefaultControllerMethods;
use crate::dom::bindings::import::module::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller;
use crate::dom::bindings::import::module::{throw_dom_exception, Error, Fallible};
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::dom::readablestream::ReadableStream;
use crate::dom::readablestreamdefaultreader::ReadRequest;
use crate::dom::underlyingsourcecontainer::{UnderlyingSourceContainer, UnderlyingSourceType};
use crate::js::conversions::ToJSValConvertible;
use crate::realms::{enter_realm, InRealm};
use crate::script_runtime::{CanGc, JSContext, JSContext as SafeJSContext};
/// The fulfillment handler for
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[allow(crown::unrooted_must_root)]
struct PullAlgorithmFulfillmentHandler {
#[ignore_malloc_size_of = "Trusted are hard"]
controller: Trusted<ReadableStreamDefaultController>,
}
impl Callback for PullAlgorithmFulfillmentHandler {
/// Continuation of <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
/// Upon fulfillment of pullPromise
fn callback(&self, _cx: JSContext, _v: HandleValue, _realm: InRealm, can_gc: CanGc) {
let controller = self.controller.root();
// Set controller.[[pulling]] to false.
controller.pulling.set(false);
// If controller.[[pullAgain]] is true,
if controller.pull_again.get() {
// Set controller.[[pullAgain]] to false.
controller.pull_again.set(false);
// Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
controller.call_pull_if_needed(can_gc);
}
}
}
/// The rejection handler for
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[allow(crown::unrooted_must_root)]
struct PullAlgorithmRejectionHandler {
#[ignore_malloc_size_of = "Trusted are hard"]
controller: Trusted<ReadableStreamDefaultController>,
}
impl Callback for PullAlgorithmRejectionHandler {
/// Continuation of <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
/// Upon rejection of pullPromise with reason e.
fn callback(&self, _cx: JSContext, v: HandleValue, _realm: InRealm, _can_gc: CanGc) {
let controller = self.controller.root();
// Perform ! ReadableStreamDefaultControllerError(controller, e).
controller.error(v);
}
}
/// The fulfillment handler for
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-start>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[allow(crown::unrooted_must_root)]
struct StartAlgorithmFulfillmentHandler {
#[ignore_malloc_size_of = "Trusted are hard"]
controller: Trusted<ReadableStreamDefaultController>,
}
impl Callback for StartAlgorithmFulfillmentHandler {
/// Continuation of <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
/// Upon fulfillment of startPromise,
fn callback(&self, _cx: JSContext, _v: HandleValue, _realm: InRealm, can_gc: CanGc) {
let controller = self.controller.root();
// Set controller.[[started]] to true.
controller.started.set(true);
// Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
controller.call_pull_if_needed(can_gc);
}
}
/// The rejection handler for
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-start>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[allow(crown::unrooted_must_root)]
struct StartAlgorithmRejectionHandler {
#[ignore_malloc_size_of = "Trusted are hard"]
controller: Trusted<ReadableStreamDefaultController>,
}
impl Callback for StartAlgorithmRejectionHandler {
/// Continuation of <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
/// Upon rejection of startPromise with reason r,
fn callback(&self, _cx: JSContext, v: HandleValue, _realm: InRealm, _can_gc: CanGc) {
let controller = self.controller.root();
// Perform ! ReadableStreamDefaultControllerError(controller, r).
controller.error(v);
}
}
/// <https://streams.spec.whatwg.org/#value-with-size>
#[derive(JSTraceable)]
#[crown::unrooted_must_root_lint::must_root]
pub struct ValueWithSize {
value: Box<Heap<JSVal>>,
size: f64,
}
/// <https://streams.spec.whatwg.org/#value-with-size>
#[derive(JSTraceable)]
#[crown::unrooted_must_root_lint::must_root]
pub enum EnqueuedValue {
/// A value enqueued from Rust.
Native(Box<[u8]>),
/// A Js value.
Js(ValueWithSize),
}
impl EnqueuedValue {
fn size(&self) -> f64 {
match self {
EnqueuedValue::Native(v) => v.len() as f64,
EnqueuedValue::Js(v) => v.size,
}
}
#[allow(unsafe_code)]
fn to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue) {
match self {
EnqueuedValue::Native(chunk) => {
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<Uint8>(cx, chunk, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for native chunk.");
unsafe { array_buffer_ptr.to_jsval(*cx, rval) };
},
EnqueuedValue::Js(value_with_size) => unsafe {
value_with_size.value.to_jsval(*cx, rval);
},
}
}
}
/// <https://streams.spec.whatwg.org/#is-non-negative-number>
fn is_non_negative_number(value: &EnqueuedValue) -> bool {
let value_with_size = match value {
EnqueuedValue::Native(_) => return true,
EnqueuedValue::Js(value_with_size) => value_with_size,
};
// If v is not a Number, return false.
// Checked as part of the WebIDL.
// If v is NaN, return false.
if value_with_size.size.is_nan() {
return false;
}
// If v < 0, return false.
if value_with_size.size.is_sign_negative() {
return false;
}
true
}
/// <https://streams.spec.whatwg.org/#queue-with-sizes>
#[derive(Default, JSTraceable, MallocSizeOf)]
#[crown::unrooted_must_root_lint::must_root]
pub struct QueueWithSizes {
#[ignore_malloc_size_of = "EnqueuedValue::Js"]
queue: VecDeque<EnqueuedValue>,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-queuetotalsize>
total_size: f64,
}
impl QueueWithSizes {
/// <https://streams.spec.whatwg.org/#dequeue-value>
#[allow(crown::unrooted_must_root)]
fn dequeue_value(&mut self) -> EnqueuedValue {
let value = self
.queue
.pop_front()
.expect("Buffer cannot be empty when dequeue value is called into.");
self.total_size -= value.size();
value
}
/// <https://streams.spec.whatwg.org/#enqueue-value-with-size>
#[allow(crown::unrooted_must_root)]
fn enqueue_value_with_size(&mut self, value: EnqueuedValue) -> Result<(), Error> {
// If ! IsNonNegativeNumber(size) is false, throw a RangeError exception.
if !is_non_negative_number(&value) {
return Err(Error::Range(
"The size of the enqueued chunk is not a non-negative number.".to_string(),
));
}
// If size is +∞, throw a RangeError exception.
if value.size().is_infinite() {
return Err(Error::Range(
"The size of the enqueued chunk is infinite.".to_string(),
));
}
self.total_size += value.size();
self.queue.push_back(value);
Ok(())
}
fn is_empty(&self) -> bool {
self.queue.is_empty()
}
/// Only used with native sources.
fn get_in_memory_bytes(&self) -> Option<Vec<u8>> {
self.queue
.iter()
.try_fold(Vec::new(), |mut acc, value| match value {
EnqueuedValue::Native(chunk) => {
acc.extend(chunk.iter().copied());
Some(acc)
},
_ => {
warn!("get_in_memory_bytes called on a controller with non-native source.");
None
},
})
}
/// <https://streams.spec.whatwg.org/#reset-queue>
fn reset(&mut self) {
self.queue.clear();
self.total_size = Default::default();
}
}
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller>
#[dom_struct]
pub struct ReadableStreamDefaultController {
reflector_: Reflector,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-queue>
queue: RefCell<QueueWithSizes>,
/// A mutable reference to the underlying source is used to implement these two
/// internal slots:
///
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pullalgorithm>
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-cancelalgorithm>
underlying_source: MutNullableDom<UnderlyingSourceContainer>,
stream: MutNullableDom<ReadableStream>,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-strategyhwm>
strategy_hwm: f64,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-strategysizealgorithm>
#[ignore_malloc_size_of = "mozjs"]
strategy_size: RefCell<Option<Rc<QueuingStrategySize>>>,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-closerequested>
close_requested: Cell<bool>,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-started>
started: Cell<bool>,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pulling>
pulling: Cell<bool>,
/// <https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pullagain>
pull_again: Cell<bool>,
}
impl ReadableStreamDefaultController {
#[allow(crown::unrooted_must_root)]
fn new_inherited(
global: &GlobalScope,
underlying_source_type: UnderlyingSourceType,
strategy_hwm: f64,
strategy_size: Rc<QueuingStrategySize>,
can_gc: CanGc,
) -> ReadableStreamDefaultController {
ReadableStreamDefaultController {
reflector_: Reflector::new(),
queue: RefCell::new(Default::default()),
stream: MutNullableDom::new(None),
underlying_source: MutNullableDom::new(Some(&*UnderlyingSourceContainer::new(
global,
underlying_source_type,
can_gc,
))),
strategy_hwm,
strategy_size: RefCell::new(Some(strategy_size)),
close_requested: Default::default(),
started: Default::default(),
pulling: Default::default(),
pull_again: Default::default(),
}
}
#[allow(crown::unrooted_must_root)]
pub fn new(
global: &GlobalScope,
underlying_source: UnderlyingSourceType,
strategy_hwm: f64,
strategy_size: Rc<QueuingStrategySize>,
can_gc: CanGc,
) -> DomRoot<ReadableStreamDefaultController> {
reflect_dom_object(
Box::new(ReadableStreamDefaultController::new_inherited(
global,
underlying_source,
strategy_hwm,
strategy_size,
can_gc,
)),
global,
can_gc,
)
}
/// <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
#[allow(unsafe_code)]
pub fn setup(&self, stream: DomRoot<ReadableStream>, can_gc: CanGc) -> Result<(), Error> {
// Assert: stream.[[controller]] is undefined
stream.assert_no_controller();
// Set controller.[[stream]] to stream.
self.stream.set(Some(&stream));
let global = &*self.global();
let rooted_default_controller = DomRoot::from_ref(self);
// Perform ! ResetQueue(controller).
// Set controller.[[started]], controller.[[closeRequested]],
// controller.[[pullAgain]], and controller.[[pulling]] to false.
// Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm
// and controller.[[strategyHWM]] to highWaterMark.
// Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm
// and controller.[[strategyHWM]] to highWaterMark.
// Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
// Note: the above steps are done in `new`.
// Set stream.[[controller]] to controller.
stream.set_default_controller(&rooted_default_controller);
if let Some(underlying_source) = rooted_default_controller.underlying_source.get() {
// Let startResult be the result of performing startAlgorithm. (This might throw an exception.)
let start_result = underlying_source
.call_start_algorithm(
Controller::ReadableStreamDefaultController(rooted_default_controller.clone()),
can_gc,
)
.unwrap_or_else(|| {
let promise = Promise::new(global, can_gc);
promise.resolve_native(&());
Ok(promise)
});
// Let startPromise be a promise resolved with startResult.
let start_promise = start_result?;
// Upon fulfillment of startPromise,
let fulfillment_handler = Box::new(StartAlgorithmFulfillmentHandler {
controller: Trusted::new(&*rooted_default_controller),
});
// Upon rejection of startPromise with reason r,
let rejection_handler = Box::new(StartAlgorithmRejectionHandler {
controller: Trusted::new(&*rooted_default_controller),
});
let handler = PromiseNativeHandler::new(
global,
Some(fulfillment_handler),
Some(rejection_handler),
);
let realm = enter_realm(global);
let comp = InRealm::Entered(&realm);
start_promise.append_native_handler(&handler, comp, can_gc);
};
Ok(())
}
/// Setting the JS object after the heap has settled down.
pub fn set_underlying_source_this_object(&self, this_object: HandleObject) {
if let Some(underlying_source) = self.underlying_source.get() {
underlying_source.set_underlying_source_this_object(this_object);
}
}
/// <https://streams.spec.whatwg.org/#dequeue-value>
#[allow(crown::unrooted_must_root)]
fn dequeue_value(&self) -> EnqueuedValue {
let mut queue = self.queue.borrow_mut();
queue.dequeue_value()
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-should-call-pull>
fn should_call_pull(&self) -> bool {
// Let stream be controller.[[stream]].
// Note: the spec does not assert that stream is not undefined here,
// so we return false if it is.
let Some(stream) = self.stream.get() else {
debug!("`should_call_pull` called on a controller without a stream.");
return false;
};
// If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is false, return.
if !self.can_close_or_enqueue() {
return false;
}
// If controller.[[started]] is false, return false.
if !self.started.get() {
return false;
}
// If ! IsReadableStreamLocked(stream) is true
// and ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
if stream.is_locked() && stream.get_num_read_requests() > 0 {
return true;
}
// Let desiredSize be ! ReadableStreamDefaultControllerGetDesiredSize(controller).
// Assert: desiredSize is not null.
let desired_size = self.get_desired_size().expect("desiredSize is not null.");
if desired_size > 0. {
return true;
}
false
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
#[allow(unsafe_code)]
fn call_pull_if_needed(&self, can_gc: CanGc) {
if !self.should_call_pull() {
return;
}
// If controller.[[pulling]] is true,
if self.pulling.get() {
// Set controller.[[pullAgain]] to true.
self.pull_again.set(true);
return;
}
// Set controller.[[pulling]] to true.
self.pulling.set(true);
// Let pullPromise be the result of performing controller.[[pullAlgorithm]].
// Continues into the resolve and reject handling of the native handler.
let global = self.global();
let rooted_default_controller = DomRoot::from_ref(self);
let controller =
Controller::ReadableStreamDefaultController(rooted_default_controller.clone());
let Some(underlying_source) = self.underlying_source.get() else {
return;
};
let fulfillment_handler = Box::new(PullAlgorithmFulfillmentHandler {
controller: Trusted::new(&*rooted_default_controller),
});
let rejection_handler = Box::new(PullAlgorithmRejectionHandler {
controller: Trusted::new(&*rooted_default_controller),
});
let handler =
PromiseNativeHandler::new(&global, Some(fulfillment_handler), Some(rejection_handler));
let realm = enter_realm(&*global);
let comp = InRealm::Entered(&realm);
let result = underlying_source
.call_pull_algorithm(controller, can_gc)
.unwrap_or_else(|| {
let promise = Promise::new(&global, can_gc);
promise.resolve_native(&());
Ok(promise)
});
let promise = result.unwrap_or_else(|error| {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
// TODO: check if `self.global()` is the right globalscope.
unsafe {
error
.clone()
.to_jsval(*cx, &self.global(), rval.handle_mut())
};
let promise = Promise::new(&global, can_gc);
promise.reject_native(&rval.handle());
promise
});
promise.append_native_handler(&handler, comp, can_gc);
}
/// <https://streams.spec.whatwg.org/#rs-default-controller-private-cancel>
#[allow(unsafe_code)]
pub fn perform_cancel_steps(&self, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
// Perform ! ResetQueue(this).
self.queue.borrow_mut().reset();
let underlying_source = self
.underlying_source
.get()
.expect("Controller should have a source when the cancel steps are called into.");
let global = self.global();
// Let result be the result of performing this.[[cancelAlgorithm]], passing reason.
let result = underlying_source
.call_cancel_algorithm(reason, can_gc)
.unwrap_or_else(|| {
let promise = Promise::new(&global, can_gc);
promise.resolve_native(&());
Ok(promise)
});
let promise = result.unwrap_or_else(|error| {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
// TODO: check if `self.global()` is the right globalscope.
unsafe {
error
.clone()
.to_jsval(*cx, &self.global(), rval.handle_mut())
};
let promise = Promise::new(&global, can_gc);
promise.reject_native(&rval.handle());
promise
});
// Perform ! ReadableStreamDefaultControllerClearAlgorithms(this).
self.clear_algorithms();
// Return result(the promise).
promise
}
/// <https://streams.spec.whatwg.org/#rs-default-controller-private-pull>
#[allow(crown::unrooted_must_root)]
pub fn perform_pull_steps(&self, read_request: &ReadRequest, can_gc: CanGc) {
// Let stream be this.[[stream]].
// Note: the spec does not assert that there is a stream.
let Some(stream) = self.stream.get() else {
return;
};
// if queue contains bytes, perform chunk steps.
if !self.queue.borrow().is_empty() {
// Rooting: chunk will be tied to a rooted value
// before calling into a function that can GC.
let chunk = self.dequeue_value();
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
let result = RootedTraceableBox::new(Heap::default());
chunk.to_jsval(cx, rval.handle_mut());
result.set(*rval);
// If this.[[closeRequested]] is true and this.[[queue]] is empty
if self.close_requested.get() && self.queue.borrow().is_empty() {
// Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller).
self.clear_algorithms();
// Perform ! ReadableStreamClose(stream).
stream.close();
} else {
// Otherwise, perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this).
self.call_pull_if_needed(can_gc);
}
// Perform readRequests chunk steps, given chunk.
read_request.chunk_steps(result);
} else {
// Perform ! ReadableStreamAddReadRequest(stream, readRequest).
stream.add_read_request(read_request);
// Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this).
self.call_pull_if_needed(can_gc);
}
}
/// <https://streams.spec.whatwg.org/#ref-for-abstract-opdef-readablestreamcontroller-releasesteps>
pub fn perform_release_steps(&self) {
// step 1 - Return.
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-enqueue>
#[allow(unsafe_code)]
pub fn enqueue(
&self,
cx: SafeJSContext,
chunk: SafeHandleValue,
can_gc: CanGc,
) -> Result<(), Error> {
// If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is false, return.
if !self.can_close_or_enqueue() {
return Ok(());
}
let stream = self
.stream
.get()
.expect("Controller must have a stream when a chunk is enqueued.");
// If ! IsReadableStreamLocked(stream) is true
// and ! ReadableStreamGetNumReadRequests(stream) > 0,
// perform ! ReadableStreamFulfillReadRequest(stream, chunk, false).
if stream.is_locked() && stream.get_num_read_requests() > 0 {
stream.fulfill_read_request(chunk, false);
} else {
// Otherwise,
// Let result be the result of performing controller.[[strategySizeAlgorithm]],
// passing in chunk, and interpreting the result as a completion record.
// Note: the clone is necessary to prevent potential re-borrow panics.
let strategy_size = {
let reference = self.strategy_size.borrow();
reference.clone()
};
let size = if let Some(strategy_size) = strategy_size {
// Note: the Rethrow exception handling is necessary,
// otherwise returning JSFailed will panic because no exception is pending.
let result = strategy_size.Call__(chunk, ExceptionHandling::Rethrow);
match result {
// Let chunkSize be result.[[Value]].
Ok(size) => size,
Err(error) => {
// If result is an abrupt completion,
rooted!(in(*cx) let mut rval = UndefinedValue());
unsafe { assert!(JS_GetPendingException(*cx, rval.handle_mut())) };
// Perform ! ReadableStreamDefaultControllerError(controller, result.[[Value]]).
self.error(rval.handle());
// Return result.
// Note: we need to return a type error, because no exception is pending.
return Err(error);
},
}
} else {
0.
};
{
// Let enqueueResult be EnqueueValueWithSize(controller, chunk, chunkSize).
let res = {
let mut queue = self.queue.borrow_mut();
queue.enqueue_value_with_size(EnqueuedValue::Js(ValueWithSize {
value: Heap::boxed(chunk.get()),
size,
}))
};
if let Err(error) = res {
// If enqueueResult is an abrupt completion,
// First, throw the exception.
// Note: this must be done manually here,
// because `enqueue_value_with_size` does not call into JS.
throw_dom_exception(cx, &self.global(), error);
// Then, get a handle to the JS val for the exception,
// and use that to error the stream.
rooted!(in(*cx) let mut rval = UndefinedValue());
unsafe { assert!(JS_GetPendingException(*cx, rval.handle_mut())) };
// Perform ! ReadableStreamDefaultControllerError(controller, enqueueResult.[[Value]]).
self.error(rval.handle());
// Return enqueueResult.
// Note: because we threw the exception above,
// there is a pending exception and we can return JSFailed.
return Err(Error::JSFailed);
}
}
}
// Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
self.call_pull_if_needed(can_gc);
Ok(())
}
/// Native call to
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-enqueue>
#[allow(crown::unrooted_must_root)]
pub fn enqueue_native(&self, chunk: Vec<u8>) {
let stream = self
.stream
.get()
.expect("Controller must have a stream when a chunk is enqueued.");
if stream.is_locked() && stream.get_num_read_requests() > 0 {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
let enqueued_chunk = EnqueuedValue::Native(chunk.into_boxed_slice());
enqueued_chunk.to_jsval(cx, rval.handle_mut());
stream.fulfill_read_request(rval.handle(), false);
} else {
let mut queue = self.queue.borrow_mut();
queue
.enqueue_value_with_size(EnqueuedValue::Native(chunk.into_boxed_slice()))
.expect("Enqueuing a chunk from Rust should not fail.");
}
}
/// Does the stream have all data in memory?
pub fn in_memory(&self) -> bool {
let Some(underlying_source) = self.underlying_source.get() else {
return false;
};
underlying_source.in_memory()
}
/// Return bytes synchronously if the stream has all data in memory.
pub fn get_in_memory_bytes(&self) -> Option<Vec<u8>> {
let underlying_source = self.underlying_source.get()?;
if underlying_source.in_memory() {
return self.queue.borrow().get_in_memory_bytes();
}
None
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-clear-algorithms>
fn clear_algorithms(&self) {
// Set controller.[[pullAlgorithm]] to undefined.
// Set controller.[[cancelAlgorithm]] to undefined.
self.underlying_source.set(None);
// Set controller.[[strategySizeAlgorithm]] to undefined.
*self.strategy_size.borrow_mut() = None;
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-close>
pub fn close(&self) {
// If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is false, return.
if !self.can_close_or_enqueue() {
return;
}
let Some(stream) = self.stream.get() else {
return;
};
// Set controller.[[closeRequested]] to true.
self.close_requested.set(true);
if self.queue.borrow().is_empty() {
// Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller).
self.clear_algorithms();
// Perform ! ReadableStreamClose(stream).
stream.close();
}
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-get-desired-size>
fn get_desired_size(&self) -> Option<f64> {
let stream = self.stream.get()?;
// If state is "errored", return null.
if stream.is_errored() {
return None;
}
// If state is "closed", return 0.
if stream.is_closed() {
return Some(0.0);
}
// Return controller.[[strategyHWM]] controller.[[queueTotalSize]].
let queue = self.queue.borrow();
let desired_size = self.strategy_hwm - queue.total_size.clamp(0.0, f64::MAX);
Some(desired_size.clamp(desired_size, self.strategy_hwm))
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue>
fn can_close_or_enqueue(&self) -> bool {
let Some(stream) = self.stream.get() else {
return false;
};
// If controller.[[closeRequested]] is false and state is "readable", return true.
if !self.close_requested.get() && stream.is_readable() {
return true;
}
// Otherwise, return false.
false
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-error>
pub fn error(&self, e: SafeHandleValue) {
let Some(stream) = self.stream.get() else {
return;
};
// If stream.[[state]] is not "readable", return.
if !stream.is_readable() {
return;
}
// Perform ! ResetQueue(controller).
self.queue.borrow_mut().reset();
// Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller).
self.clear_algorithms();
stream.error(e);
}
}
impl ReadableStreamDefaultControllerMethods<crate::DomTypeHolder>
for ReadableStreamDefaultController
{
/// <https://streams.spec.whatwg.org/#rs-default-controller-desired-size>
fn GetDesiredSize(&self) -> Option<f64> {
self.get_desired_size()
}
/// <https://streams.spec.whatwg.org/#rs-default-controller-close>
fn Close(&self) -> Fallible<()> {
if !self.can_close_or_enqueue() {
// If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is false,
// throw a TypeError exception.
return Err(Error::Type("Stream cannot be closed.".to_string()));
}
// Perform ! ReadableStreamDefaultControllerClose(this).
self.close();
Ok(())
}
/// <https://streams.spec.whatwg.org/#rs-default-controller-enqueue>
fn Enqueue(&self, cx: SafeJSContext, chunk: SafeHandleValue, can_gc: CanGc) -> Fallible<()> {
// If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is false, throw a TypeError exception.
if !self.can_close_or_enqueue() {
return Err(Error::Type("Stream cannot be enqueued to.".to_string()));
}
// Perform ? ReadableStreamDefaultControllerEnqueue(this, chunk).
self.enqueue(cx, chunk, can_gc)
}
/// <https://streams.spec.whatwg.org/#rs-default-controller-error>
fn Error(&self, _cx: SafeJSContext, e: SafeHandleValue) -> Fallible<()> {
self.error(e);
Ok(())
}
}

View file

@ -0,0 +1,530 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::collections::VecDeque;
use std::mem;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue};
use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
use super::bindings::root::MutNullableDom;
use super::types::ReadableStreamDefaultController;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::ReadableStreamDefaultReaderBinding::{
ReadableStreamDefaultReaderMethods, ReadableStreamReadResult,
};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::import::module::Fallible;
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::dom::readablestream::ReadableStream;
use crate::realms::{enter_realm, InRealm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// <https://streams.spec.whatwg.org/#read-request>
#[derive(Clone, JSTraceable)]
#[crown::unrooted_must_root_lint::must_root]
pub enum ReadRequest {
/// <https://streams.spec.whatwg.org/#default-reader-read>
Read(Rc<Promise>),
/// <https://streams.spec.whatwg.org/#ref-for-read-request%E2%91%A2>
DefaultTee {
tee_read_request: Dom<DefaultTeeReadRequest>,
},
}
impl ReadRequest {
/// <https://streams.spec.whatwg.org/#read-request-chunk-steps>
pub fn chunk_steps(&self, chunk: RootedTraceableBox<Heap<JSVal>>) {
match self {
ReadRequest::Read(promise) => {
promise.resolve_native(&ReadableStreamReadResult {
done: Some(false),
value: chunk,
});
},
ReadRequest::DefaultTee { tee_read_request } => {
tee_read_request.enqueue_chunk_steps(chunk);
},
}
}
/// <https://streams.spec.whatwg.org/#read-request-close-steps>
pub fn close_steps(&self) {
match self {
ReadRequest::Read(promise) => {
let result = RootedTraceableBox::new(Heap::default());
result.set(UndefinedValue());
promise.resolve_native(&ReadableStreamReadResult {
done: Some(true),
value: result,
});
},
ReadRequest::DefaultTee { tee_read_request } => {
tee_read_request.close_steps();
},
}
}
/// <https://streams.spec.whatwg.org/#read-request-error-steps>
pub fn error_steps(&self, e: SafeHandleValue) {
match self {
ReadRequest::Read(promise) => promise.reject_native(&e),
ReadRequest::DefaultTee { tee_read_request } => {
tee_read_request.error_steps();
},
}
}
}
/// The rejection handler for
/// <https://streams.spec.whatwg.org/#readable-stream-tee>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[crown::unrooted_must_root_lint::must_root]
struct ClosedPromiseRejectionHandler {
branch_1_controller: Dom<ReadableStreamDefaultController>,
branch_2_controller: Dom<ReadableStreamDefaultController>,
#[ignore_malloc_size_of = "Rc"]
canceled_1: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
cancel_promise: Rc<Promise>,
}
impl Callback for ClosedPromiseRejectionHandler {
/// Continuation of <https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed>
/// Upon rejection of reader.[[closedPromise]] with reason r,
fn callback(&self, _cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, _can_gc: CanGc) {
let branch_1_controller = &self.branch_1_controller;
let branch_2_controller = &self.branch_2_controller;
// Perform ! ReadableStreamDefaultControllerError(branch_1.[[controller]], r).
branch_1_controller.error(v);
// Perform ! ReadableStreamDefaultControllerError(branch_2.[[controller]], r).
branch_2_controller.error(v);
// If canceled_1 is false or canceled_2 is false, resolve cancelPromise with undefined.
if !self.canceled_1.get() || !self.canceled_2.get() {
self.cancel_promise.resolve_native(&());
}
}
}
/// <https://streams.spec.whatwg.org/#readablestreamdefaultreader>
#[dom_struct]
pub struct ReadableStreamDefaultReader {
reflector_: Reflector,
/// <https://streams.spec.whatwg.org/#readablestreamgenericreader-stream>
stream: MutNullableDom<ReadableStream>,
#[ignore_malloc_size_of = "no VecDeque support"]
read_requests: DomRefCell<VecDeque<ReadRequest>>,
/// <https://streams.spec.whatwg.org/#readablestreamgenericreader-closedpromise>
#[ignore_malloc_size_of = "Rc is hard"]
closed_promise: DomRefCell<Rc<Promise>>,
}
impl ReadableStreamDefaultReader {
/// <https://streams.spec.whatwg.org/#default-reader-constructor>
#[allow(non_snake_case)]
pub fn Constructor(
global: &GlobalScope,
proto: Option<SafeHandleObject>,
can_gc: CanGc,
stream: &ReadableStream,
) -> Fallible<DomRoot<Self>> {
let reader = Self::new_with_proto(global, proto, can_gc);
// Perform ? SetUpReadableStreamDefaultReader(this, stream).
Self::set_up(&reader, stream, global, can_gc)?;
Ok(reader)
}
fn new_with_proto(
global: &GlobalScope,
proto: Option<SafeHandleObject>,
can_gc: CanGc,
) -> DomRoot<ReadableStreamDefaultReader> {
reflect_dom_object_with_proto(
Box::new(ReadableStreamDefaultReader::new_inherited(global, can_gc)),
global,
proto,
can_gc,
)
}
pub fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> ReadableStreamDefaultReader {
ReadableStreamDefaultReader {
reflector_: Reflector::new(),
stream: MutNullableDom::new(None),
read_requests: DomRefCell::new(Default::default()),
closed_promise: DomRefCell::new(Promise::new(global, can_gc)),
}
}
/// <https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader>
pub fn set_up(
&self,
stream: &ReadableStream,
global: &GlobalScope,
can_gc: CanGc,
) -> Fallible<()> {
// If ! IsReadableStreamLocked(stream) is true, throw a TypeError exception.
if stream.is_locked() {
return Err(Error::Type("stream is locked".to_owned()));
}
// Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
self.generic_initialize(global, stream, can_gc)?;
// Set reader.[[readRequests]] to a new empty list.
self.read_requests.borrow_mut().clear();
Ok(())
}
/// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize>
pub fn generic_initialize(
&self,
global: &GlobalScope,
stream: &ReadableStream,
can_gc: CanGc,
) -> Fallible<()> {
// Set reader.[[stream]] to stream.
self.stream.set(Some(stream));
// Set stream.[[reader]] to reader.
stream.set_reader(Some(self));
if stream.is_readable() {
// If stream.[[state]] is "readable
// Set reader.[[closedPromise]] to a new promise.
*self.closed_promise.borrow_mut() = Promise::new(global, can_gc);
} else if stream.is_closed() {
// Otherwise, if stream.[[state]] is "closed",
// Set reader.[[closedPromise]] to a promise resolved with undefined.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
*self.closed_promise.borrow_mut() = Promise::new_resolved(global, cx, rval.handle())?;
} else {
// Assert: stream.[[state]] is "errored"
assert!(stream.is_errored());
// Set reader.[[closedPromise]] to a promise rejected with stream.[[storedError]].
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue());
stream.get_stored_error(error.handle_mut());
*self.closed_promise.borrow_mut() = Promise::new_rejected(global, cx, error.handle())?;
// Set reader.[[closedPromise]].[[PromiseIsHandled]] to true
self.closed_promise.borrow().set_promise_is_handled();
}
Ok(())
}
/// <https://streams.spec.whatwg.org/#readable-stream-close>
#[allow(crown::unrooted_must_root)]
pub fn close(&self) {
// Resolve reader.[[closedPromise]] with undefined.
self.closed_promise.borrow().resolve_native(&());
// If reader implements ReadableStreamDefaultReader,
// Let readRequests be reader.[[readRequests]].
let mut read_requests = self.take_read_requests();
// Set reader.[[readRequests]] to an empty list.
// For each readRequest of readRequests,
for request in read_requests.drain(0..) {
// Perform readRequests close steps.
request.close_steps();
}
}
/// <https://streams.spec.whatwg.org/#readable-stream-add-read-request>
pub fn add_read_request(&self, read_request: &ReadRequest) {
self.read_requests
.borrow_mut()
.push_back(read_request.clone());
}
/// <https://streams.spec.whatwg.org/#readable-stream-get-num-read-requests>
pub fn get_num_read_requests(&self) -> usize {
self.read_requests.borrow().len()
}
/// <https://streams.spec.whatwg.org/#readable-stream-error>
pub fn error(&self, e: SafeHandleValue) {
// Reject reader.[[closedPromise]] with e.
self.closed_promise.borrow().reject_native(&e);
// Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
self.closed_promise.borrow().set_promise_is_handled();
// Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
self.error_read_requests(e);
}
/// The removal steps of <https://streams.spec.whatwg.org/#readable-stream-fulfill-read-request>
#[allow(crown::unrooted_must_root)]
pub fn remove_read_request(&self) -> ReadRequest {
self.read_requests
.borrow_mut()
.pop_front()
.expect("Reader must have read request when remove is called into.")
}
/// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-release>
#[allow(unsafe_code)]
pub fn generic_release(&self) {
// Let stream be reader.[[stream]].
// Assert: stream is not undefined.
assert!(self.stream.get().is_some());
if let Some(stream) = self.stream.get() {
// Assert: stream.[[reader]] is reader.
assert!(stream.has_default_reader());
if stream.is_readable() {
// If stream.[[state]] is "readable", reject reader.[[closedPromise]] with a TypeError exception.
self.closed_promise
.borrow()
.reject_error(Error::Type("stream state is not readable".to_owned()));
} else {
// Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("Cannot release lock due to stream state.".to_owned()).to_jsval(
*cx,
&self.global(),
error.handle_mut(),
)
};
*self.closed_promise.borrow_mut() =
Promise::new_rejected(&self.global(), cx, error.handle()).unwrap();
}
// Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
self.closed_promise.borrow().set_promise_is_handled();
// Perform ! stream.[[controller]].[[ReleaseSteps]]().
stream.perform_release_steps();
// Set stream.[[reader]] to undefined.
stream.set_reader(None);
// Set reader.[[stream]] to undefined.
self.stream.set(None);
}
}
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease>
#[allow(unsafe_code)]
pub fn release(&self) {
// Perform ! ReadableStreamReaderGenericRelease(reader).
self.generic_release();
// Let e be a new TypeError exception.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("Reader is released".to_owned()).to_jsval(
*cx,
&self.global(),
error.handle_mut(),
)
};
// Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
self.error_read_requests(error.handle());
}
#[allow(crown::unrooted_must_root)]
fn take_read_requests(&self) -> VecDeque<ReadRequest> {
mem::take(&mut *self.read_requests.borrow_mut())
}
/// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel>
fn generic_cancel(&self, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
// Let stream be reader.[[stream]].
let stream = self.stream.get();
// Assert: stream is not undefined.
let stream =
stream.expect("Reader should have a stream when generic cancel is called into.");
// Return ! ReadableStreamCancel(stream, reason).
stream.cancel(reason, can_gc)
}
/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreadererrorreadrequests>
#[allow(crown::unrooted_must_root)]
fn error_read_requests(&self, rval: SafeHandleValue) {
// step 1
let mut read_requests = self.take_read_requests();
// step 2 & 3
for request in read_requests.drain(0..) {
request.error_steps(rval);
}
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-reader-read>
pub fn read(&self, read_request: &ReadRequest, can_gc: CanGc) {
// Let stream be reader.[[stream]].
// Assert: stream is not undefined.
assert!(self.stream.get().is_some());
let stream = self.stream.get().unwrap();
// Set stream.[[disturbed]] to true.
stream.set_is_disturbed(true);
// If stream.[[state]] is "closed", perform readRequests close steps.
if stream.is_closed() {
read_request.close_steps();
} else if stream.is_errored() {
// Otherwise, if stream.[[state]] is "errored",
// perform readRequests error steps given stream.[[storedError]].
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue());
stream.get_stored_error(error.handle_mut());
read_request.error_steps(error.handle());
} else {
// Otherwise
// Assert: stream.[[state]] is "readable".
assert!(stream.is_readable());
// Perform ! stream.[[controller]].[[PullSteps]](readRequest).
stream.perform_pull_steps(read_request, can_gc);
}
}
/// <https://streams.spec.whatwg.org/#ref-for-readablestreamgenericreader-closedpromise%E2%91%A1>
pub fn append_native_handler_to_closed_promise(
&self,
branch_1: &ReadableStream,
branch_2: &ReadableStream,
canceled_1: Rc<Cell<bool>>,
canceled_2: Rc<Cell<bool>>,
cancel_promise: Rc<Promise>,
can_gc: CanGc,
) {
let branch_1_controller = branch_1.get_default_controller();
let branch_2_controller = branch_2.get_default_controller();
let global = self.global();
let handler = PromiseNativeHandler::new(
&global,
None,
Some(Box::new(ClosedPromiseRejectionHandler {
branch_1_controller: Dom::from_ref(&branch_1_controller),
branch_2_controller: Dom::from_ref(&branch_2_controller),
canceled_1,
canceled_2,
cancel_promise,
})),
);
let realm = enter_realm(&*global);
let comp = InRealm::Entered(&realm);
self.closed_promise
.borrow()
.append_native_handler(&handler, comp, can_gc);
}
}
impl ReadableStreamDefaultReaderMethods<crate::DomTypeHolder> for ReadableStreamDefaultReader {
/// <https://streams.spec.whatwg.org/#default-reader-constructor>
fn Constructor(
global: &GlobalScope,
proto: Option<SafeHandleObject>,
can_gc: CanGc,
stream: &ReadableStream,
) -> Fallible<DomRoot<Self>> {
ReadableStreamDefaultReader::Constructor(global, proto, can_gc, stream)
}
/// <https://streams.spec.whatwg.org/#default-reader-read>
#[allow(unsafe_code)]
#[allow(crown::unrooted_must_root)]
fn Read(&self, can_gc: CanGc) -> Rc<Promise> {
// If this.[[stream]] is undefined, return a promise rejected with a TypeError exception.
if self.stream.get().is_none() {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut error = UndefinedValue());
unsafe {
Error::Type("stream is undefined".to_owned()).to_jsval(
*cx,
&self.global(),
error.handle_mut(),
)
};
return Promise::new_rejected(&self.global(), cx, error.handle()).unwrap();
}
// Let promise be a new promise.
let promise = Promise::new(&self.reflector_.global(), can_gc);
// Let readRequest be a new read request with the following items:
// chunk steps, given chunk
// Resolve promise with «[ "value" → chunk, "done" → false ]».
//
// close steps
// Resolve promise with «[ "value" → undefined, "done" → true ]».
//
// error steps, given e
// Reject promise with e.
// Rooting(unrooted_must_root): the read request contains only a promise,
// which does not need to be rooted,
// as it is safely managed natively via an Rc.
let read_request = ReadRequest::Read(promise.clone());
// Perform ! ReadableStreamDefaultReaderRead(this, readRequest).
self.read(&read_request, can_gc);
// Return promise.
promise
}
/// <https://streams.spec.whatwg.org/#default-reader-release-lock>
fn ReleaseLock(&self) {
if self.stream.get().is_some() {
// step 2 - Perform ! ReadableStreamDefaultReaderRelease(this).
self.release();
}
// step 1 - If this.[[stream]] is undefined, return.
}
/// <https://streams.spec.whatwg.org/#generic-reader-closed>
fn Closed(&self) -> Rc<Promise> {
self.closed_promise.borrow().clone()
}
/// <https://streams.spec.whatwg.org/#generic-reader-cancel>
fn Cancel(&self, _cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
if self.stream.get().is_none() {
// If this.[[stream]] is undefined,
// return a promise rejected with a TypeError exception.
let promise = Promise::new(&self.reflector_.global(), can_gc);
promise.reject_error(Error::Type("stream is undefined".to_owned()));
promise
} else {
// Return ! ReadableStreamReaderGenericCancel(this, reason).
self.generic_cancel(reason, can_gc)
}
}
}

View file

@ -30,7 +30,8 @@ use crate::dom::bindings::str::{ByteString, USVString};
use crate::dom::globalscope::GlobalScope;
use crate::dom::headers::{is_obs_text, is_vchar, Guard, Headers};
use crate::dom::promise::Promise;
use crate::dom::readablestream::{ExternalUnderlyingSource, ReadableStream};
use crate::dom::readablestream::ReadableStream;
use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext, StreamConsumer};
#[dom_struct]
@ -53,10 +54,11 @@ pub struct Response {
#[allow(non_snake_case)]
impl Response {
pub fn new_inherited(global: &GlobalScope) -> Response {
pub fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> Response {
let stream = ReadableStream::new_with_external_underlying_source(
global,
ExternalUnderlyingSource::FetchResponse,
UnderlyingSourceType::FetchResponse,
can_gc,
);
Response {
reflector_: Reflector::new(),
@ -82,7 +84,7 @@ impl Response {
can_gc: CanGc,
) -> DomRoot<Response> {
reflect_dom_object_with_proto(
Box::new(Response::new_inherited(global)),
Box::new(Response::new_inherited(global, can_gc)),
global,
proto,
can_gc,
@ -444,19 +446,19 @@ impl Response {
*self.stream_consumer.borrow_mut() = sc;
}
pub fn stream_chunk(&self, chunk: Vec<u8>, can_gc: CanGc) {
pub fn stream_chunk(&self, chunk: Vec<u8>) {
// Note, are these two actually mutually exclusive?
if let Some(stream_consumer) = self.stream_consumer.borrow().as_ref() {
stream_consumer.consume_chunk(chunk.as_slice());
} else if let Some(body) = self.body_stream.get() {
body.enqueue_native(chunk, can_gc);
body.enqueue_native(chunk);
}
}
#[allow(crown::unrooted_must_root)]
pub fn finish(&self) {
if let Some(body) = self.body_stream.get() {
body.close_native();
body.controller_close_native();
}
let stream_consumer = self.stream_consumer.borrow_mut().take();
if let Some(stream_consumer) = stream_consumer {

View file

@ -0,0 +1,225 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::ptr;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::{Heap, IsPromiseObject, JSObject};
use js::jsval::JSVal;
use js::rust::{Handle as SafeHandle, HandleObject, HandleValue as SafeHandleValue, IntoHandle};
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource;
use crate::dom::bindings::import::module::Error;
use crate::dom::bindings::import::module::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller;
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::script_runtime::CanGc;
/// <https://streams.spec.whatwg.org/#underlying-source-api>
/// The `Js` variant corresponds to
/// the JavaScript object representing the underlying source.
/// The other variants are native sources in Rust.
#[derive(JSTraceable)]
#[crown::unrooted_must_root_lint::must_root]
pub enum UnderlyingSourceType {
/// Facilitate partial integration with sources
/// that are currently read into memory.
Memory(usize),
/// A blob as underlying source, with a known total size.
Blob(usize),
/// A fetch response as underlying source.
FetchResponse,
/// A struct representing a JS object as underlying source,
/// and the actual JS object for use as `thisArg` in callbacks.
Js(JsUnderlyingSource, Heap<*mut JSObject>),
/// Tee
Tee(Dom<DefaultTeeUnderlyingSource>),
}
impl UnderlyingSourceType {
/// Is the source backed by a Rust native source?
pub fn is_native(&self) -> bool {
matches!(
self,
UnderlyingSourceType::Memory(_) |
UnderlyingSourceType::Blob(_) |
UnderlyingSourceType::FetchResponse
)
}
/// Does the source have all data in memory?
pub fn in_memory(&self) -> bool {
matches!(self, UnderlyingSourceType::Memory(_))
}
}
/// Wrapper around the underlying source.
#[dom_struct]
pub struct UnderlyingSourceContainer {
reflector_: Reflector,
#[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."]
underlying_source_type: UnderlyingSourceType,
}
impl UnderlyingSourceContainer {
#[allow(crown::unrooted_must_root)]
fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer {
UnderlyingSourceContainer {
reflector_: Reflector::new(),
underlying_source_type,
}
}
#[allow(crown::unrooted_must_root)]
pub fn new(
global: &GlobalScope,
underlying_source_type: UnderlyingSourceType,
can_gc: CanGc,
) -> DomRoot<UnderlyingSourceContainer> {
// TODO: setting the underlying source dict as the prototype of the
// `UnderlyingSourceContainer`, as it is later used as the "this" in Call_.
// Is this a good idea?
reflect_dom_object_with_proto(
Box::new(UnderlyingSourceContainer::new_inherited(
underlying_source_type,
)),
global,
None,
can_gc,
)
}
/// Setting the JS object after the heap has settled down.
pub fn set_underlying_source_this_object(&self, object: HandleObject) {
if let UnderlyingSourceType::Js(_source, this_obj) = &self.underlying_source_type {
this_obj.set(*object);
}
}
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-cancel>
#[allow(unsafe_code)]
pub fn call_cancel_algorithm(
&self,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
match &self.underlying_source_type {
UnderlyingSourceType::Js(source, this_obj) => {
if let Some(algo) = &source.cancel {
let result = unsafe {
algo.Call_(
&SafeHandle::from_raw(this_obj.handle()),
Some(reason),
ExceptionHandling::Rethrow,
)
};
return Some(result);
}
None
},
UnderlyingSourceType::Tee(tee_underlyin_source) => {
// Call the cancel algorithm for the appropriate branch.
tee_underlyin_source.cancel_algorithm(reason, can_gc)
},
_ => None,
}
}
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-pull>
#[allow(unsafe_code)]
pub fn call_pull_algorithm(
&self,
controller: Controller,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
match &self.underlying_source_type {
UnderlyingSourceType::Js(source, this_obj) => {
if let Some(algo) = &source.pull {
let result = unsafe {
algo.Call_(
&SafeHandle::from_raw(this_obj.handle()),
controller,
ExceptionHandling::Rethrow,
)
};
return Some(result);
}
None
},
UnderlyingSourceType::Tee(tee_underlyin_source) => {
// Call the pull algorithm for the appropriate branch.
tee_underlyin_source.pull_algorithm(can_gc)
},
// Note: other source type have no pull steps for now.
_ => None,
}
}
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-start>
///
/// Note: The algorithm can return any value, including a promise,
/// we always transform the result into a promise for convenience,
/// and it is also how to spec deals with the situation.
/// see "Let startPromise be a promise resolved with startResult."
/// at <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
#[allow(unsafe_code)]
pub fn call_start_algorithm(
&self,
controller: Controller,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
match &self.underlying_source_type {
UnderlyingSourceType::Js(source, this_obj) => {
if let Some(start) = &source.start {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
rooted!(in(*cx) let mut result: JSVal);
unsafe {
if let Err(error) = start.Call_(
&SafeHandle::from_raw(this_obj.handle()),
controller,
result.handle_mut(),
ExceptionHandling::Rethrow,
) {
return Some(Err(error));
}
}
let is_promise = unsafe {
if result.is_object() {
result_object.set(result.to_object());
IsPromiseObject(result_object.handle().into_handle())
} else {
false
}
};
let promise = if is_promise {
let promise = Promise::new_with_js_promise(result_object.handle(), cx);
promise
} else {
let promise = Promise::new(&self.global(), can_gc);
promise.resolve_native(&result.get());
promise
};
return Some(Ok(promise));
}
None
},
UnderlyingSourceType::Tee(_) => {
// Let startAlgorithm be an algorithm that returns undefined.
None
},
_ => None,
}
}
/// Does the source have all data in memory?
pub fn in_memory(&self) -> bool {
self.underlying_source_type.in_memory()
}
}

View file

@ -0,0 +1,34 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#qs
dictionary QueuingStrategy {
unrestricted double highWaterMark;
QueuingStrategySize size;
};
callback QueuingStrategySize = unrestricted double (any chunk);
dictionary QueuingStrategyInit {
required unrestricted double highWaterMark;
};
[Exposed=*]
interface ByteLengthQueuingStrategy {
constructor(QueuingStrategyInit init);
readonly attribute unrestricted double highWaterMark;
[Throws]
readonly attribute Function size;
};
[Exposed=*]
interface CountQueuingStrategy {
constructor(QueuingStrategyInit init);
readonly attribute unrestricted double highWaterMark;
[Throws]
readonly attribute Function size;
};

View file

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#rbs-controller-class-definition
[Exposed=*]
interface ReadableByteStreamController {
[Throws] // Throws on OOM
readonly attribute ReadableStreamBYOBRequest? byobRequest;
readonly attribute unrestricted double? desiredSize;
[Throws]
undefined close();
[Throws]
undefined enqueue(ArrayBufferView chunk);
[Throws]
undefined error(optional any e);
};

View file

@ -2,10 +2,59 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
// https://streams.spec.whatwg.org/#readablestream
[LegacyNoInterfaceObject, Exposed=(Window,Worker)]
// Need to escape "ReadableStream" so it's treated as an identifier.
[Exposed=*] // [Transferable] - See Bug 1562065
interface _ReadableStream {
[Throws]
constructor(optional object underlyingSource, optional QueuingStrategy strategy = {});
// [Throws]
// static ReadableStream from(any asyncIterable);
readonly attribute boolean locked;
[NewObject]
Promise<undefined> cancel(optional any reason);
[Throws]
ReadableStreamReader getReader(optional ReadableStreamGetReaderOptions options = {});
// [Throws]
// ReadableStream pipeThrough(ReadableWritablePair transform, optional StreamPipeOptions options = {});
// [NewObject]
// Promise<undefined> pipeTo(WritableStream destination, optional StreamPipeOptions options = {});
[Throws]
sequence<ReadableStream> tee();
// [GenerateReturnMethod]
// async iterable<any>(optional ReadableStreamIteratorOptions options = {});
};
enum ReadableStreamType { "bytes" };
enum ReadableStreamReaderMode { "byob" };
dictionary ReadableStreamGetReaderOptions {
ReadableStreamReaderMode mode;
};
/*
dictionary ReadableStreamIteratorOptions {
boolean preventCancel = false;
};
dictionary ReadableWritablePair {
required ReadableStream readable;
required WritableStream writable;
};
dictionary StreamPipeOptions {
boolean preventClose = false;
boolean preventAbort = false;
boolean preventCancel = false;
AbortSignal signal;
};
*/

View file

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#byob-reader-class-definition
[Exposed=*]
interface ReadableStreamBYOBReader {
[Throws]
constructor(ReadableStream stream);
[NewObject]
Promise<ReadableStreamReadResult> read(ArrayBufferView view);
[Throws]
undefined releaseLock();
};
ReadableStreamBYOBReader includes ReadableStreamGenericReader;

View file

@ -0,0 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#rs-byob-request-class-definition
[Exposed=*]
interface ReadableStreamBYOBRequest {
readonly attribute ArrayBufferView? view;
[Throws]
undefined respond([EnforceRange] unsigned long long bytesWritten);
[Throws]
undefined respondWithNewView(ArrayBufferView view);
};

View file

@ -0,0 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#rs-default-controller-class-definition
[Exposed=*]
interface ReadableStreamDefaultController {
readonly attribute unrestricted double? desiredSize;
[Throws]
undefined close();
[Throws]
undefined enqueue(optional any chunk);
[Throws]
undefined error(optional any e);
};

View file

@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#generic-reader-mixin-definition
// https://streams.spec.whatwg.org/#default-reader-class-definition
typedef (ReadableStreamDefaultReader or ReadableStreamBYOBReader) ReadableStreamReader;
interface mixin ReadableStreamGenericReader {
readonly attribute Promise<undefined> closed;
[NewObject]
Promise<undefined> cancel(optional any reason);
};
[Exposed=*]
interface ReadableStreamDefaultReader {
[Throws]
constructor(ReadableStream stream);
[NewObject]
Promise<ReadableStreamReadResult> read();
undefined releaseLock();
};
ReadableStreamDefaultReader includes ReadableStreamGenericReader;
dictionary ReadableStreamReadResult {
any value;
boolean done;
};
// The DefaultTeeReadRequest interface is entirely internal to Servo, and should not be accessible to
// web pages.
[LegacyNoInterfaceObject, Exposed=(Window,Worker)]
// Need to escape "DefaultTeeReadRequest" so it's treated as an identifier.
interface _DefaultTeeReadRequest {
};
// The DefaultTeeUnderlyingSource interface is entirely internal to Servo, and should not be accessible to
// web pages.
[LegacyNoInterfaceObject, Exposed=(Window,Worker)]
// Need to escape "DefaultTeeUnderlyingSource" so it's treated as an identifier.
interface _DefaultTeeUnderlyingSource {
};

View file

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://streams.spec.whatwg.org/#underlying-source-api
[GenerateInit]
dictionary UnderlyingSource {
UnderlyingSourceStartCallback start;
UnderlyingSourcePullCallback pull;
UnderlyingSourceCancelCallback cancel;
ReadableStreamType type;
[EnforceRange] unsigned long long autoAllocateChunkSize;
};
typedef (ReadableStreamDefaultController or ReadableByteStreamController) ReadableStreamController;
callback UnderlyingSourceStartCallback = any (ReadableStreamController controller);
callback UnderlyingSourcePullCallback = Promise<undefined> (ReadableStreamController controller);
callback UnderlyingSourceCancelCallback = Promise<undefined> (optional any reason);

View file

@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
[LegacyNoInterfaceObject, Exposed=(Window,Worker)]
// Need to escape "ReadableStream" so it's treated as an identifier.
interface _UnderlyingSourceContainer {
};

View file

@ -279,7 +279,7 @@ impl FetchResponseListener for FetchContext {
fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
let response = self.response_object.root();
response.stream_chunk(chunk, CanGc::note());
response.stream_chunk(chunk);
}
fn process_response_eof(

View file

@ -18,6 +18,7 @@ use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
use crate::dom::bindings::root::DomRoot;
use crate::dom::defaultteereadrequest::DefaultTeeReadRequestMicrotask;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlimageelement::ImageElementMicrotask;
use crate::dom::htmlmediaelement::MediaElementMicrotask;
@ -41,6 +42,7 @@ pub enum Microtask {
User(UserMicrotask),
MediaElement(MediaElementMicrotask),
ImageElement(ImageElementMicrotask),
ReadableStreamTeeReadRequest(DefaultTeeReadRequestMicrotask),
CustomElementReaction,
NotifyMutationObservers,
}
@ -140,6 +142,9 @@ impl MicrotaskQueue {
Microtask::NotifyMutationObservers => {
MutationObserver::notify_mutation_observers();
},
Microtask::ReadableStreamTeeReadRequest(ref task) => {
task.microtask_chunk_steps(can_gc)
},
}
}
}

View file

@ -89,6 +89,7 @@ WEBIDL_STANDARDS = [
b"//encoding.spec.whatwg.org",
b"//fetch.spec.whatwg.org",
b"//html.spec.whatwg.org",
b"//streams.spec.whatwg.org",
b"//url.spec.whatwg.org",
b"//xhr.spec.whatwg.org",
b"//w3c.github.io",

View file

@ -1,3 +0,0 @@
[response-arraybuffer-realm.window.html]
[realm of the ArrayBuffer from Response arrayBuffer()]
expected: FAIL

View file

@ -1,15 +1,33 @@
[response-stream-with-broken-then.any.worker.html]
[Attempt to inject {done: false, value: bye} via Object.prototype.then.]
expected: FAIL
[Attempt to inject value: undefined via Object.prototype.then.]
expected: FAIL
[Attempt to inject undefined via Object.prototype.then.]
expected: FAIL
[Attempt to inject 8.2 via Object.prototype.then.]
expected: FAIL
[intercepting arraybuffer to text conversion via Object.prototype.then should not be possible]
expected: FAIL
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
expected: FAIL
[response-stream-with-broken-then.any.sharedworker.html]
expected: ERROR
[response-stream-with-broken-then.any.html]
[Attempt to inject {done: false, value: bye} via Object.prototype.then.]
expected: FAIL
[Attempt to inject value: undefined via Object.prototype.then.]
expected: FAIL
[Attempt to inject undefined via Object.prototype.then.]
expected: FAIL
[Attempt to inject 8.2 via Object.prototype.then.]
expected: FAIL
[intercepting arraybuffer to text conversion via Object.prototype.then should not be possible]
expected: FAIL
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
expected: FAIL
[response-stream-with-broken-then.any.serviceworker.html]
expected: ERROR

View file

@ -1,3 +0,0 @@
[cross-realm-crash.window.html]
[should not crash on reading from stream cancelled in destroyed realm]
expected: FAIL

View file

@ -1,61 +1,9 @@
[default-reader.any.worker.html]
[Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via direct construction)]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should be OK if the stream is errored]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via getReader)]
expected: FAIL
[ReadableStreamDefaultReader closed should always return the same promise object]
expected: FAIL
[ReadableStreamDefaultReader constructor should get a ReadableStream object as argument]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should be OK if the stream is closed]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via direct construction)]
expected: FAIL
[Second reader can read chunks after first reader was released with pending read requests]
expected: FAIL
[default-reader.any.sharedworker.html]
expected: ERROR
[default-reader.any.serviceworker.html]
expected: ERROR
[default-reader.any.html]
[Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via direct construction)]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should be OK if the stream is errored]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via getReader)]
expected: FAIL
[ReadableStreamDefaultReader closed should always return the same promise object]
expected: FAIL
[ReadableStreamDefaultReader constructor should get a ReadableStream object as argument]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should be OK if the stream is closed]
expected: FAIL
[Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via direct construction)]
expected: FAIL
[Second reader can read chunks after first reader was released with pending read requests]
expected: FAIL
[default-reader.any.shadowrealm.html]
expected: TIMEOUT

View file

@ -4,22 +4,6 @@
[general.any.serviceworker.html]
expected: ERROR
[general.any.html]
[ReadableStream can't be constructed with an invalid type]
expected: FAIL
[ReadableStream: if pull rejects, it should error the stream]
expected: FAIL
[general.any.worker.html]
[ReadableStream can't be constructed with an invalid type]
expected: FAIL
[ReadableStream: if pull rejects, it should error the stream]
expected: FAIL
[general.any.shadowrealm.html]
expected: TIMEOUT

View file

@ -1,18 +1,3 @@
[global.html]
[Stream objects created in expected globals]
expected: FAIL
[Cancel promise is created in same global as stream]
expected: FAIL
[Closed Promise in correct global]
expected: FAIL
[Reader objects in correct global]
expected: FAIL
[Desired size can be grafted from one prototype to another]
expected: FAIL
[Tee Branches in correct global]
expected: FAIL

View file

@ -1,13 +1,9 @@
[tee.any.sharedworker.html]
expected: ERROR
[tee.any.worker.html]
[tee.any.serviceworker.html]
expected: ERROR
[tee.any.html]
[tee.any.shadowrealm.html]
expected: TIMEOUT

View file

@ -8,18 +8,10 @@
[ReadableStream (empty): instances have the correct methods and properties]
expected: FAIL
[ReadableStream (empty) reader: releasing the lock should reject all pending read requests]
expected: FAIL
[templated.any.worker.html]
[ReadableStream (empty): instances have the correct methods and properties]
expected: FAIL
[ReadableStream (empty) reader: releasing the lock should reject all pending read requests]
expected: FAIL
[templated.any.shadowrealm.html]
expected: TIMEOUT

View file

@ -13495,14 +13495,14 @@
]
],
"interfaces.https.html": [
"a70a25f44ce0c5aff0eac067afba5ac2d79141d6",
"75b7d9bc3e68a1147cf2d86b2a7af2b14f45597a",
[
null,
{}
]
],
"interfaces.worker.js": [
"c8e8ae9921eb01f71a63ebbce42ff589d0a12bf8",
"5288c40b0fb6e67f2b903f82f074580faa4dca33",
[
"mozilla/interfaces.worker.html",
{}

View file

@ -233,6 +233,11 @@ test_interfaces([
"PromiseRejectionEvent",
"RadioNodeList",
"Range",
"ReadableStreamDefaultReader",
"ReadableStreamDefaultController",
"ReadableStreamBYOBReader",
"ReadableByteStreamController",
"ReadableStreamBYOBRequest",
"Request",
"Response",
"Screen",

View file

@ -53,6 +53,11 @@ test_interfaces([
"ProgressEvent",
"PromiseRejectionEvent",
"ReadableStream",
"ReadableStreamDefaultReader",
"ReadableStreamDefaultController",
"ReadableStreamBYOBReader",
"ReadableByteStreamController",
"ReadableStreamBYOBRequest",
"Request",
"Response",
"SecurityPolicyViolationEvent",