From fc2135cc02436a535bbe024e72751634921ca540 Mon Sep 17 00:00:00 2001 From: Taym Haddadi Date: Wed, 18 Jun 2025 08:26:05 -0700 Subject: [PATCH] implement Writablestreamdefaultcontroller abortcontroller (#37511) using abort signal and abortcontroller in Writablestreamdefaultcontroller Part of https://github.com/servo/servo/issues/34866 --------- Signed-off-by: Taym Haddadi --- components/script/dom/abortcontroller.rs | 16 ++++- components/script/dom/abortsignal.rs | 1 - components/script/dom/readablestream.rs | 4 +- components/script/dom/writablestream.rs | 20 ++++-- .../dom/writablestreamdefaultcontroller.rs | 27 ++++++++ .../script/dom/writablestreamdefaultwriter.rs | 5 +- .../WritableStreamDefaultController.webidl | 2 +- .../streams/piping/pipe-through.any.js.ini | 45 -------------- .../writable-streams/aborting.any.js.ini | 62 ------------------- 9 files changed, 63 insertions(+), 119 deletions(-) diff --git a/components/script/dom/abortcontroller.rs b/components/script/dom/abortcontroller.rs index fdd79c51417..2df26511be4 100644 --- a/components/script/dom/abortcontroller.rs +++ b/components/script/dom/abortcontroller.rs @@ -35,7 +35,7 @@ impl AbortController { } /// - fn new_with_proto( + pub(crate) fn new_with_proto( global: &GlobalScope, proto: Option, can_gc: CanGc, @@ -52,10 +52,22 @@ impl AbortController { } /// - fn signal_abort(&self, cx: JSContext, reason: HandleValue, realm: InRealm, can_gc: CanGc) { + pub(crate) fn signal_abort( + &self, + cx: JSContext, + reason: HandleValue, + realm: InRealm, + can_gc: CanGc, + ) { // signal abort on controller’s signal with reason if it is given. self.signal.signal_abort(cx, reason, realm, can_gc); } + + /// + pub(crate) fn signal(&self) -> DomRoot { + // The signal getter steps are to return this’s signal. + self.signal.as_rooted() + } } impl AbortControllerMethods for AbortController { diff --git a/components/script/dom/abortsignal.rs b/components/script/dom/abortsignal.rs index cdd56b4897b..2b3021869ed 100644 --- a/components/script/dom/abortsignal.rs +++ b/components/script/dom/abortsignal.rs @@ -59,7 +59,6 @@ impl AbortSignal { } } - #[allow(dead_code)] pub(crate) fn new_with_proto( global: &GlobalScope, proto: Option, diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 977d0d14489..205a885acdd 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -738,7 +738,7 @@ impl PipeTo { let promise = match action { ShutdownAction::WritableStreamAbort => { let dest = self.writer.get_stream().expect("Stream must be set"); - dest.abort(cx, global, error.handle(), can_gc) + dest.abort(cx, global, error.handle(), realm, can_gc) }, ShutdownAction::ReadableStreamCancel => { let source = self @@ -771,7 +771,7 @@ impl PipeTo { // If dest.[[state]] is "writable", let promise = if dest.is_writable() { // return ! WritableStreamAbort(dest, error) - dest.abort(cx, global, error.handle(), can_gc) + dest.abort(cx, global, error.handle(), realm, can_gc) } else { // Otherwise, return a promise resolved with undefined. Promise::new_resolved(global, cx, (), can_gc) diff --git a/components/script/dom/writablestream.rs b/components/script/dom/writablestream.rs index a01596acd5f..4defef05007 100644 --- a/components/script/dom/writablestream.rs +++ b/components/script/dom/writablestream.rs @@ -655,6 +655,7 @@ impl WritableStream { cx: SafeJSContext, global: &GlobalScope, provided_reason: SafeHandleValue, + realm: InRealm, can_gc: CanGc, ) -> Rc { // If stream.[[state]] is "closed" or "errored", @@ -663,10 +664,21 @@ impl WritableStream { return Promise::new_resolved(global, cx, (), can_gc); } - // TODO: Signal abort on stream.[[controller]].[[abortController]] with reason. + // Signal abort on stream.[[controller]].[[abortController]] with reason. + self.get_controller() + .expect("Stream must have a controller.") + .signal_abort(cx, provided_reason, realm, can_gc); - // TODO: If state is "closed" or "errored", return a promise resolved with undefined. - // Note: state may have changed because of signal above. + // Let state be stream.[[state]]. + let state = self.state.get(); + + // If state is "closed" or "errored", return a promise resolved with undefined. + if matches!( + state, + WritableStreamState::Closed | WritableStreamState::Errored + ) { + return Promise::new_resolved(global, cx, (), can_gc); + } // If stream.[[pendingAbortRequest]] is not undefined, if self.pending_abort_request.borrow().is_some() { @@ -1077,7 +1089,7 @@ impl WritableStreamMethods for WritableStream { } // Return ! WritableStreamAbort(this, reason). - self.abort(cx, &global, reason, can_gc) + self.abort(cx, &global, reason, realm, can_gc) } /// diff --git a/components/script/dom/writablestreamdefaultcontroller.rs b/components/script/dom/writablestreamdefaultcontroller.rs index 135ee6faa59..0cf11ebeac2 100644 --- a/components/script/dom/writablestreamdefaultcontroller.rs +++ b/components/script/dom/writablestreamdefaultcontroller.rs @@ -27,6 +27,7 @@ use crate::dom::messageport::MessagePort; use crate::dom::promise::Promise; use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler}; use crate::dom::readablestreamdefaultcontroller::{EnqueuedValue, QueueWithSizes, ValueWithSize}; +use crate::dom::types::{AbortController, AbortSignal}; use crate::dom::writablestream::WritableStream; use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; @@ -339,15 +340,20 @@ pub struct WritableStreamDefaultController { /// stream: MutNullableDom, + + /// + abort_controller: Dom, } impl WritableStreamDefaultController { /// #[cfg_attr(crown, allow(crown::unrooted_must_root))] fn new_inherited( + global: &GlobalScope, underlying_sink_type: UnderlyingSinkType, strategy_hwm: f64, strategy_size: Rc, + can_gc: CanGc, ) -> WritableStreamDefaultController { WritableStreamDefaultController { reflector_: Reflector::new(), @@ -358,6 +364,7 @@ impl WritableStreamDefaultController { strategy_hwm, strategy_size: RefCell::new(Some(strategy_size)), started: Default::default(), + abort_controller: Dom::from_ref(&AbortController::new_with_proto(global, None, can_gc)), } } @@ -371,9 +378,11 @@ impl WritableStreamDefaultController { ) -> DomRoot { reflect_dom_object( Box::new(WritableStreamDefaultController::new_inherited( + global, underlying_sink_type, strategy_hwm, strategy_size, + can_gc, )), global, can_gc, @@ -389,6 +398,18 @@ impl WritableStreamDefaultController { self.underlying_sink_obj.set(*this_object); } + /// "Signal abort" call from + pub(crate) fn signal_abort( + &self, + cx: SafeJSContext, + reason: SafeHandleValue, + realm: InRealm, + can_gc: CanGc, + ) { + self.abort_controller + .signal_abort(cx, reason, realm, can_gc); + } + /// fn clear_algorithms(&self) { match &self.underlying_sink_type { @@ -1085,4 +1106,10 @@ impl WritableStreamDefaultControllerMethods // Perform ! WritableStreamDefaultControllerError(this, e). self.error(&stream, cx, e, &global, can_gc); } + + /// + fn Signal(&self) -> DomRoot { + // Return this.[[abortController]]’s signal. + self.abort_controller.signal() + } } diff --git a/components/script/dom/writablestreamdefaultwriter.rs b/components/script/dom/writablestreamdefaultwriter.rs index 60aab9598cc..0bbc620c38d 100644 --- a/components/script/dom/writablestreamdefaultwriter.rs +++ b/components/script/dom/writablestreamdefaultwriter.rs @@ -238,6 +238,7 @@ impl WritableStreamDefaultWriter { cx: SafeJSContext, global: &GlobalScope, reason: SafeHandleValue, + realm: InRealm, can_gc: CanGc, ) -> Rc { // Let stream be writer.[[stream]]. @@ -247,7 +248,7 @@ impl WritableStreamDefaultWriter { }; // Return ! WritableStreamAbort(stream, reason). - stream.abort(cx, global, reason, can_gc) + stream.abort(cx, global, reason, realm, can_gc) } /// @@ -468,7 +469,7 @@ impl WritableStreamDefaultWriterMethods for WritableStream } // Return ! WritableStreamDefaultWriterAbort(this, reason). - self.abort(cx, &global, reason, can_gc) + self.abort(cx, &global, reason, realm, can_gc) } /// diff --git a/components/script_bindings/webidls/WritableStreamDefaultController.webidl b/components/script_bindings/webidls/WritableStreamDefaultController.webidl index 299f1911b33..9802dce0647 100644 --- a/components/script_bindings/webidls/WritableStreamDefaultController.webidl +++ b/components/script_bindings/webidls/WritableStreamDefaultController.webidl @@ -6,6 +6,6 @@ [Exposed=*] interface WritableStreamDefaultController { - // readonly attribute AbortSignal signal; + readonly attribute AbortSignal signal; undefined error(optional any e); }; diff --git a/tests/wpt/meta/streams/piping/pipe-through.any.js.ini b/tests/wpt/meta/streams/piping/pipe-through.any.js.ini index 77cd1734611..a6e3544c039 100644 --- a/tests/wpt/meta/streams/piping/pipe-through.any.js.ini +++ b/tests/wpt/meta/streams/piping/pipe-through.any.js.ini @@ -5,28 +5,6 @@ expected: ERROR [pipe-through.any.worker.html] - expected: ERROR - [pipeThrough should accept a real AbortSignal] - expected: FAIL - - [invalid values of signal should throw; specifically 'null'] - expected: FAIL - - [invalid values of signal should throw; specifically '0'] - expected: FAIL - - [invalid values of signal should throw; specifically 'NaN'] - expected: FAIL - - [invalid values of signal should throw; specifically 'true'] - expected: FAIL - - [invalid values of signal should throw; specifically 'AbortSignal'] - expected: FAIL - - [invalid values of signal should throw; specifically '[object AbortSignal\]'] - expected: FAIL - [pipe-through.any.sharedworker.html] expected: ERROR @@ -45,26 +23,3 @@ [pipe-through.any.shadowrealm-in-sharedworker.html] expected: ERROR - -[pipe-through.any.html] - expected: ERROR - [pipeThrough should accept a real AbortSignal] - expected: FAIL - - [invalid values of signal should throw; specifically 'null'] - expected: FAIL - - [invalid values of signal should throw; specifically '0'] - expected: FAIL - - [invalid values of signal should throw; specifically 'NaN'] - expected: FAIL - - [invalid values of signal should throw; specifically 'true'] - expected: FAIL - - [invalid values of signal should throw; specifically 'AbortSignal'] - expected: FAIL - - [invalid values of signal should throw; specifically '[object AbortSignal\]'] - expected: FAIL diff --git a/tests/wpt/meta/streams/writable-streams/aborting.any.js.ini b/tests/wpt/meta/streams/writable-streams/aborting.any.js.ini index 05f99147ffc..73a6e8c7dba 100644 --- a/tests/wpt/meta/streams/writable-streams/aborting.any.js.ini +++ b/tests/wpt/meta/streams/writable-streams/aborting.any.js.ini @@ -1,69 +1,7 @@ -[aborting.any.html] - [WritableStreamDefaultController.signal] - expected: FAIL - - [the abort signal is signalled synchronously - write] - expected: FAIL - - [the abort signal is signalled synchronously - close] - expected: FAIL - - [the abort signal is not signalled on error] - expected: FAIL - - [the abort signal is not signalled on write failure] - expected: FAIL - - [the abort signal is not signalled on close failure] - expected: FAIL - - [recursive abort() call from abort() aborting signal (not started)] - expected: FAIL - - [recursive abort() call from abort() aborting signal] - expected: FAIL - - [recursive close() call from abort() aborting signal (not started)] - expected: FAIL - - [recursive close() call from abort() aborting signal] - expected: FAIL - - [aborting.https.any.shadowrealm-in-serviceworker.html] expected: ERROR [aborting.any.worker.html] - [WritableStreamDefaultController.signal] - expected: FAIL - - [the abort signal is signalled synchronously - write] - expected: FAIL - - [the abort signal is signalled synchronously - close] - expected: FAIL - - [the abort signal is not signalled on error] - expected: FAIL - - [the abort signal is not signalled on write failure] - expected: FAIL - - [the abort signal is not signalled on close failure] - expected: FAIL - - [recursive abort() call from abort() aborting signal (not started)] - expected: FAIL - - [recursive abort() call from abort() aborting signal] - expected: FAIL - - [recursive close() call from abort() aborting signal (not started)] - expected: FAIL - - [recursive close() call from abort() aborting signal] - expected: FAIL - [aborting.any.shadowrealm-in-dedicatedworker.html] expected: ERROR