Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 97 additions & 11 deletions components/script/dom/abortsignal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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(),
}
}

Expand All @@ -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,
Expand All @@ -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 signal’s dependent signals:
// TODO
// Step 4.1. If dependentSignal is not aborted:
// TODO
// Step 4.1.1. Set dependentSignal’s abort reason to signal’s 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 dependentSignal’s abort reason to signal’s 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>
Expand Down Expand Up @@ -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 resultSignal’s abort reason to signal’s 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 resultSignal’s dependent to true.
result_signal.dependent.set(true);
// Step 4. For each signal of signals:
for signal in signals.iter() {
// Step 4.1. If signal’s dependent is false:
if !signal.dependent.get() {
// Step 4.1.1. Append signal to resultSignal’s source signals.
result_signal
.source_signals
.borrow_mut()
.insert(Dom::from_ref(signal));
// Step 4.1.2. Append resultSignal to signal’s dependent signals.
signal
.dependent_signals
.borrow_mut()
.insert(Dom::from_ref(&result_signal));
} else {
// Step 4.2. Otherwise, for each sourceSignal of signal’s 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 resultSignal’s source signals.
result_signal
.source_signals
.borrow_mut()
.insert(source_signal.clone());
// Step 4.2.3. Append resultSignal to sourceSignal’s 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 {
Expand Down Expand Up @@ -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)
Copy link
Member

@Taym95 Taym95 Sep 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing Any. I was working on this in #36936, so it would be good to check to avoid duplicate work in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I asked @jdm if you folks weren't working on it, as I indeed wanted to avoid duplicate work. He said that you weren't, but I wasn't aware that you were continuing on it in a different ticket.

}

/// <https://dom.spec.whatwg.org/#dom-abortsignal-reason>
fn Reason(&self, _cx: SafeJSContext, mut rval: MutableHandleValue) {
// The reason getter steps are to return this’s abort reason.
Expand Down
Loading
Loading