Add signal to request (#39290)

The signal taken from the requestinit is now passed into
the request object with the relevant steps. I added all
spec comments to this method, as I had trouble figuring
out which steps I had to add.

This required implementing the algorithm to create
dependent signals, which is used in the `any()` method.
So that's now implemented as well.

All of that required the machinery to have dependent and
source signals on an AbortSignal. It uses an IndexSet
as the spec requires it to be an ordered set.

Part of #34866

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-09-16 22:41:12 +02:00 committed by GitHub
parent 1898a740a8
commit 22fbb3458b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 278 additions and 266 deletions

View file

@ -2,18 +2,21 @@
* 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::cell::{Cell, RefCell};
use dom_struct::dom_struct;
use indexmap::IndexSet;
use js::jsapi::{ExceptionStackBehavior, Heap, JS_SetPendingException};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::{HandleObject, HandleValue, MutableHandleValue};
use script_bindings::inheritance::Castable;
use script_bindings::trace::CustomTraceable;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
use crate::dom::bindings::error::{Error, ErrorToJsval};
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::readablestream::PipeTo;
@ -48,6 +51,15 @@ pub(crate) struct AbortSignal {
/// <https://dom.spec.whatwg.org/#abortsignal-abort-algorithms>
abort_algorithms: RefCell<Vec<AbortAlgorithm>>,
/// <https://dom.spec.whatwg.org/#abortsignal-dependent>
dependent: Cell<bool>,
/// <https://dom.spec.whatwg.org/#abortsignal-source-signals>
source_signals: DomRefCell<IndexSet<Dom<AbortSignal>>>,
/// <https://dom.spec.whatwg.org/#abortsignal-dependent-signals>
dependent_signals: DomRefCell<IndexSet<Dom<AbortSignal>>>,
}
impl AbortSignal {
@ -56,6 +68,9 @@ impl AbortSignal {
eventtarget: EventTarget::new_inherited(),
abort_reason: Default::default(),
abort_algorithms: Default::default(),
dependent: Default::default(),
source_signals: Default::default(),
dependent_signals: Default::default(),
}
}
@ -73,6 +88,7 @@ impl AbortSignal {
}
/// <https://dom.spec.whatwg.org/#abortsignal-signal-abort>
#[cfg_attr(crown, allow(crown::unrooted_must_root))] // TODO(39333): Remove when all iterators are marked as safe
pub(crate) fn signal_abort(
&self,
cx: SafeJSContext,
@ -99,21 +115,25 @@ impl AbortSignal {
}
// Step 3. Let dependentSignalsToAbort be a new list.
// TODO
let mut dependent_signals_to_abort = vec![];
// Step 4. For each dependentSignal of signals dependent signals:
// TODO
// Step 4.1. If dependentSignal is not aborted:
// TODO
// Step 4.1.1. Set dependentSignals abort reason to signals abort reason.
// TODO
// Step 4.1.2. Append dependentSignal to dependentSignalsToAbort.
// TODO
for dependent_signal in self.dependent_signals.borrow().iter() {
// Step 4.1. If dependentSignal is not aborted:
if !dependent_signal.aborted() {
// Step 4.1.1. Set dependentSignals abort reason to signals abort reason.
dependent_signal.abort_reason.set(self.abort_reason.get());
// Step 4.1.2. Append dependentSignal to dependentSignalsToAbort.
dependent_signals_to_abort.push(dependent_signal.as_rooted());
}
}
// Step 5. Run the abort steps for signal.
self.run_the_abort_steps(cx, &global, realm, can_gc);
// Step 6. For each dependentSignal of dependentSignalsToAbort, run the abort steps for dependentSignal.
// TODO
for dependent_signal in dependent_signals_to_abort.iter() {
dependent_signal.run_the_abort_steps(cx, &global, realm, can_gc);
}
}
/// <https://dom.spec.whatwg.org/#abortsignal-add>
@ -174,6 +194,61 @@ impl AbortSignal {
// An AbortSignal object is aborted when its abort reason is not undefined.
!self.abort_reason.get().is_undefined()
}
/// <https://dom.spec.whatwg.org/#create-a-dependent-abort-signal>
#[cfg_attr(crown, allow(crown::unrooted_must_root))] // TODO(39333): Remove when all iterators are marked as safe
pub(crate) fn create_dependent_abort_signal(
signals: Vec<DomRoot<AbortSignal>>,
global: &GlobalScope,
can_gc: CanGc,
) -> DomRoot<AbortSignal> {
// Step 1. Let resultSignal be a new object implementing signalInterface using realm.
let result_signal = Self::new_with_proto(global, None, can_gc);
// Step 2. For each signal of signals: if signal is aborted,
// then set resultSignals abort reason to signals abort reason and return resultSignal.
for signal in signals.iter() {
if signal.aborted() {
result_signal.abort_reason.set(signal.abort_reason.get());
return result_signal;
}
}
// Step 3. Set resultSignals dependent to true.
result_signal.dependent.set(true);
// Step 4. For each signal of signals:
for signal in signals.iter() {
// Step 4.1. If signals dependent is false:
if !signal.dependent.get() {
// Step 4.1.1. Append signal to resultSignals source signals.
result_signal
.source_signals
.borrow_mut()
.insert(Dom::from_ref(signal));
// Step 4.1.2. Append resultSignal to signals dependent signals.
signal
.dependent_signals
.borrow_mut()
.insert(Dom::from_ref(&result_signal));
} else {
// Step 4.2. Otherwise, for each sourceSignal of signals source signals:
for source_signal in signal.source_signals.borrow().iter() {
// Step 4.2.1. Assert: sourceSignal is not aborted and not dependent.
assert!(!source_signal.aborted() && !source_signal.dependent.get());
// Step 4.2.2. Append sourceSignal to resultSignals source signals.
result_signal
.source_signals
.borrow_mut()
.insert(source_signal.clone());
// Step 4.2.3. Append resultSignal to sourceSignals dependent signals.
source_signal
.dependent_signals
.borrow_mut()
.insert(Dom::from_ref(&result_signal));
}
}
}
// Step 5. Return resultSignal.
result_signal
}
}
impl AbortSignalMethods<crate::DomTypeHolder> for AbortSignal {
@ -208,6 +283,17 @@ impl AbortSignalMethods<crate::DomTypeHolder> for AbortSignal {
signal
}
/// <https://dom.spec.whatwg.org/#dom-abortsignal-any>
fn Any(
global: &GlobalScope,
signals: Vec<DomRoot<AbortSignal>>,
can_gc: CanGc,
) -> DomRoot<AbortSignal> {
// The static any(signals) method steps are to return the result
// of creating a dependent abort signal from signals using AbortSignal and the current realm.
Self::create_dependent_abort_signal(signals, global, can_gc)
}
/// <https://dom.spec.whatwg.org/#dom-abortsignal-reason>
fn Reason(&self, _cx: SafeJSContext, mut rval: MutableHandleValue) {
// The reason getter steps are to return thiss abort reason.