From 2f2e962ae00734d75b310d05351ee8261bd791c8 Mon Sep 17 00:00:00 2001 From: gterzian <2792687+gterzian@users.noreply.github.com> Date: Fri, 6 Jun 2025 21:10:31 +0700 Subject: [PATCH] fix setting and propagating of abort reason Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> --- components/script/dom/abortcontroller.rs | 2 +- components/script/dom/abortsignal.rs | 15 ++++++++++-- components/script/dom/readablestream.rs | 31 ++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/components/script/dom/abortcontroller.rs b/components/script/dom/abortcontroller.rs index 0a7afd94fc8..fdd79c51417 100644 --- a/components/script/dom/abortcontroller.rs +++ b/components/script/dom/abortcontroller.rs @@ -26,7 +26,7 @@ impl AbortController { /// fn new_inherited(signal: &AbortSignal) -> AbortController { // Note: continuation of the constructor steps. - + // Set this’s signal to signal. AbortController { reflector_: Reflector::new(), diff --git a/components/script/dom/abortsignal.rs b/components/script/dom/abortsignal.rs index 8d221f6e166..d3685200cdd 100644 --- a/components/script/dom/abortsignal.rs +++ b/components/script/dom/abortsignal.rs @@ -91,7 +91,7 @@ impl AbortSignal { let abort_reason = reason.get(); // Set signal’s abort reason to reason if it is given; - if !abort_reason.is_undefined() { + if !abort_reason.is_null() { self.abort_reason.set(abort_reason); } else { // otherwise to a new "AbortError" DOMException. @@ -133,6 +133,17 @@ impl AbortSignal { ) { match algorithm { AbortAlgorithm::StreamPiping(pipe) => { + // Note: the streams spec says to "If signal is aborted, perform abortAlgorithm", + // which bypasses the signal abort concept, + // and bypasses the steps in that concept that set the default reason + // (see "otherwise to a new "AbortError" DOMException.") + // So we add it here even though it remains unspecified, + // because the `/stream/piping/abort.any.js` test rely on this mechanism. + if self.abort_reason.get().is_undefined() { + rooted!(in(*cx) let mut rooted_error = UndefinedValue()); + Error::Abort.to_jsval(cx, &global, rooted_error.handle_mut(), can_gc); + self.abort_reason.set(rooted_error.get()) + } rooted!(in(*cx) let mut reason = UndefinedValue()); reason.set(self.abort_reason.get()); pipe.abort_with_reason(cx, global, reason.handle(), realm, can_gc); @@ -168,7 +179,7 @@ impl AbortSignal { /// pub(crate) fn aborted(&self) -> bool { // An AbortSignal object is aborted when its abort reason is not undefined. - !self.abort_reason.get().is_undefined() + !self.abort_reason.get().is_null() } } diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 6f8d3835de9..c9e34cc3505 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -182,6 +182,15 @@ impl PipeTo { // Let error be signal’s abort reason. self.abort_reason.set(error.get()); + // Note: setting the error now, + // will result in a rejection of the pipe promise, with this error. + // Unless any shutdown action raise their own error, + // in which case this error will be overwritten by the shutdown action error. + self.shutdown_error.set(error.get()); + + rooted!(in(*cx) let mut error = self.shutdown_error.get()); + assert!(!error.is_undefined()); + // Let actions be an empty ordered set. // Note: the actions are defined, and performed, inside `shutdown_with_an_action`. @@ -329,10 +338,28 @@ impl Callback for PipeTo { // Finalize, passing along error if it was given. if !result.is_undefined() { - // All actions either resolve with undefined, + // Most actions either resolve with undefined, // or reject with an error, // and the error should be used when finalizing. - self.shutdown_error.set(result.get()); + // One exception is the `Abort` action, + // which resolves with a list of undefined values. + rooted!(in(*cx) let object = result.to_object()); + rooted!(in(*cx) let mut done = UndefinedValue()); + let property = unsafe { + get_dictionary_property( + *cx, + object.handle(), + "0", + done.handle_mut(), + can_gc, + ) + }; + if let Ok(false) = property { + // If `result` isn't undefined, or a list, + // then it is an error + // and should overwrite the current shutdown error. + self.shutdown_error.set(result.get()); + } } self.finalize(cx, &global, can_gc); },