rqjs_ext/modules/
events.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3#![allow(clippy::mutable_key_type, clippy::for_kv_map)]
4
5use std::sync::{Arc, RwLock};
6
7use rquickjs::{
8    class::{JsClass, OwnedBorrow, Trace, Tracer},
9    function::OnceFn,
10    module::{Declarations, Exports, ModuleDef},
11    prelude::{Func, Opt, Rest, This},
12    Array, Class, Ctx, Exception, Function, Object, Result, String as JsString, Symbol, Undefined,
13    Value,
14};
15
16use crate::modules::exceptions::DOMException;
17
18use crate::utils::{mc_oneshot, result::ResultExt};
19
20// use tracing::trace;
21
22// use crate::{
23//     module_builder::ModuleInfo,
24//     modules::exceptions::DOMException,
25//     utils::{mc_oneshot, result::ResultExt},
26//     vm::{CtxExtension, ErrorExtensions},
27// };
28
29#[derive(Clone, Debug)]
30pub enum EventKey<'js> {
31    Symbol(Symbol<'js>),
32    String(String),
33}
34
35impl<'js> EventKey<'js> {
36    fn from_value(ctx: &Ctx, value: Value<'js>) -> Result<Self> {
37        if value.is_string() {
38            let key: String = value.get()?;
39            Ok(EventKey::String(key))
40        } else {
41            let sym = value.into_symbol().ok_or("Not a symbol").or_throw(ctx)?;
42            Ok(EventKey::Symbol(sym))
43        }
44    }
45}
46
47impl<'js> Eq for EventKey<'js> {}
48
49impl<'js> PartialEq for EventKey<'js> {
50    fn eq(&self, other: &Self) -> bool {
51        match (self, other) {
52            (EventKey::Symbol(symbol1), EventKey::Symbol(symbol2)) => symbol1 == symbol2,
53            (EventKey::String(str1), EventKey::String(str2)) => str1 == str2,
54            _ => false,
55        }
56    }
57}
58
59pub struct EventItem<'js> {
60    callback: Function<'js>,
61    once: bool,
62}
63
64pub type EventList<'js> = Vec<(EventKey<'js>, Vec<EventItem<'js>>)>;
65pub type Events<'js> = Arc<RwLock<EventList<'js>>>;
66
67#[rquickjs::class]
68#[derive(Clone)]
69pub struct EventEmitter<'js> {
70    pub events: Events<'js>,
71}
72
73impl<'js> Emitter<'js> for EventEmitter<'js> {
74    fn get_event_list(&self) -> Arc<RwLock<EventList<'js>>> {
75        self.events.clone()
76    }
77}
78
79impl<'js> Trace<'js> for EventEmitter<'js> {
80    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
81        self.trace_event_emitter(tracer);
82    }
83}
84
85pub trait EmitError<'js> {
86    fn emit_error<C>(self, ctx: &Ctx<'js>, this: Class<'js, C>) -> Result<bool>
87    where
88        C: Emitter<'js>;
89}
90
91// impl<'js, T> EmitError<'js> for Result<T> {
92//     fn emit_error<C>(self, ctx: &Ctx<'js>, this: Class<'js, C>) -> Result<bool>
93//     where
94//         C: Emitter<'js>,
95//     {
96//         if let Err(err) = self.catch(ctx) {
97//             if this.borrow().has_listener_str("error") {
98//                 let error_value = err.into_value(ctx)?;
99//                 C::emit_str(This(this), ctx, "error", vec![error_value], false)?;
100//                 return Ok(true);
101//             }
102//             return Err(err.throw(ctx));
103//         }
104//         Ok(false)
105//     }
106// }
107
108pub trait Emitter<'js>
109where
110    Self: JsClass<'js> + Sized + 'js,
111{
112    fn get_event_list(&self) -> Arc<RwLock<EventList<'js>>>;
113
114    fn on_event_changed(&mut self, _event: EventKey<'js>, _added: bool) -> Result<()> {
115        Ok(())
116    }
117
118    fn add_event_emitter_prototype(ctx: &Ctx<'js>) -> Result<Object<'js>> {
119        let proto = Class::<Self>::prototype(ctx.clone())
120            .or_throw_msg(ctx, "Prototype for EventEmitter not found")?;
121
122        let on = Function::new(ctx.clone(), Self::on)?;
123        let off = Function::new(ctx.clone(), Self::remove_event_listener)?;
124
125        proto.set("once", Func::from(Self::once))?;
126
127        proto.set("on", on.clone())?;
128
129        proto.set("emit", Func::from(Self::emit))?;
130
131        proto.set("prependListener", Func::from(Self::prepend_listener))?;
132
133        proto.set(
134            "prependOnceListener",
135            Func::from(Self::prepend_once_listener),
136        )?;
137
138        proto.set("off", off.clone())?;
139
140        proto.set("eventNames", Func::from(Self::event_names))?;
141
142        proto.set("addListener", on)?;
143
144        proto.set("removeListener", off)?;
145
146        Ok(proto)
147    }
148
149    fn trace_event_emitter<'a>(&self, tracer: Tracer<'a, 'js>) {
150        let events = self.get_event_list();
151        let events = events.read().unwrap();
152        for (key, items) in events.iter() {
153            if let EventKey::Symbol(sym) = &key {
154                tracer.mark(sym);
155            }
156
157            for item in items {
158                tracer.mark(&item.callback);
159            }
160        }
161    }
162
163    fn remove_event_listener(
164        this: This<Class<'js, Self>>,
165        ctx: Ctx<'js>,
166        event: Value<'js>,
167        listener: Function<'js>,
168    ) -> Result<Class<'js, Self>> {
169        let events = this.clone().borrow().get_event_list();
170        let mut events = events.write().or_throw(&ctx)?;
171
172        let key = EventKey::from_value(&ctx, event)?;
173        if let Some(index) = events.iter_mut().position(|(k, _)| k == &key) {
174            let items = &mut events[index].1;
175            if let Some(pos) = items.iter().position(|item| item.callback == listener) {
176                items.remove(pos);
177                if items.is_empty() {
178                    events.remove(index);
179                }
180            }
181        };
182
183        Ok(this.0)
184    }
185
186    fn add_event_listener_str(
187        this: This<Class<'js, Self>>,
188        ctx: &Ctx<'js>,
189        event: &str,
190        listener: Function<'js>,
191        prepend: bool,
192        once: bool,
193    ) -> Result<Class<'js, Self>> {
194        let event = to_event(ctx, event)?;
195        Self::add_event_listener(this, ctx.clone(), event, listener, prepend, once)
196    }
197
198    fn once(
199        this: This<Class<'js, Self>>,
200        ctx: Ctx<'js>,
201        event: Value<'js>,
202        listener: Function<'js>,
203    ) -> Result<Class<'js, Self>> {
204        Self::add_event_listener(this, ctx, event, listener, false, true)
205    }
206
207    fn on(
208        this: This<Class<'js, Self>>,
209        ctx: Ctx<'js>,
210        event: Value<'js>,
211        listener: Function<'js>,
212    ) -> Result<Class<'js, Self>> {
213        Self::add_event_listener(this, ctx, event, listener, false, false)
214    }
215
216    fn prepend_listener(
217        this: This<Class<'js, Self>>,
218        ctx: Ctx<'js>,
219        event: Value<'js>,
220        listener: Function<'js>,
221    ) -> Result<Class<'js, Self>> {
222        Self::add_event_listener(this, ctx, event, listener, true, false)
223    }
224
225    fn prepend_once_listener(
226        this: This<Class<'js, Self>>,
227        ctx: Ctx<'js>,
228        event: Value<'js>,
229        listener: Function<'js>,
230    ) -> Result<Class<'js, Self>> {
231        Self::add_event_listener(this, ctx, event, listener, true, true)
232    }
233
234    fn add_event_listener(
235        this: This<Class<'js, Self>>,
236        ctx: Ctx<'js>,
237        event: Value<'js>,
238        listener: Function<'js>,
239        prepend: bool,
240        once: bool,
241    ) -> Result<Class<'js, Self>> {
242        let this2 = this.clone();
243        let events = &this2.borrow().get_event_list();
244        let mut events = events.write().or_throw(&ctx)?;
245        let key = EventKey::from_value(&ctx, event)?;
246        let mut is_new = false;
247
248        let items = match events.iter_mut().find(|(k, _)| k == &key) {
249            Some((_, entry_items)) => entry_items,
250            None => {
251                let new_items = Vec::new();
252                is_new = true;
253                events.push((key.clone(), new_items));
254                &mut events.last_mut().unwrap().1
255            }
256        };
257
258        let item = EventItem {
259            callback: listener,
260            once,
261        };
262        if !prepend {
263            items.push(item);
264        } else {
265            items.insert(0, item);
266        }
267        if is_new {
268            this2.borrow_mut().on_event_changed(key, true)?
269        }
270        Ok(this.0)
271    }
272
273    fn has_listener_str(&self, event: &str) -> bool {
274        let key = EventKey::String(String::from(event));
275        has_key(self.get_event_list(), key)
276    }
277
278    #[allow(dead_code)]
279    fn has_listener(&self, ctx: Ctx<'js>, event: Value<'js>) -> Result<bool> {
280        let key = EventKey::from_value(&ctx, event)?;
281        Ok(has_key(self.get_event_list(), key))
282    }
283
284    #[allow(dead_code)]
285    fn get_listeners(&self, ctx: &Ctx<'js>, event: Value<'js>) -> Result<Vec<Function<'js>>> {
286        let key = EventKey::from_value(ctx, event)?;
287        Ok(find_all_listeners(self.get_event_list(), key))
288    }
289
290    fn get_listeners_str(&self, event: &str) -> Vec<Function<'js>> {
291        let key = EventKey::String(String::from(event));
292        find_all_listeners(self.get_event_list(), key)
293    }
294
295    fn do_emit(
296        event: Value<'js>,
297        this: This<Class<'js, Self>>,
298        ctx: &Ctx<'js>,
299        args: Rest<Value<'js>>,
300        defer: bool,
301    ) -> Result<()> {
302        // trace!("Emitting: {:?}", event);
303        let this2 = this.clone();
304        let events = &this2.borrow().get_event_list();
305        let mut events = events.write().or_throw(ctx)?;
306        let key = EventKey::from_value(ctx, event)?;
307
308        if let Some(index) = events.iter_mut().position(|(k, _)| k == &key) {
309            let items = &mut events[index].1;
310            let mut callbacks = Vec::with_capacity(items.len());
311            items.retain(|item: &EventItem<'_>| {
312                callbacks.push(item.callback.clone());
313                !item.once
314            });
315            if items.is_empty() {
316                events.remove(index);
317                this.borrow_mut().on_event_changed(key, false)?;
318            }
319            drop(events);
320            for callback in callbacks {
321                let args = args.iter().map(|arg| arg.to_owned()).collect();
322                let args = Rest(args);
323                let this = This(this.clone());
324                if defer {
325                    callback.defer((this, args))?;
326                } else {
327                    callback.call::<_, ()>((this, args))?;
328                }
329            }
330        }
331
332        Ok(())
333    }
334
335    fn emit_str(
336        this: This<Class<'js, Self>>,
337        ctx: &Ctx<'js>,
338        event: &str,
339        args: Vec<Value<'js>>,
340        defer: bool,
341    ) -> Result<()> {
342        let event = to_event(ctx, event)?;
343        Self::do_emit(event, this, ctx, args.into(), defer)
344    }
345
346    fn emit(
347        this: This<Class<'js, Self>>,
348        ctx: Ctx<'js>,
349        event: Value<'js>,
350        args: Rest<Value<'js>>,
351    ) -> Result<()> {
352        Self::do_emit(event, this, &ctx, args, false)
353    }
354
355    fn event_names(this: This<OwnedBorrow<'js, Self>>, ctx: Ctx<'js>) -> Result<Vec<Value<'js>>> {
356        let events = this.get_event_list();
357        let events = events.read().or_throw(&ctx)?;
358
359        let mut names = Vec::with_capacity(events.len());
360        for (key, _entry) in events.iter() {
361            let value = match key {
362                EventKey::Symbol(symbol) => symbol.clone().into_value(),
363                EventKey::String(str) => JsString::from_str(ctx.clone(), str)?.into(),
364            };
365
366            names.push(value)
367        }
368
369        Ok(names)
370    }
371}
372
373fn find_all_listeners<'js>(
374    events: Arc<RwLock<EventList<'js>>>,
375    key: EventKey<'js>,
376) -> Vec<Function<'js>> {
377    let events = events.read().unwrap();
378    let items = events.iter().find(|(k, _)| k == &key);
379    if let Some((_, callbacks)) = items {
380        callbacks.iter().map(|item| item.callback.clone()).collect()
381    } else {
382        vec![]
383    }
384}
385
386fn has_key<'js>(event_list: Arc<RwLock<EventList<'js>>>, key: EventKey<'js>) -> bool {
387    event_list.read().unwrap().iter().any(|(k, _)| k == &key)
388}
389
390fn to_event<'js>(ctx: &Ctx<'js>, event: &str) -> Result<Value<'js>> {
391    let event = JsString::from_str(ctx.clone(), event)?;
392    Ok(event.into_value())
393}
394
395impl<'js> Default for EventEmitter<'js> {
396    fn default() -> Self {
397        Self::new()
398    }
399}
400
401#[rquickjs::methods]
402impl<'js> EventEmitter<'js> {
403    #[qjs(constructor)]
404    pub fn new() -> Self {
405        Self {
406            #[allow(clippy::arc_with_non_send_sync)]
407            events: Arc::new(RwLock::new(Vec::new())),
408        }
409    }
410}
411
412#[rquickjs::class]
413#[derive(rquickjs::class::Trace)]
414pub struct AbortController<'js> {
415    signal: Class<'js, AbortSignal<'js>>,
416}
417
418#[rquickjs::methods]
419impl<'js> AbortController<'js> {
420    #[qjs(constructor)]
421    pub fn new(ctx: Ctx<'js>) -> Result<Self> {
422        let signal = AbortSignal::new();
423
424        let abort_controller = Self {
425            signal: Class::instance(ctx, signal)?,
426        };
427        Ok(abort_controller)
428    }
429
430    #[qjs(get)]
431    pub fn signal(&self) -> Class<'js, AbortSignal<'js>> {
432        self.signal.clone()
433    }
434
435    pub fn abort(
436        ctx: Ctx<'js>,
437        this: This<Class<'js, Self>>,
438        reason: Opt<Value<'js>>,
439    ) -> Result<()> {
440        let instance = this.0.borrow();
441        let signal = instance.signal.clone();
442        let mut signal_borrow = signal.borrow_mut();
443        if signal_borrow.aborted {
444            //only once
445            return Ok(());
446        }
447        signal_borrow.set_reason(reason);
448        drop(signal_borrow);
449        AbortSignal::send_aborted(This(signal), ctx)?;
450
451        Ok(())
452    }
453}
454
455fn get_reason_or_dom_exception<'js>(
456    ctx: &Ctx<'js>,
457    reason: Option<&Value<'js>>,
458    name: &str,
459) -> Result<Value<'js>> {
460    let reason = if let Some(reason) = reason {
461        reason.clone()
462    } else {
463        let ex = DOMException::new(ctx.clone(), Opt(None), Opt(Some(name.into())))?;
464        Class::instance(ctx.clone(), ex)?.into_value()
465    };
466    Ok(reason)
467}
468
469#[derive(Clone)]
470#[rquickjs::class]
471pub struct AbortSignal<'js> {
472    emitter: EventEmitter<'js>,
473    aborted: bool,
474    reason: Option<Value<'js>>,
475    pub sender: mc_oneshot::Sender<Value<'js>>,
476}
477
478impl<'js> Trace<'js> for AbortSignal<'js> {
479    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
480        if let Some(reason) = &self.reason {
481            tracer.mark(reason);
482        }
483    }
484}
485
486impl<'js> Emitter<'js> for AbortSignal<'js> {
487    fn get_event_list(&self) -> Arc<RwLock<EventList<'js>>> {
488        self.emitter.get_event_list()
489    }
490}
491
492impl<'js> Default for AbortSignal<'js> {
493    fn default() -> Self {
494        Self::new()
495    }
496}
497
498#[rquickjs::methods(rename_all = "camelCase")]
499impl<'js> AbortSignal<'js> {
500    #[qjs(constructor)]
501    pub fn new() -> Self {
502        let (sender, _) = mc_oneshot::channel::<Value<'js>>();
503        Self {
504            emitter: EventEmitter::new(),
505            aborted: false,
506            reason: None,
507            sender,
508        }
509    }
510
511    #[qjs(get, rename = "onabort")]
512    pub fn get_on_abort(&self) -> Option<Function<'js>> {
513        Self::get_listeners_str(self, "abort").first().cloned()
514    }
515
516    #[qjs(set, rename = "onabort")]
517    pub fn set_on_abort(
518        this: This<Class<'js, Self>>,
519        ctx: Ctx<'js>,
520        listener: Function<'js>,
521    ) -> Result<()> {
522        Self::add_event_listener_str(this, &ctx, "abort", listener, false, false)?;
523        Ok(())
524    }
525
526    pub fn throw_if_aborted(&self, ctx: Ctx<'js>) -> Result<()> {
527        if self.aborted {
528            return Err(ctx.throw(
529                self.reason
530                    .clone()
531                    .unwrap_or_else(|| Undefined.into_value(ctx.clone())),
532            ));
533        }
534        Ok(())
535    }
536
537    #[qjs(static)]
538    pub fn any(ctx: Ctx<'js>, signals: Array<'js>) -> Result<Class<'js, Self>> {
539        let mut new_signal = AbortSignal::new();
540
541        let mut signal_instances = Vec::with_capacity(signals.len());
542
543        for signal in signals.iter() {
544            let signal: Value = signal?;
545            let signal: Class<AbortSignal> = Class::from_value(&signal)
546                .map_err(|_| Exception::throw_type(&ctx, "Value is not an AbortSignal instance"))?;
547            let signal_borrow = signal.borrow();
548            if signal_borrow.aborted {
549                new_signal.aborted = true;
550                new_signal.reason.clone_from(&signal_borrow.reason);
551                let new_signal = Class::instance(ctx, new_signal)?;
552                return Ok(new_signal);
553            } else {
554                drop(signal_borrow);
555                signal_instances.push(signal);
556            }
557        }
558
559        let new_signal_instance = Class::instance(ctx.clone(), new_signal)?;
560        for signal in signal_instances {
561            let signal_instance_2 = new_signal_instance.clone();
562            Self::add_event_listener_str(
563                This(signal),
564                &ctx,
565                "abort",
566                Function::new(
567                    ctx.clone(),
568                    OnceFn::from(|ctx, signal| {
569                        struct Args<'js>(Ctx<'js>, This<Class<'js, AbortSignal<'js>>>);
570                        let Args(ctx, signal) = Args(ctx, signal);
571                        let mut borrow = signal_instance_2.borrow_mut();
572                        borrow.aborted = true;
573                        borrow.reason.clone_from(&signal.borrow().reason);
574                        drop(borrow);
575                        Self::send_aborted(This(signal_instance_2), ctx)
576                    }),
577                )?,
578                false,
579                true,
580            )?;
581        }
582
583        Ok(new_signal_instance)
584    }
585
586    #[qjs(get)]
587    pub fn aborted(&self) -> bool {
588        self.aborted
589    }
590
591    #[qjs(get)]
592    pub fn reason(&self) -> Option<Value<'js>> {
593        self.reason.clone()
594    }
595
596    #[qjs(set, rename = "reason")]
597    pub fn set_reason(&mut self, reason: Opt<Value<'js>>) {
598        if let Some(new_reason) = reason.0 {
599            self.reason.replace(new_reason);
600        } else {
601            self.reason.take();
602        }
603    }
604
605    #[qjs(skip)]
606    pub fn send_aborted(this: This<Class<'js, Self>>, ctx: Ctx<'js>) -> Result<()> {
607        let mut borrow = this.borrow_mut();
608        borrow.aborted = true;
609        let reason = get_reason_or_dom_exception(&ctx, borrow.reason.as_ref(), "AbortError")?;
610        borrow.sender.send(reason);
611        drop(borrow);
612        Self::emit_str(this, &ctx, "abort", vec![], false)?;
613        Ok(())
614    }
615
616    #[qjs(static)]
617    pub fn abort(ctx: Ctx<'js>, reason: Opt<Value<'js>>) -> Result<Class<'js, Self>> {
618        let mut signal = Self::new();
619        signal.set_reason(reason);
620        let instance = Class::instance(ctx.clone(), signal)?;
621        Self::send_aborted(This(instance.clone()), ctx)?;
622        Ok(instance)
623    }
624
625    #[qjs(static)]
626    pub fn timeout(ctx: Ctx<'js>, milliseconds: u64) -> Result<Class<'js, Self>> {
627        let timeout_error = get_reason_or_dom_exception(&ctx, None, "TimeoutError")?;
628
629        let signal = Self::new();
630        let signal_instance = Class::instance(ctx.clone(), signal)?;
631        let signal_instance2 = signal_instance.clone();
632
633        // ctx.clone().spawn_exit(async move {
634        //     tokio::time::sleep(Duration::from_millis(milliseconds)).await;
635        //     let mut borrow = signal_instance.borrow_mut();
636        //     borrow.set_reason(Opt(Some(timeout_error)));
637        //     drop(borrow);
638        //     Self::send_aborted(This(signal_instance), ctx)?;
639        //     Ok(())
640        // })?;
641
642        Ok(signal_instance2)
643    }
644}
645
646pub struct EventsModule;
647
648impl ModuleDef for EventsModule {
649    fn declare(declare: &Declarations<'_>) -> Result<()> {
650        declare.declare(stringify!(EventEmitter))?;
651        declare.declare("default")?;
652
653        Ok(())
654    }
655
656    fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
657        let ctor = Class::<EventEmitter>::create_constructor(ctx)?
658            .expect("Can't create EventEmitter constructor");
659        ctor.set(stringify!(EventEmitter), ctor.clone())?;
660        exports.export(stringify!(EventEmitter), ctor.clone())?;
661        exports.export("default", ctor)?;
662
663        EventEmitter::add_event_emitter_prototype(ctx)?;
664
665        Ok(())
666    }
667}
668
669// impl From<EventsModule> for ModuleInfo<EventsModule> {
670//     fn from(val: EventsModule) -> Self {
671//         ModuleInfo {
672//             name: "events",
673//             module: val,
674//         }
675//     }
676// }
677
678pub fn init(ctx: &Ctx<'_>) -> Result<()> {
679    let globals = ctx.globals();
680
681    Class::<AbortController>::define(&globals)?;
682    Class::<AbortSignal>::define(&globals)?;
683
684    AbortSignal::add_event_emitter_prototype(ctx)?;
685
686    Ok(())
687}