1#![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#[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
91pub 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 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 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 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
669pub 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}