script/dom/
abortsignal.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::RefCell;
6
7use dom_struct::dom_struct;
8use js::jsapi::{ExceptionStackBehavior, Heap, JS_SetPendingException};
9use js::jsval::{JSVal, UndefinedValue};
10use js::rust::{HandleObject, HandleValue, MutableHandleValue};
11use script_bindings::inheritance::Castable;
12
13use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
14use crate::dom::bindings::error::{Error, ErrorToJsval};
15use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
16use crate::dom::bindings::root::DomRoot;
17use crate::dom::eventtarget::EventTarget;
18use crate::dom::globalscope::GlobalScope;
19use crate::dom::readablestream::PipeTo;
20use crate::realms::InRealm;
21use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
22
23impl js::gc::Rootable for AbortAlgorithm {}
24
25/// <https://dom.spec.whatwg.org/#abortcontroller-api-integration>
26/// TODO: implement algorithms at call point,
27/// in order to integrate the abort signal with its various use cases.
28#[derive(Clone, JSTraceable, MallocSizeOf)]
29#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
30#[allow(dead_code)]
31pub(crate) enum AbortAlgorithm {
32    /// <https://dom.spec.whatwg.org/#add-an-event-listener>
33    DomEventLister,
34    /// <https://streams.spec.whatwg.org/#readable-stream-pipe-to>
35    StreamPiping(PipeTo),
36    /// <https://fetch.spec.whatwg.org/#dom-global-fetch>
37    Fetch,
38}
39
40/// <https://dom.spec.whatwg.org/#abortsignal>
41#[dom_struct]
42pub(crate) struct AbortSignal {
43    eventtarget: EventTarget,
44
45    /// <https://dom.spec.whatwg.org/#abortsignal-abort-reason>
46    #[ignore_malloc_size_of = "mozjs"]
47    abort_reason: Heap<JSVal>,
48
49    /// <https://dom.spec.whatwg.org/#abortsignal-abort-algorithms>
50    abort_algorithms: RefCell<Vec<AbortAlgorithm>>,
51}
52
53impl AbortSignal {
54    fn new_inherited() -> AbortSignal {
55        AbortSignal {
56            eventtarget: EventTarget::new_inherited(),
57            abort_reason: Default::default(),
58            abort_algorithms: Default::default(),
59        }
60    }
61
62    pub(crate) fn new_with_proto(
63        global: &GlobalScope,
64        proto: Option<HandleObject>,
65        can_gc: CanGc,
66    ) -> DomRoot<AbortSignal> {
67        reflect_dom_object_with_proto(
68            Box::new(AbortSignal::new_inherited()),
69            global,
70            proto,
71            can_gc,
72        )
73    }
74
75    /// <https://dom.spec.whatwg.org/#abortsignal-signal-abort>
76    pub(crate) fn signal_abort(
77        &self,
78        cx: SafeJSContext,
79        reason: HandleValue,
80        realm: InRealm,
81        can_gc: CanGc,
82    ) {
83        let global = self.global();
84
85        // If signal is aborted, then return.
86        if self.Aborted() {
87            return;
88        }
89
90        let abort_reason = reason.get();
91
92        // Set signal’s abort reason to reason if it is given;
93        if !abort_reason.is_undefined() {
94            self.abort_reason.set(abort_reason);
95        } else {
96            // otherwise to a new "AbortError" DOMException.
97            rooted!(in(*cx) let mut rooted_error = UndefinedValue());
98            Error::Abort.to_jsval(cx, &global, rooted_error.handle_mut(), can_gc);
99            self.abort_reason.set(rooted_error.get())
100        }
101
102        // Let dependentSignalsToAbort be a new list.
103        // For each dependentSignal of signal’s dependent signals:
104        // TODO: #36936
105
106        // Run the abort steps for signal.
107        self.run_the_abort_steps(cx, &global, realm, can_gc);
108
109        // For each dependentSignal of dependentSignalsToAbort, run the abort steps for dependentSignal.
110        // TODO: #36936
111    }
112
113    /// <https://dom.spec.whatwg.org/#abortsignal-add>
114    pub(crate) fn add(&self, algorithm: &AbortAlgorithm) {
115        // If signal is aborted, then return.
116        if self.aborted() {
117            return;
118        }
119
120        // Append algorithm to signal’s abort algorithms.
121        self.abort_algorithms.borrow_mut().push(algorithm.clone());
122    }
123
124    /// Run a specific abort algorithm.
125    pub(crate) fn run_abort_algorithm(
126        &self,
127        cx: SafeJSContext,
128        global: &GlobalScope,
129        algorithm: &AbortAlgorithm,
130        realm: InRealm,
131        can_gc: CanGc,
132    ) {
133        match algorithm {
134            AbortAlgorithm::StreamPiping(pipe) => {
135                rooted!(in(*cx) let mut reason = UndefinedValue());
136                reason.set(self.abort_reason.get());
137                pipe.abort_with_reason(cx, global, reason.handle(), realm, can_gc);
138            },
139            _ => {
140                // TODO: match on variant and implement algo steps.
141                // See the various items of #34866
142            },
143        }
144    }
145
146    /// <https://dom.spec.whatwg.org/#run-the-abort-steps>
147    fn run_the_abort_steps(
148        &self,
149        cx: SafeJSContext,
150        global: &GlobalScope,
151        realm: InRealm,
152        can_gc: CanGc,
153    ) {
154        // For each algorithm of signal’s abort algorithms: run algorithm.
155        for algo in self.abort_algorithms.borrow().iter() {
156            self.run_abort_algorithm(cx, global, algo, realm, can_gc);
157        }
158
159        // Empty signal’s abort algorithms.
160        self.abort_algorithms.borrow_mut().clear();
161
162        // Fire an event named abort at signal.
163        self.upcast::<EventTarget>()
164            .fire_event(atom!("abort"), can_gc);
165    }
166
167    /// <https://dom.spec.whatwg.org/#abortsignal-aborted>
168    pub(crate) fn aborted(&self) -> bool {
169        // An AbortSignal object is aborted when its abort reason is not undefined.
170        !self.abort_reason.get().is_undefined()
171    }
172}
173
174impl AbortSignalMethods<crate::DomTypeHolder> for AbortSignal {
175    /// <https://dom.spec.whatwg.org/#dom-abortsignal-aborted>
176    fn Aborted(&self) -> bool {
177        // The aborted getter steps are to return true if this is aborted; otherwise false.
178        self.aborted()
179    }
180
181    /// <https://dom.spec.whatwg.org/#dom-abortsignal-abort>
182    fn Abort(
183        cx: SafeJSContext,
184        global: &GlobalScope,
185        reason: HandleValue,
186        can_gc: CanGc,
187    ) -> DomRoot<AbortSignal> {
188        // Let signal be a new AbortSignal object.
189        let signal = AbortSignal::new_with_proto(global, None, can_gc);
190
191        let abort_reason = reason.get();
192        // Set signal’s abort reason to reason if it is given;
193        if !abort_reason.is_undefined() {
194            signal.abort_reason.set(abort_reason);
195        } else {
196            // otherwise to a new "AbortError" DOMException.
197            rooted!(in(*cx) let mut rooted_error = UndefinedValue());
198            Error::Abort.to_jsval(cx, global, rooted_error.handle_mut(), can_gc);
199            signal.abort_reason.set(rooted_error.get())
200        }
201
202        // Return signal.
203        signal
204    }
205
206    /// <https://dom.spec.whatwg.org/#dom-abortsignal-reason>
207    fn Reason(&self, _cx: SafeJSContext, mut rval: MutableHandleValue) {
208        // The reason getter steps are to return this’s abort reason.
209        rval.set(self.abort_reason.get());
210    }
211
212    /// <https://dom.spec.whatwg.org/#dom-abortsignal-throwifaborted>
213    #[allow(unsafe_code)]
214    fn ThrowIfAborted(&self) {
215        // The throwIfAborted() method steps are to throw this’s abort reason, if this is aborted.
216        if self.aborted() {
217            let cx = GlobalScope::get_cx();
218            unsafe {
219                JS_SetPendingException(
220                    *cx,
221                    self.abort_reason.handle(),
222                    ExceptionStackBehavior::Capture,
223                )
224            };
225        }
226    }
227
228    // <https://dom.spec.whatwg.org/#dom-abortsignal-onabort>
229    event_handler!(abort, GetOnabort, SetOnabort);
230}