mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Start adding support for transforms in readable and writable streams (#36470)
Start adding support for transforms in readable and writable streams. Part of https://github.com/servo/servo/issues/34676
This commit is contained in:
parent
02b38adf43
commit
4d975e947b
13 changed files with 394 additions and 161 deletions
|
@ -14,11 +14,11 @@ use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue,
|
|||
use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
|
||||
use crate::dom::bindings::callback::ExceptionHandling;
|
||||
use crate::dom::bindings::codegen::Bindings::UnderlyingSinkBinding::{
|
||||
UnderlyingSink, UnderlyingSinkAbortCallback, UnderlyingSinkCloseCallback,
|
||||
UnderlyingSinkStartCallback, UnderlyingSinkWriteCallback,
|
||||
UnderlyingSinkAbortCallback, UnderlyingSinkCloseCallback, UnderlyingSinkStartCallback,
|
||||
UnderlyingSinkWriteCallback,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::WritableStreamDefaultControllerBinding::WritableStreamDefaultControllerMethods;
|
||||
use crate::dom::bindings::error::{Error, ErrorToJsval};
|
||||
use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
|
||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
|
@ -268,15 +268,46 @@ impl Callback for WriteAlgorithmRejectionHandler {
|
|||
|
||||
/// The type of sink algorithms we are using.
|
||||
#[derive(JSTraceable, PartialEq)]
|
||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||
pub enum UnderlyingSinkType {
|
||||
/// Algorithms are provided by Js callbacks.
|
||||
Js,
|
||||
Js {
|
||||
/// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-abortalgorithm>
|
||||
abort: RefCell<Option<Rc<UnderlyingSinkAbortCallback>>>,
|
||||
|
||||
start: RefCell<Option<Rc<UnderlyingSinkStartCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-closealgorithm>
|
||||
close: RefCell<Option<Rc<UnderlyingSinkCloseCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-writealgorithm>
|
||||
write: RefCell<Option<Rc<UnderlyingSinkWriteCallback>>>,
|
||||
},
|
||||
/// Algorithms supporting streams transfer are implemented in Rust.
|
||||
/// The promise and port used in those algorithms are stored here.
|
||||
Transfer {
|
||||
backpressure_promise: Rc<RefCell<Option<Rc<Promise>>>>,
|
||||
port: Dom<MessagePort>,
|
||||
},
|
||||
/// Algorithms supporting transform streams are implemented in Rust.
|
||||
#[allow(unused)]
|
||||
Transform(/*Dom<TransformStream>, Rc<Promise>*/),
|
||||
}
|
||||
|
||||
impl UnderlyingSinkType {
|
||||
pub(crate) fn new_js(
|
||||
abort: Option<Rc<UnderlyingSinkAbortCallback>>,
|
||||
start: Option<Rc<UnderlyingSinkStartCallback>>,
|
||||
close: Option<Rc<UnderlyingSinkCloseCallback>>,
|
||||
write: Option<Rc<UnderlyingSinkWriteCallback>>,
|
||||
) -> Self {
|
||||
UnderlyingSinkType::Js {
|
||||
abort: RefCell::new(abort),
|
||||
start: RefCell::new(start),
|
||||
close: RefCell::new(close),
|
||||
write: RefCell::new(write),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#ws-default-controller-class>
|
||||
|
@ -284,21 +315,11 @@ pub enum UnderlyingSinkType {
|
|||
pub struct WritableStreamDefaultController {
|
||||
reflector_: Reflector,
|
||||
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
/// The type of underlying sink used. Besides the default JS one,
|
||||
/// there will be others for stream transfer, and for transform stream.
|
||||
#[ignore_malloc_size_of = "underlying_sink_type"]
|
||||
underlying_sink_type: UnderlyingSinkType,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-abortalgorithm>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
abort: RefCell<Option<Rc<UnderlyingSinkAbortCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-closealgorithm>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
close: RefCell<Option<Rc<UnderlyingSinkCloseCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-writealgorithm>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
write: RefCell<Option<Rc<UnderlyingSinkWriteCallback>>>,
|
||||
|
||||
/// The JS object used as `this` when invoking sink algorithms.
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
underlying_sink_obj: Heap<*mut JSObject>,
|
||||
|
@ -325,7 +346,6 @@ impl WritableStreamDefaultController {
|
|||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
fn new_inherited(
|
||||
underlying_sink_type: UnderlyingSinkType,
|
||||
underlying_sink: &UnderlyingSink,
|
||||
strategy_hwm: f64,
|
||||
strategy_size: Rc<QueuingStrategySize>,
|
||||
) -> WritableStreamDefaultController {
|
||||
|
@ -334,9 +354,6 @@ impl WritableStreamDefaultController {
|
|||
underlying_sink_type,
|
||||
queue: Default::default(),
|
||||
stream: Default::default(),
|
||||
abort: RefCell::new(underlying_sink.abort.clone()),
|
||||
close: RefCell::new(underlying_sink.close.clone()),
|
||||
write: RefCell::new(underlying_sink.write.clone()),
|
||||
underlying_sink_obj: Default::default(),
|
||||
strategy_hwm,
|
||||
strategy_size: RefCell::new(Some(strategy_size)),
|
||||
|
@ -344,10 +361,10 @@ impl WritableStreamDefaultController {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
pub(crate) fn new(
|
||||
global: &GlobalScope,
|
||||
underlying_sink_type: UnderlyingSinkType,
|
||||
underlying_sink: &UnderlyingSink,
|
||||
strategy_hwm: f64,
|
||||
strategy_size: Rc<QueuingStrategySize>,
|
||||
can_gc: CanGc,
|
||||
|
@ -355,7 +372,6 @@ impl WritableStreamDefaultController {
|
|||
reflect_dom_object(
|
||||
Box::new(WritableStreamDefaultController::new_inherited(
|
||||
underlying_sink_type,
|
||||
underlying_sink,
|
||||
strategy_hwm,
|
||||
strategy_size,
|
||||
)),
|
||||
|
@ -375,27 +391,44 @@ impl WritableStreamDefaultController {
|
|||
|
||||
/// <https://streams.spec.whatwg.org/#writable-stream-default-controller-clear-algorithms>
|
||||
fn clear_algorithms(&self) {
|
||||
// Set controller.[[writeAlgorithm]] to undefined.
|
||||
self.write.borrow_mut().take();
|
||||
match &self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js {
|
||||
abort,
|
||||
start: _,
|
||||
close,
|
||||
write,
|
||||
} => {
|
||||
// Set controller.[[writeAlgorithm]] to undefined.
|
||||
write.borrow_mut().take();
|
||||
|
||||
// Set controller.[[closeAlgorithm]] to undefined.
|
||||
self.close.borrow_mut().take();
|
||||
// Set controller.[[closeAlgorithm]] to undefined.
|
||||
close.borrow_mut().take();
|
||||
|
||||
// Set controller.[[abortAlgorithm]] to undefined.
|
||||
self.abort.borrow_mut().take();
|
||||
// Set controller.[[abortAlgorithm]] to undefined.
|
||||
abort.borrow_mut().take();
|
||||
},
|
||||
UnderlyingSinkType::Transfer {
|
||||
backpressure_promise,
|
||||
..
|
||||
} => {
|
||||
backpressure_promise.borrow_mut().take();
|
||||
},
|
||||
UnderlyingSinkType::Transform() => {
|
||||
return;
|
||||
},
|
||||
}
|
||||
|
||||
// Set controller.[[strategySizeAlgorithm]] to undefined.
|
||||
self.strategy_size.borrow_mut().take();
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controllerr>
|
||||
/// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller>
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn setup(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
stream: &WritableStream,
|
||||
start: &Option<Rc<UnderlyingSinkStartCallback>>,
|
||||
can_gc: CanGc,
|
||||
) -> Result<(), Error> {
|
||||
// Assert: stream implements WritableStream.
|
||||
|
@ -436,40 +469,7 @@ impl WritableStreamDefaultController {
|
|||
|
||||
// Let startResult be the result of performing startAlgorithm. (This may throw an exception.)
|
||||
// Let startPromise be a promise resolved with startResult.
|
||||
let start_promise = if let Some(start) = start {
|
||||
rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
|
||||
rooted!(in(*cx) let mut result: JSVal);
|
||||
rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
|
||||
start.Call_(
|
||||
&this_object.handle(),
|
||||
self,
|
||||
result.handle_mut(),
|
||||
ExceptionHandling::Rethrow,
|
||||
can_gc,
|
||||
)?;
|
||||
let is_promise = unsafe {
|
||||
if result.is_object() {
|
||||
result_object.set(result.to_object());
|
||||
IsPromiseObject(result_object.handle().into_handle())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if is_promise {
|
||||
let promise = Promise::new_with_js_promise(result_object.handle(), cx);
|
||||
promise
|
||||
} else {
|
||||
Promise::new_resolved(global, cx, result.get(), can_gc)
|
||||
}
|
||||
} else {
|
||||
// Note: we are either here because the Js algorithm is none,
|
||||
// or because we are suppporting a stream transfer as
|
||||
// part of #abstract-opdef-setupcrossrealmtransformwritable
|
||||
// and the logic is the same for both.
|
||||
|
||||
// Let startAlgorithm be an algorithm that returns undefined.
|
||||
Promise::new_resolved(global, cx, (), can_gc)
|
||||
};
|
||||
let start_promise = self.start_algorithm(cx, global, can_gc)?;
|
||||
|
||||
let rooted_default_controller = DomRoot::from_ref(self);
|
||||
|
||||
|
@ -509,6 +509,64 @@ impl WritableStreamDefaultController {
|
|||
self.advance_queue_if_needed(cx, global, can_gc);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn start_algorithm(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<Rc<Promise>> {
|
||||
match &self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js {
|
||||
start,
|
||||
abort: _,
|
||||
close: _,
|
||||
write: _,
|
||||
} => {
|
||||
let algo = start.borrow().clone();
|
||||
let start_promise = if let Some(start) = algo {
|
||||
rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
|
||||
rooted!(in(*cx) let mut result: JSVal);
|
||||
rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
|
||||
start.Call_(
|
||||
&this_object.handle(),
|
||||
self,
|
||||
result.handle_mut(),
|
||||
ExceptionHandling::Rethrow,
|
||||
can_gc,
|
||||
)?;
|
||||
let is_promise = unsafe {
|
||||
if result.is_object() {
|
||||
result_object.set(result.to_object());
|
||||
IsPromiseObject(result_object.handle().into_handle())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if is_promise {
|
||||
let promise = Promise::new_with_js_promise(result_object.handle(), cx);
|
||||
promise
|
||||
} else {
|
||||
Promise::new_resolved(global, cx, result.get(), can_gc)
|
||||
}
|
||||
} else {
|
||||
// Let startAlgorithm be an algorithm that returns undefined.
|
||||
Promise::new_resolved(global, cx, (), can_gc)
|
||||
};
|
||||
|
||||
Ok(start_promise)
|
||||
},
|
||||
UnderlyingSinkType::Transfer { .. } => {
|
||||
// Let startAlgorithm be an algorithm that returns undefined.
|
||||
Ok(Promise::new_resolved(global, cx, (), can_gc))
|
||||
},
|
||||
UnderlyingSinkType::Transform() => {
|
||||
// Let startAlgorithm be an algorithm that returns startPromise.
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#ref-for-abstract-opdef-writablestreamcontroller-abortsteps>
|
||||
pub(crate) fn abort_steps(
|
||||
&self,
|
||||
|
@ -517,10 +575,15 @@ impl WritableStreamDefaultController {
|
|||
reason: SafeHandleValue,
|
||||
can_gc: CanGc,
|
||||
) -> Rc<Promise> {
|
||||
let result = match self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js => {
|
||||
let result = match &self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js {
|
||||
abort,
|
||||
start: _,
|
||||
close: _,
|
||||
write: _,
|
||||
} => {
|
||||
rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
|
||||
let algo = self.abort.borrow().clone();
|
||||
let algo = abort.borrow().clone();
|
||||
// Let result be the result of performing this.[[abortAlgorithm]], passing reason.
|
||||
let result = if let Some(algo) = algo {
|
||||
algo.Call_(
|
||||
|
@ -538,7 +601,7 @@ impl WritableStreamDefaultController {
|
|||
promise
|
||||
})
|
||||
},
|
||||
UnderlyingSinkType::Transfer { ref port, .. } => {
|
||||
UnderlyingSinkType::Transfer { port, .. } => {
|
||||
// The steps from the `abortAlgorithm` at
|
||||
// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
|
||||
|
||||
|
@ -559,6 +622,10 @@ impl WritableStreamDefaultController {
|
|||
}
|
||||
promise
|
||||
},
|
||||
UnderlyingSinkType::Transform() => {
|
||||
// Return ! TransformStreamDefaultSinkAbortAlgorithm(stream, reason).
|
||||
todo!()
|
||||
},
|
||||
};
|
||||
|
||||
// Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
|
||||
|
@ -575,10 +642,15 @@ impl WritableStreamDefaultController {
|
|||
global: &GlobalScope,
|
||||
can_gc: CanGc,
|
||||
) -> Rc<Promise> {
|
||||
match self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js => {
|
||||
match &self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js {
|
||||
abort: _,
|
||||
start: _,
|
||||
close: _,
|
||||
write,
|
||||
} => {
|
||||
rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
|
||||
let algo = self.write.borrow().clone();
|
||||
let algo = write.borrow().clone();
|
||||
let result = if let Some(algo) = algo {
|
||||
algo.Call_(
|
||||
&this_object.handle(),
|
||||
|
@ -597,9 +669,8 @@ impl WritableStreamDefaultController {
|
|||
})
|
||||
},
|
||||
UnderlyingSinkType::Transfer {
|
||||
ref backpressure_promise,
|
||||
ref port,
|
||||
..
|
||||
backpressure_promise,
|
||||
port,
|
||||
} => {
|
||||
// The steps from the `writeAlgorithm` at
|
||||
// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
|
||||
|
@ -636,6 +707,10 @@ impl WritableStreamDefaultController {
|
|||
.append_native_handler(&handler, comp, can_gc);
|
||||
result_promise
|
||||
},
|
||||
UnderlyingSinkType::Transform() => {
|
||||
// Return ! TransformStreamDefaultSinkWriteAlgorithm(stream, chunk).
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -646,11 +721,16 @@ impl WritableStreamDefaultController {
|
|||
global: &GlobalScope,
|
||||
can_gc: CanGc,
|
||||
) -> Rc<Promise> {
|
||||
match self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js => {
|
||||
match &self.underlying_sink_type {
|
||||
UnderlyingSinkType::Js {
|
||||
abort: _,
|
||||
start: _,
|
||||
close,
|
||||
write: _,
|
||||
} => {
|
||||
rooted!(in(*cx) let mut this_object = ptr::null_mut::<JSObject>());
|
||||
this_object.set(self.underlying_sink_obj.get());
|
||||
let algo = self.close.borrow().clone();
|
||||
let algo = close.borrow().clone();
|
||||
let result = if let Some(algo) = algo {
|
||||
algo.Call_(&this_object.handle(), ExceptionHandling::Rethrow, can_gc)
|
||||
} else {
|
||||
|
@ -662,7 +742,7 @@ impl WritableStreamDefaultController {
|
|||
promise
|
||||
})
|
||||
},
|
||||
UnderlyingSinkType::Transfer { ref port, .. } => {
|
||||
UnderlyingSinkType::Transfer { port, .. } => {
|
||||
// The steps from the `closeAlgorithm` at
|
||||
// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
|
||||
|
||||
|
@ -677,6 +757,10 @@ impl WritableStreamDefaultController {
|
|||
// Return a promise resolved with undefined.
|
||||
Promise::new_resolved(global, cx, (), can_gc)
|
||||
},
|
||||
UnderlyingSinkType::Transform() => {
|
||||
// Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue