mirror of
https://github.com/servo/servo.git
synced 2025-06-13 19:04:30 +00:00
* add basic interface for writable stream Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add stubs for pipeTo and pipeThrough methods Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add stubs for writable stream defautl writer Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add stubs for writable stream controller Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add underlying source dict Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add underlying source dict Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement constructor Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement init writable stream Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * impl setup default controller Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller setup Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller advance queue if neededd Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream finish erroring Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream reject close and closed promise if needed Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * finish implementation of stream finish erroring Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * call into controller setup from stream constructor Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream mark first write request in flight Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller process write Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * call into advance queue if needed at various points Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream deal with rejection, use from_safe_context Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller clear algorithms Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unused todo Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream start erroring Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * finish writer ensure ready promise rejected Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream finish in flight write request Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement write constructor and setup Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller error Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary unsafe code Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * finish implementing process write Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement close sentinel Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement public locked Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream abort Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix use of crown Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary options around writer promises Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer get desired size Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer ready Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer abort Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer release lock Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer public write Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement private writer write Uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement writer release. Uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * impl controller process close Uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * finish controller process close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * root promise handlers Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * handler errors in stream and writer constructor finish implementation of stream finish in flight close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix warnings Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement controller get chunk size Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * tidy the webidls Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * implement stream get writer Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix assertion of stream state when advancing queue if needed Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add docs for value with size Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * use reject_error in abort Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary allowances of unsafe code Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * turn writable-streams test suite on Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * update encodings test expectations Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * properly check if type is set on sink in stream constructor Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix double borrow in controller advance queue if needed Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * make the queue aware of the close sentinel when dequeuing a value Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix assertion of no backpressure in update backpressure Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * also clear strategy size when clearing algorithms Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove this object arg when calling into strategy size Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix has operations marked in flight Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix typo in has in flight write request Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * turn error into no-op when aborting a stream, if the stream is closed or errored. Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix error handling of calling into abort algorithm Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix error handling of calling into close and write algorithms Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix double borrow on queue fix logic in update_backpressure fix logic in get_desired_size fix logic in writer setup Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * update test expectations for aborting suite Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix controller get_backpressure Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix clippy Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * update test expectations to expect errors in tests using unsupported apis Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix error handling of calling into start algo in controller setup Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * update test expectations for test checking for undefined this in strategy size call Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * update test expectation to timeout for response-stream-with-broken-then.any.worker Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * update interfaces Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix use of global() and error to_jsval Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix use of crown for promise handlers Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove fail expectation from worker interface objects test Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove fail expectation for test expecting this to be undefined in callback Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix documentation link for writablestream state Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * refactor write_requests to use a vec deque uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary doc Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * refactor reject_close_and_closed_promise_if_needed to take a safe js context as argument uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * pass globals and contexts by ref where possible uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix doc link for controller Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary comment Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * change update_backpressure to be a method of the writablestream uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * rename writer method that resolve closed and ready promise for clarity uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add comments for steps in peek queue value Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix doc link for the abort algorihtm fulfillment handler Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix step doc and variable name in abort algo rejection handler Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * Add must_root to pending abort request Co-authored-by: Josh Matthews <josh@joshmatthews.net> Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com> * limit visibility to crate for has_operations_marked_inflight Co-authored-by: Josh Matthews <josh@joshmatthews.net> Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com> * limit visibility to crate for get_stored_error Co-authored-by: Josh Matthews <josh@joshmatthews.net> Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com> * remove potention re-borrow risk in reject loop on write requests in finish_erroring Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove potential re-borrow risk when taking pending abort request in finish_erroring Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove potential re-borrow risk when taking close request in reject_close_and_closed_promise_if_needed Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove re-borrow risks in finish_in_flight_close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove re-borrow risk on in_flight_close_request in finish_in_flight_close_with_error Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary clone of of reason in abort Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix condition on backpressure and a writable state in close Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * limit visibility to crate for update_backpressure Co-authored-by: Josh Matthews <josh@joshmatthews.net> Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com> * remove mutability of reason in abort workflow Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove unnecessary use of ignore_malloc_size_of around Dom in controller Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix ignore malloc size of comment for strategy size Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * reduce visibility of public methods to crate in controller Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove use of JS_GetPendingException in controller get_chunk_size Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * return early on error in write Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * use is_some_and in assertion that stream.witer is writer in release Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * root pending abort request uses ai Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix mutable re-borrow risk in writer Signed-off-by: gterzian <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: Josh Matthews <josh@joshmatthews.net>
500 lines
19 KiB
Rust
500 lines
19 KiB
Rust
/* 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/. */
|
|
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
use dom_struct::dom_struct;
|
|
use js::jsval::UndefinedValue;
|
|
use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
|
|
|
|
use crate::dom::bindings::codegen::Bindings::WritableStreamDefaultWriterBinding::WritableStreamDefaultWriterMethods;
|
|
use crate::dom::bindings::error::Error;
|
|
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomGlobal, Reflector};
|
|
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
|
use crate::dom::globalscope::GlobalScope;
|
|
use crate::dom::promise::Promise;
|
|
use crate::dom::writablestream::WritableStream;
|
|
use crate::realms::InRealm;
|
|
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
|
|
|
/// <https://streams.spec.whatwg.org/#writablestreamdefaultwriter>
|
|
#[dom_struct]
|
|
pub struct WritableStreamDefaultWriter {
|
|
reflector_: Reflector,
|
|
|
|
#[ignore_malloc_size_of = "Rc is hard"]
|
|
ready_promise: RefCell<Rc<Promise>>,
|
|
|
|
/// <https://streams.spec.whatwg.org/#writablestreamdefaultwriter-closedpromise>
|
|
#[ignore_malloc_size_of = "Rc is hard"]
|
|
closed_promise: RefCell<Rc<Promise>>,
|
|
|
|
/// <https://streams.spec.whatwg.org/#writablestreamdefaultwriter-stream>
|
|
stream: MutNullableDom<WritableStream>,
|
|
}
|
|
|
|
impl WritableStreamDefaultWriter {
|
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
|
/// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer>
|
|
/// The parts that create a new promise.
|
|
fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> WritableStreamDefaultWriter {
|
|
WritableStreamDefaultWriter {
|
|
reflector_: Reflector::new(),
|
|
stream: Default::default(),
|
|
closed_promise: RefCell::new(Promise::new(global, can_gc)),
|
|
ready_promise: RefCell::new(Promise::new(global, can_gc)),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn new(
|
|
global: &GlobalScope,
|
|
proto: Option<SafeHandleObject>,
|
|
can_gc: CanGc,
|
|
) -> DomRoot<WritableStreamDefaultWriter> {
|
|
reflect_dom_object_with_proto(
|
|
Box::new(WritableStreamDefaultWriter::new_inherited(global, can_gc)),
|
|
global,
|
|
proto,
|
|
can_gc,
|
|
)
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer>
|
|
/// Continuing from `new_inherited`, the rest.
|
|
pub(crate) fn setup(&self, cx: SafeJSContext, stream: &WritableStream) -> Result<(), Error> {
|
|
// If ! IsWritableStreamLocked(stream) is true, throw a TypeError exception.
|
|
if stream.is_locked() {
|
|
return Err(Error::Type("Stream is locked".to_string()));
|
|
}
|
|
|
|
// Set writer.[[stream]] to stream.
|
|
self.stream.set(Some(stream));
|
|
|
|
// Set stream.[[writer]] to writer.
|
|
stream.set_writer(Some(self));
|
|
|
|
// Let state be stream.[[state]].
|
|
|
|
// If state is "writable",
|
|
if stream.is_writable() {
|
|
// If ! WritableStreamCloseQueuedOrInFlight(stream) is false
|
|
// and stream.[[backpressure]] is true,
|
|
if !stream.close_queued_or_in_flight() && stream.get_backpressure() {
|
|
// set writer.[[readyPromise]] to a new promise.
|
|
// Done in `new_inherited`.
|
|
} else {
|
|
// Otherwise, set writer.[[readyPromise]] to a promise resolved with undefined.
|
|
// Note: new promise created in `new_inherited`.
|
|
self.ready_promise.borrow().resolve_native(&());
|
|
}
|
|
|
|
// Set writer.[[closedPromise]] to a new promise.
|
|
// Done in `new_inherited`.
|
|
return Ok(());
|
|
}
|
|
|
|
// Otherwise, if state is "erroring",
|
|
if stream.is_erroring() {
|
|
rooted!(in(*cx) let mut error = UndefinedValue());
|
|
stream.get_stored_error(error.handle_mut());
|
|
|
|
// Set writer.[[readyPromise]] to a promise rejected with stream.[[storedError]].
|
|
// Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
|
|
// Note: new promise created in `new_inherited`.
|
|
let ready_promise = self.ready_promise.borrow();
|
|
ready_promise.reject_native(&error.handle());
|
|
ready_promise.set_promise_is_handled();
|
|
|
|
// Set writer.[[closedPromise]] to a new promise.
|
|
// Done in `new_inherited`.
|
|
return Ok(());
|
|
}
|
|
|
|
// Otherwise, if state is "closed",
|
|
if stream.is_closed() {
|
|
// Set writer.[[readyPromise]] to a promise resolved with undefined.
|
|
// Note: new promise created in `new_inherited`.
|
|
self.ready_promise.borrow().resolve_native(&());
|
|
|
|
// Set writer.[[closedPromise]] to a promise resolved with undefined.
|
|
// Note: new promise created in `new_inherited`.
|
|
self.closed_promise.borrow().resolve_native(&());
|
|
return Ok(());
|
|
}
|
|
|
|
// Otherwise,
|
|
// Assert: state is "errored".
|
|
assert!(stream.is_errored());
|
|
|
|
// Let storedError be stream.[[storedError]].
|
|
rooted!(in(*cx) let mut error = UndefinedValue());
|
|
stream.get_stored_error(error.handle_mut());
|
|
|
|
// Set writer.[[readyPromise]] to a promise rejected with stream.[[storedError]].
|
|
// Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
|
|
// Note: new promise created in `new_inherited`.
|
|
let ready_promise = self.ready_promise.borrow();
|
|
ready_promise.reject_native(&error.handle());
|
|
ready_promise.set_promise_is_handled();
|
|
|
|
// Set writer.[[closedPromise]] to a promise rejected with storedError.
|
|
// Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
|
|
// Note: new promise created in `new_inherited`.
|
|
let ready_promise = self.closed_promise.borrow();
|
|
ready_promise.reject_native(&error.handle());
|
|
ready_promise.set_promise_is_handled();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn reject_closed_promise_with_stored_error(&self, error: &SafeHandleValue) {
|
|
self.closed_promise.borrow().reject_native(error);
|
|
}
|
|
|
|
pub(crate) fn set_close_promise_is_handled(&self) {
|
|
self.closed_promise.borrow().set_promise_is_handled();
|
|
}
|
|
|
|
pub(crate) fn set_ready_promise(&self, promise: Rc<Promise>) {
|
|
*self.ready_promise.borrow_mut() = promise;
|
|
}
|
|
|
|
pub(crate) fn resolve_ready_promise_with_undefined(&self) {
|
|
self.ready_promise.borrow().resolve_native(&());
|
|
}
|
|
|
|
pub(crate) fn resolve_closed_promise_with_undefined(&self) {
|
|
self.closed_promise.borrow().resolve_native(&());
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-ready-promise-rejected>
|
|
pub(crate) fn ensure_ready_promise_rejected(
|
|
&self,
|
|
global: &GlobalScope,
|
|
error: SafeHandleValue,
|
|
can_gc: CanGc,
|
|
) {
|
|
let ready_promise = self.ready_promise.borrow().clone();
|
|
|
|
// If writer.[[readyPromise]].[[PromiseState]] is "pending",
|
|
if ready_promise.is_pending() {
|
|
// reject writer.[[readyPromise]] with error.
|
|
ready_promise.reject_native(&error);
|
|
|
|
// Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
|
|
ready_promise.set_promise_is_handled();
|
|
} else {
|
|
// Otherwise, set writer.[[readyPromise]] to a promise rejected with error.
|
|
let promise = Promise::new(global, can_gc);
|
|
promise.reject_native(&error);
|
|
|
|
// Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
|
|
promise.set_promise_is_handled();
|
|
*self.ready_promise.borrow_mut() = promise;
|
|
}
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-closed-promise-rejected>
|
|
pub(crate) fn ensure_closed_promise_rejected(
|
|
&self,
|
|
global: &GlobalScope,
|
|
error: SafeHandleValue,
|
|
can_gc: CanGc,
|
|
) {
|
|
let closed_promise = self.closed_promise.borrow().clone();
|
|
|
|
// If writer.[[closedPromise]].[[PromiseState]] is "pending",
|
|
if closed_promise.is_pending() {
|
|
// reject writer.[[closedPromise]] with error.
|
|
closed_promise.reject_native(&error);
|
|
|
|
// Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
|
|
closed_promise.set_promise_is_handled();
|
|
} else {
|
|
// Otherwise, set writer.[[closedPromise]] to a promise rejected with error.
|
|
let promise = Promise::new(global, can_gc);
|
|
promise.reject_native(&error);
|
|
|
|
// Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
|
|
promise.set_promise_is_handled();
|
|
*self.closed_promise.borrow_mut() = promise;
|
|
}
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#writable-stream-default-writer-abort>
|
|
fn abort(
|
|
&self,
|
|
cx: SafeJSContext,
|
|
global: &GlobalScope,
|
|
reason: SafeHandleValue,
|
|
can_gc: CanGc,
|
|
) -> Rc<Promise> {
|
|
// Let stream be writer.[[stream]].
|
|
let Some(stream) = self.stream.get() else {
|
|
// Assert: stream is not undefined.
|
|
unreachable!("Stream should be set.");
|
|
};
|
|
|
|
// Return ! WritableStreamAbort(stream, reason).
|
|
stream.abort(cx, global, reason, can_gc)
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#writable-stream-default-writer-close>
|
|
fn close(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) -> Rc<Promise> {
|
|
// Let stream be writer.[[stream]].
|
|
let Some(stream) = self.stream.get() else {
|
|
// Assert: stream is not undefined.
|
|
unreachable!("Stream should be set.");
|
|
};
|
|
|
|
// Return ! WritableStreamClose(stream).
|
|
stream.close(cx, global, can_gc)
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#writable-stream-default-writer-write>
|
|
fn write(
|
|
&self,
|
|
cx: SafeJSContext,
|
|
global: &GlobalScope,
|
|
chunk: SafeHandleValue,
|
|
can_gc: CanGc,
|
|
) -> Rc<Promise> {
|
|
// Let stream be writer.[[stream]].
|
|
let Some(stream) = self.stream.get() else {
|
|
// Assert: stream is not undefined.
|
|
unreachable!("Stream should be set.");
|
|
};
|
|
|
|
// Let controller be stream.[[controller]].
|
|
// Note: asserting controller is some.
|
|
let Some(controller) = stream.get_controller() else {
|
|
unreachable!("Controller should be set.");
|
|
};
|
|
|
|
// Let chunkSize be ! WritableStreamDefaultControllerGetChunkSize(controller, chunk).
|
|
let chunk_size = controller.get_chunk_size(cx, global, chunk, can_gc);
|
|
|
|
// If stream is not equal to writer.[[stream]],
|
|
// return a promise rejected with a TypeError exception.
|
|
if !self
|
|
.stream
|
|
.get()
|
|
.map_or(false, |current_stream| current_stream == stream)
|
|
{
|
|
let promise = Promise::new(global, can_gc);
|
|
promise.reject_error(Error::Type(
|
|
"Stream is not equal to writer stream".to_string(),
|
|
));
|
|
return promise;
|
|
}
|
|
|
|
// Let state be stream.[[state]].
|
|
// If state is "errored",
|
|
if stream.is_errored() {
|
|
// return a promise rejected with stream.[[storedError]].
|
|
rooted!(in(*cx) let mut error = UndefinedValue());
|
|
stream.get_stored_error(error.handle_mut());
|
|
let promise = Promise::new(global, can_gc);
|
|
promise.reject_native(&error.handle());
|
|
return promise;
|
|
}
|
|
|
|
// If ! WritableStreamCloseQueuedOrInFlight(stream) is true
|
|
// or state is "closed",
|
|
if stream.close_queued_or_in_flight() || stream.is_closed() {
|
|
// return a promise rejected with a TypeError exception
|
|
// indicating that the stream is closing or closed
|
|
let promise = Promise::new(global, can_gc);
|
|
promise.reject_error(Error::Type(
|
|
"Stream has been closed, or has close queued or in-flight".to_string(),
|
|
));
|
|
return promise;
|
|
}
|
|
|
|
// If state is "erroring",
|
|
if stream.is_erroring() {
|
|
// return a promise rejected with stream.[[storedError]].
|
|
rooted!(in(*cx) let mut error = UndefinedValue());
|
|
stream.get_stored_error(error.handle_mut());
|
|
let promise = Promise::new(global, can_gc);
|
|
promise.reject_native(&error.handle());
|
|
return promise;
|
|
}
|
|
|
|
// Assert: state is "writable".
|
|
assert!(stream.is_writable());
|
|
|
|
// Let promise be ! WritableStreamAddWriteRequest(stream).
|
|
let promise = stream.add_write_request(global, can_gc);
|
|
|
|
// Perform ! WritableStreamDefaultControllerWrite(controller, chunk, chunkSize).
|
|
controller.write(cx, global, chunk, chunk_size, can_gc);
|
|
|
|
// Return promise.
|
|
promise
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#writable-stream-default-writer-release>
|
|
pub(crate) fn release(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) {
|
|
// Let stream be this.[[stream]].
|
|
let Some(stream) = self.stream.get() else {
|
|
// Assert: stream is not undefined.
|
|
unreachable!("Stream should be set.");
|
|
};
|
|
|
|
// Assert: stream.[[writer]] is writer.
|
|
assert!(stream.get_writer().is_some_and(|writer| &*writer == self));
|
|
|
|
// Let releasedError be a new TypeError.
|
|
let released_error = Error::Type("Writer has been released".to_string());
|
|
|
|
// Root the js val of the error.
|
|
rooted!(in(*cx) let mut error = UndefinedValue());
|
|
released_error.to_jsval(cx, global, error.handle_mut());
|
|
|
|
// Perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError).
|
|
self.ensure_ready_promise_rejected(global, error.handle(), can_gc);
|
|
|
|
// Perform ! WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError).
|
|
self.ensure_closed_promise_rejected(global, error.handle(), can_gc);
|
|
|
|
// Set stream.[[writer]] to undefined.
|
|
stream.set_writer(None);
|
|
|
|
// Set this.[[stream]] to undefined.
|
|
self.stream.set(None);
|
|
}
|
|
}
|
|
|
|
impl WritableStreamDefaultWriterMethods<crate::DomTypeHolder> for WritableStreamDefaultWriter {
|
|
/// <https://streams.spec.whatwg.org/#default-writer-closed>
|
|
fn Closed(&self) -> Rc<Promise> {
|
|
// Return this.[[closedPromise]].
|
|
return self.closed_promise.borrow().clone();
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-desired-size>
|
|
fn GetDesiredSize(&self) -> Result<Option<f64>, Error> {
|
|
// If this.[[stream]] is undefined, throw a TypeError exception.
|
|
let Some(stream) = self.stream.get() else {
|
|
return Err(Error::Type("Stream is undefined".to_string()));
|
|
};
|
|
|
|
// Return ! WritableStreamDefaultWriterGetDesiredSize(this).
|
|
Ok(stream.get_desired_size())
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-ready>
|
|
fn Ready(&self) -> Rc<Promise> {
|
|
// Return this.[[readyPromise]].
|
|
return self.ready_promise.borrow().clone();
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-abort>
|
|
fn Abort(
|
|
&self,
|
|
cx: SafeJSContext,
|
|
reason: SafeHandleValue,
|
|
realm: InRealm,
|
|
can_gc: CanGc,
|
|
) -> Rc<Promise> {
|
|
let global = GlobalScope::from_safe_context(cx, realm);
|
|
|
|
// If this.[[stream]] is undefined,
|
|
if self.stream.get().is_none() {
|
|
// return a promise rejected with a TypeError exception.
|
|
let promise = Promise::new(&global, can_gc);
|
|
promise.reject_error(Error::Type("Stream is undefined".to_string()));
|
|
return promise;
|
|
}
|
|
|
|
// Return ! WritableStreamDefaultWriterAbort(this, reason).
|
|
self.abort(cx, &global, reason, can_gc)
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-close>
|
|
fn Close(&self, in_realm: InRealm, can_gc: CanGc) -> Rc<Promise> {
|
|
let cx = GlobalScope::get_cx();
|
|
let global = GlobalScope::from_safe_context(cx, in_realm);
|
|
let promise = Promise::new(&global, can_gc);
|
|
|
|
// Let stream be this.[[stream]].
|
|
let Some(stream) = self.stream.get() else {
|
|
// If stream is undefined,
|
|
// return a promise rejected with a TypeError exception.
|
|
promise.reject_error(Error::Type("Stream is undefined".to_string()));
|
|
return promise;
|
|
};
|
|
|
|
// If ! WritableStreamCloseQueuedOrInFlight(stream) is true
|
|
if stream.close_queued_or_in_flight() {
|
|
// return a promise rejected with a TypeError exception.
|
|
promise.reject_error(Error::Type(
|
|
"Stream has closed queued or in-flight".to_string(),
|
|
));
|
|
return promise;
|
|
}
|
|
|
|
self.close(cx, &global, can_gc)
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-release-lock>
|
|
fn ReleaseLock(&self, can_gc: CanGc) {
|
|
// Let stream be this.[[stream]].
|
|
let Some(stream) = self.stream.get() else {
|
|
// If stream is undefined, return.
|
|
return;
|
|
};
|
|
|
|
// Assert: stream.[[writer]] is not undefined.
|
|
assert!(stream.get_writer().is_some());
|
|
|
|
let global = self.global();
|
|
let cx = GlobalScope::get_cx();
|
|
|
|
// Perform ! WritableStreamDefaultWriterRelease(this).
|
|
self.release(cx, &global, can_gc);
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-write>
|
|
fn Write(
|
|
&self,
|
|
cx: SafeJSContext,
|
|
chunk: SafeHandleValue,
|
|
realm: InRealm,
|
|
can_gc: CanGc,
|
|
) -> Rc<Promise> {
|
|
let global = GlobalScope::from_safe_context(cx, realm);
|
|
|
|
// If this.[[stream]] is undefined,
|
|
if self.stream.get().is_none() {
|
|
// return a promise rejected with a TypeError exception.
|
|
let global = GlobalScope::from_safe_context(cx, realm);
|
|
let promise = Promise::new(&global, can_gc);
|
|
promise.reject_error(Error::Type("Stream is undefined".to_string()));
|
|
return promise;
|
|
}
|
|
|
|
// Return ! WritableStreamDefaultWriterWrite(this, chunk).
|
|
self.write(cx, &global, chunk, can_gc)
|
|
}
|
|
|
|
/// <https://streams.spec.whatwg.org/#default-writer-constructor>
|
|
fn Constructor(
|
|
global: &GlobalScope,
|
|
proto: Option<SafeHandleObject>,
|
|
can_gc: CanGc,
|
|
stream: &WritableStream,
|
|
) -> Result<DomRoot<WritableStreamDefaultWriter>, Error> {
|
|
let writer = WritableStreamDefaultWriter::new(global, proto, can_gc);
|
|
|
|
let cx = GlobalScope::get_cx();
|
|
|
|
// Perform ? SetUpWritableStreamDefaultWriter(this, stream).
|
|
writer.setup(cx, stream)?;
|
|
|
|
Ok(writer)
|
|
}
|
|
}
|