1#![allow(
4 clippy::mutable_key_type,
5 clippy::for_kv_map,
6 clippy::new_without_default
7)]
8use std::{
9 rc::Rc,
10 sync::{Arc, RwLock},
11};
12
13use crate::utils::{
14 error::ErrorExtensions, module::ModuleInfo, object::ObjectExt, result::ResultExt,
15};
16use rquickjs::{
17 class::{JsClass, Trace, Tracer},
18 module::{Declarations, Exports, ModuleDef},
19 prelude::{Func, Opt, Rest, This},
20 CatchResultExt, Class, Ctx, Function, JsLifetime, Object, Result, String as JsString, Symbol,
21 Value,
22};
23use tracing::trace;
24
25use self::{custom_event::CustomEvent, event::Event, event_target::EventTarget};
26
27pub mod custom_event;
28pub mod event;
29pub mod event_target;
30
31#[derive(Clone, Debug)]
32pub enum EventKey<'js> {
33 Symbol(Symbol<'js>),
34 String(Rc<str>),
35}
36
37impl<'js> EventKey<'js> {
38 fn from_value(ctx: &Ctx, value: Value<'js>) -> Result<Self> {
39 if value.is_string() {
40 let key: String = value.get()?;
41 Ok(EventKey::String(key.into()))
42 } else {
43 let sym = value.into_symbol().ok_or("Not a symbol").or_throw(ctx)?;
44 Ok(EventKey::Symbol(sym))
45 }
46 }
47}
48
49impl Eq for EventKey<'_> {}
50
51impl PartialEq for EventKey<'_> {
52 fn eq(&self, other: &Self) -> bool {
53 match (self, other) {
54 (EventKey::Symbol(symbol1), EventKey::Symbol(symbol2)) => symbol1 == symbol2,
55 (EventKey::String(str1), EventKey::String(str2)) => str1 == str2,
56 _ => false,
57 }
58 }
59}
60
61pub struct EventItem<'js> {
62 callback: Function<'js>,
63 once: bool,
64}
65
66pub type EventList<'js> = Vec<(EventKey<'js>, Vec<EventItem<'js>>)>;
67pub type Events<'js> = Arc<RwLock<EventList<'js>>>;
68
69fn events_symbol<'js>(ctx: &Ctx<'js>) -> Result<Symbol<'js>> {
71 Symbol::new_global(ctx.clone(), "__ee")
72}
73
74fn class_to_obj<'js, C: JsClass<'js>>(class: Class<'js, C>) -> Result<Object<'js>> {
76 Object::from_value(class.into_value())
77}
78
79#[allow(clippy::arc_with_non_send_sync)]
83pub fn resolve_events<'js>(ctx: &Ctx<'js>, obj: &Object<'js>) -> Result<Events<'js>> {
84 if let Some(class) = Class::<EventEmitter>::from_object(obj) {
86 return Ok(class.borrow().events.clone());
87 }
88 let sym = events_symbol(ctx)?;
89 if let Some(ee) = obj.get::<_, Option<Class<'js, EventEmitter<'js>>>>(sym.clone())? {
91 return Ok(ee.borrow().events.clone());
92 }
93 let events: Events<'js> = Arc::new(RwLock::new(Vec::new()));
95 let ee = Class::instance(
96 ctx.clone(),
97 EventEmitter {
98 events: events.clone(),
99 },
100 )?;
101 obj.set(sym, ee)?;
102 Ok(events)
103}
104
105#[rquickjs::class]
106#[derive(Clone)]
107pub struct EventEmitter<'js> {
108 pub events: Events<'js>,
109}
110
111unsafe impl<'js> JsLifetime<'js> for EventEmitter<'js> {
112 type Changed<'to> = EventEmitter<'to>;
113}
114
115impl<'js> Emitter<'js> for EventEmitter<'js> {
116 fn get_event_list(&self) -> Arc<RwLock<EventList<'js>>> {
117 self.events.clone()
118 }
119}
120
121impl<'js> Trace<'js> for EventEmitter<'js> {
122 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
123 self.trace_event_emitter(tracer);
124 }
125}
126
127#[rquickjs::methods]
128impl<'js> EventEmitter<'js> {
129 #[qjs(constructor)]
130 pub fn new() -> Self {
131 Self {
132 #[allow(clippy::arc_with_non_send_sync)]
133 events: Arc::new(RwLock::new(Vec::new())),
134 }
135 }
136}
137
138pub trait EmitError<'js> {
139 fn emit_error<C>(self, id: &'static str, ctx: &Ctx<'js>, this: Class<'js, C>) -> Result<bool>
140 where
141 C: Emitter<'js>;
142}
143
144impl<'js, T> EmitError<'js> for Result<T> {
145 fn emit_error<C>(self, id: &'static str, ctx: &Ctx<'js>, this: Class<'js, C>) -> Result<bool>
146 where
147 C: Emitter<'js>,
148 {
149 if let Err(err) = self.catch(ctx) {
150 trace!("Error caught in: {}", id);
151 if this.borrow().has_listener_str("error") {
152 let error_value = err.into_value(ctx)?;
153 C::emit_str(this, ctx, "error", vec![error_value], false)?;
154 return Ok(true);
155 }
156 return Err(err.throw(ctx));
157 }
158 Ok(false)
159 }
160}
161
162pub trait Emitter<'js>
163where
164 Self: JsClass<'js> + Sized + 'js,
165{
166 fn get_event_list(&self) -> Arc<RwLock<EventList<'js>>>;
167
168 fn on_event_changed(&mut self, _event: EventKey<'js>, _added: bool) -> Result<()> {
169 Ok(())
170 }
171
172 fn resolve_events_from(ctx: &Ctx<'js>, this: &Object<'js>) -> Result<Events<'js>> {
175 if let Some(class) = Class::<Self>::from_object(this) {
176 return Ok(class.borrow().get_event_list());
177 }
178 resolve_events(ctx, this)
179 }
180
181 fn add_event_emitter_prototype(ctx: &Ctx<'js>) -> Result<Object<'js>> {
182 let proto = Class::<Self>::prototype(ctx)?
183 .or_throw_msg(ctx, "Prototype for EventEmitter not found")?;
184
185 let on = Function::new(ctx.clone(), Self::on)?;
186 let off = Function::new(ctx.clone(), Self::remove_event_listener)?;
187
188 proto.set("once", Func::from(Self::once))?;
189 proto.set("on", on.clone())?;
190 proto.set("emit", Func::from(Self::emit))?;
191 proto.set("prependListener", Func::from(Self::prepend_listener))?;
192 proto.set(
193 "prependOnceListener",
194 Func::from(Self::prepend_once_listener),
195 )?;
196 proto.set("off", off.clone())?;
197 proto.set("eventNames", Func::from(Self::event_names))?;
198 proto.set("addListener", on)?;
199 proto.set("removeListener", off)?;
200 proto.set("listenerCount", Func::from(Self::listener_count))?;
201 proto.set("removeAllListeners", Func::from(Self::remove_all_listeners))?;
202
203 Ok(proto)
204 }
205
206 fn add_event_target_prototype(ctx: &Ctx<'js>) -> Result<Object<'js>> {
207 let proto = Class::<Self>::prototype(ctx)?
208 .or_throw_msg(ctx, "Prototype for EventTarget not found")?;
209
210 let on = Function::new(ctx.clone(), Self::evt_add_event_listener)?;
211 let off = Function::new(ctx.clone(), Self::remove_event_listener)?;
212
213 proto.set("dispatchEvent", Func::from(Self::evt_dispatch_event))?;
214 proto.set("addEventListener", on)?;
215 proto.set("removeEventListener", off)?;
216
217 Ok(proto)
218 }
219
220 fn trace_event_emitter<'a>(&self, tracer: Tracer<'a, 'js>) {
221 let events = self.get_event_list();
222 let events = events.read().unwrap();
223 for (key, items) in events.iter() {
224 if let EventKey::Symbol(sym) = &key {
225 tracer.mark(sym);
226 }
227
228 for item in items {
229 tracer.mark(&item.callback);
230 }
231 }
232 }
233
234 fn remove_event_listener_str(
235 this: Class<'js, Self>,
236 ctx: &Ctx<'js>,
237 event: &str,
238 listener: Function<'js>,
239 ) -> Result<Object<'js>> {
240 let event = to_event(ctx, event)?;
241 Self::remove_event_listener(This(class_to_obj(this)?), ctx.clone(), event, listener)
242 }
243
244 fn remove_event_listener(
245 this: This<Object<'js>>,
246 ctx: Ctx<'js>,
247 event: Value<'js>,
248 listener: Function<'js>,
249 ) -> Result<Object<'js>> {
250 let events = Self::resolve_events_from(&ctx, &this)?;
251 let mut events = events.write().or_throw(&ctx)?;
252
253 let key = EventKey::from_value(&ctx, event)?;
254 if let Some(index) = events.iter_mut().position(|(k, _)| k == &key) {
255 let items = &mut events[index].1;
256 if let Some(pos) = items.iter().position(|item| item.callback == listener) {
257 items.remove(pos);
258 if items.is_empty() {
259 events.remove(index);
260 }
261 }
262 };
263
264 Ok(this.0)
265 }
266
267 fn add_event_listener_str(
268 this: Class<'js, Self>,
269 ctx: &Ctx<'js>,
270 event: &str,
271 listener: Function<'js>,
272 prepend: bool,
273 once: bool,
274 ) -> Result<Object<'js>> {
275 let event = to_event(ctx, event)?;
276 Self::add_event_listener(
277 This(class_to_obj(this)?),
278 ctx.clone(),
279 event,
280 listener,
281 prepend,
282 once,
283 )
284 }
285
286 fn once(
287 this: This<Object<'js>>,
288 ctx: Ctx<'js>,
289 event: Value<'js>,
290 listener: Function<'js>,
291 ) -> Result<Object<'js>> {
292 Self::add_event_listener(this, ctx, event, listener, false, true)
293 }
294
295 fn on(
296 this: This<Object<'js>>,
297 ctx: Ctx<'js>,
298 event: Value<'js>,
299 listener: Function<'js>,
300 ) -> Result<Object<'js>> {
301 Self::add_event_listener(this, ctx, event, listener, false, false)
302 }
303
304 fn prepend_listener(
305 this: This<Object<'js>>,
306 ctx: Ctx<'js>,
307 event: Value<'js>,
308 listener: Function<'js>,
309 ) -> Result<Object<'js>> {
310 Self::add_event_listener(this, ctx, event, listener, true, false)
311 }
312
313 fn prepend_once_listener(
314 this: This<Object<'js>>,
315 ctx: Ctx<'js>,
316 event: Value<'js>,
317 listener: Function<'js>,
318 ) -> Result<Object<'js>> {
319 Self::add_event_listener(this, ctx, event, listener, true, true)
320 }
321
322 fn evt_add_event_listener(
323 this: This<Object<'js>>,
324 ctx: Ctx<'js>,
325 event: Value<'js>,
326 listener: Function<'js>,
327 options: Opt<Object<'js>>,
328 ) -> Result<Object<'js>> {
329 let mut once = false;
330 if let Some(opt) = options.0 {
331 if let Some(once_opt) = opt.get("once")? {
332 once = once_opt;
333 }
334 }
335 Self::add_event_listener(this, ctx, event, listener, false, once)
336 }
337
338 fn add_event_listener(
339 this: This<Object<'js>>,
340 ctx: Ctx<'js>,
341 event: Value<'js>,
342 listener: Function<'js>,
343 prepend: bool,
344 once: bool,
345 ) -> Result<Object<'js>> {
346 let events = Self::resolve_events_from(&ctx, &this)?;
347 let mut events = events.write().or_throw(&ctx)?;
348 let key = EventKey::from_value(&ctx, event)?;
349 let mut is_new = false;
350
351 let items = match events.iter_mut().find(|(k, _)| k == &key) {
352 Some((_, entry_items)) => entry_items,
353 None => {
354 is_new = true;
355 events.push((key.clone(), Vec::new()));
356 &mut events.last_mut().unwrap().1
357 },
358 };
359
360 let item = EventItem {
361 callback: listener,
362 once,
363 };
364 if !prepend {
365 items.push(item);
366 } else {
367 items.insert(0, item);
368 }
369 if is_new {
370 if let Some(class) = Class::<Self>::from_object(&this.0) {
371 class.borrow_mut().on_event_changed(key, true)?;
372 }
373 }
374 Ok(this.0)
375 }
376
377 fn has_listener_str(&self, event: &str) -> bool {
378 let key = EventKey::String(event.into());
379 has_key(self.get_event_list(), key)
380 }
381
382 #[allow(dead_code)]
383 fn has_listener(&self, ctx: Ctx<'js>, event: Value<'js>) -> Result<bool> {
384 let key = EventKey::from_value(&ctx, event)?;
385 Ok(has_key(self.get_event_list(), key))
386 }
387
388 #[allow(dead_code)]
389 fn get_listeners(&self, ctx: &Ctx<'js>, event: Value<'js>) -> Result<Vec<Function<'js>>> {
390 let key = EventKey::from_value(ctx, event)?;
391 Ok(find_all_listeners(self.get_event_list(), key))
392 }
393
394 fn get_listeners_str(&self, event: &str) -> Vec<Function<'js>> {
395 let key = EventKey::String(event.into());
396 find_all_listeners(self.get_event_list(), key)
397 }
398
399 fn do_emit(
400 event: Value<'js>,
401 this: This<Object<'js>>,
402 ctx: &Ctx<'js>,
403 args: Rest<Value<'js>>,
404 defer: bool,
405 ) -> Result<bool> {
406 let events = Self::resolve_events_from(ctx, &this)?;
407 let mut events = events.write().or_throw(ctx)?;
408 let key = EventKey::from_value(ctx, event)?;
409
410 if let Some(index) = events.iter_mut().position(|(k, _)| k == &key) {
411 let items = &mut events[index].1;
412 let mut callbacks = Vec::with_capacity(items.len());
413 items.retain(|item: &EventItem<'_>| {
414 callbacks.push(item.callback.clone());
415 !item.once
416 });
417 if items.is_empty() {
418 events.remove(index);
419 if let Some(class) = Class::<Self>::from_object(&this.0) {
420 class.borrow_mut().on_event_changed(key, false)?;
421 }
422 }
423 drop(events);
424 for callback in callbacks {
425 let args: Vec<Value<'js>> = args.iter().map(|arg| arg.to_owned()).collect();
426 let args = Rest(args);
427 let this_val = This(this.0.clone().into_value());
428 if defer {
429 callback.defer((this_val, args))?;
430 } else {
431 callback.call::<_, ()>((this_val, args))?;
432 }
433 }
434 Ok(true)
435 } else {
436 Ok(false)
437 }
438 }
439
440 fn emit_str(
441 this: Class<'js, Self>,
442 ctx: &Ctx<'js>,
443 event: &str,
444 args: Vec<Value<'js>>,
445 defer: bool,
446 ) -> Result<()> {
447 let event = to_event(ctx, event)?;
448 Self::do_emit(event, This(class_to_obj(this)?), ctx, args.into(), defer)?;
449 Ok(())
450 }
451
452 fn emit(
453 this: This<Object<'js>>,
454 ctx: Ctx<'js>,
455 event: Value<'js>,
456 args: Rest<Value<'js>>,
457 ) -> Result<bool> {
458 Self::do_emit(event, this, &ctx, args, false)
459 }
460
461 fn evt_dispatch_event(
462 this: This<Object<'js>>,
463 ctx: Ctx<'js>,
464 event: Value<'js>,
465 ) -> Result<bool> {
466 let event_type = event.get_optional("type")?.unwrap();
467 Self::do_emit(event_type, this, &ctx, Rest(vec![event]), false)
468 }
469
470 fn event_names(this: This<Object<'js>>, ctx: Ctx<'js>) -> Result<Vec<Value<'js>>> {
471 let events = Self::resolve_events_from(&ctx, &this)?;
472 let events = events.read().or_throw(&ctx)?;
473
474 let mut names = Vec::with_capacity(events.len());
475 for (key, _entry) in events.iter() {
476 let value = match key {
477 EventKey::Symbol(symbol) => symbol.clone().into_value(),
478 EventKey::String(str) => JsString::from_str(ctx.clone(), str)?.into(),
479 };
480
481 names.push(value)
482 }
483
484 Ok(names)
485 }
486
487 fn listener_count(this: This<Object<'js>>, ctx: Ctx<'js>, event: Value<'js>) -> Result<usize> {
488 let events = Self::resolve_events_from(&ctx, &this)?;
489 let key = EventKey::from_value(&ctx, event)?;
490 let events = events.read().or_throw(&ctx)?;
491 Ok(events
492 .iter()
493 .find(|(k, _)| k == &key)
494 .map_or(0, |(_, items)| items.len()))
495 }
496
497 fn remove_all_listeners(
498 this: This<Object<'js>>,
499 ctx: Ctx<'js>,
500 event: Opt<Value<'js>>,
501 ) -> Result<Object<'js>> {
502 let events = Self::resolve_events_from(&ctx, &this)?;
503 let mut events = events.write().or_throw(&ctx)?;
504 match event.0 {
505 Some(event) if !event.is_undefined() => {
506 let key = EventKey::from_value(&ctx, event)?;
507 events.retain(|(k, _)| k != &key);
508 },
509 _ => events.clear(),
510 }
511 Ok(this.0)
512 }
513}
514
515fn find_all_listeners<'js>(
516 events: Arc<RwLock<EventList<'js>>>,
517 key: EventKey<'js>,
518) -> Vec<Function<'js>> {
519 let events = events.read().unwrap();
520 let items = events.iter().find(|(k, _)| k == &key);
521 if let Some((_, callbacks)) = items {
522 callbacks.iter().map(|item| item.callback.clone()).collect()
523 } else {
524 vec![]
525 }
526}
527
528fn has_key<'js>(event_list: Arc<RwLock<EventList<'js>>>, key: EventKey<'js>) -> bool {
529 event_list.read().unwrap().iter().any(|(k, _)| k == &key)
530}
531
532fn to_event<'js>(ctx: &Ctx<'js>, event: &str) -> Result<Value<'js>> {
533 let event = JsString::from_str(ctx.clone(), event)?;
534 Ok(event.into_value())
535}
536
537pub struct EventsModule;
538
539impl ModuleDef for EventsModule {
540 fn declare(declare: &Declarations) -> Result<()> {
541 declare.declare(stringify!(EventEmitter))?;
542 declare.declare("default")?;
543
544 Ok(())
545 }
546
547 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
548 let ctor = Class::<EventEmitter>::create_constructor(ctx)?
549 .expect("Can't create EventEmitter constructor");
550 ctor.set(stringify!(EventEmitter), ctor.clone())?;
551 exports.export(stringify!(EventEmitter), ctor.clone())?;
552 exports.export("default", ctor)?;
553
554 EventEmitter::add_event_emitter_prototype(ctx)?;
555
556 Ok(())
557 }
558}
559
560impl From<EventsModule> for ModuleInfo<EventsModule> {
561 fn from(val: EventsModule) -> Self {
562 ModuleInfo {
563 name: "events",
564 module: val,
565 }
566 }
567}
568
569pub fn init(ctx: &Ctx<'_>) -> Result<()> {
570 let globals = ctx.globals();
571
572 Class::<EventTarget>::define(&globals)?;
573 Class::<CustomEvent>::define(&globals)?;
574 Class::<Event>::define(&globals)?;
575
576 EventTarget::add_event_target_prototype(ctx)?;
577
578 Ok(())
579}