event.rs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use std::{
  5. boxed::Box,
  6. cell::Cell,
  7. collections::HashMap,
  8. fmt,
  9. hash::Hash,
  10. sync::{Arc, Mutex},
  11. };
  12. use uuid::Uuid;
  13. /// Checks if an event name is valid.
  14. pub fn is_event_name_valid(event: &str) -> bool {
  15. event
  16. .chars()
  17. .all(|c| c.is_alphanumeric() || c == '-' || c == '/' || c == ':' || c == '_')
  18. }
  19. pub fn assert_event_name_is_valid(event: &str) {
  20. assert!(
  21. is_event_name_valid(event),
  22. "Event name must include only alphanumeric characters, `-`, `/`, `:` and `_`."
  23. );
  24. }
  25. /// Represents an event handler.
  26. #[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
  27. pub struct EventHandler(Uuid);
  28. impl fmt::Display for EventHandler {
  29. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  30. self.0.fmt(f)
  31. }
  32. }
  33. /// An event that was triggered.
  34. #[derive(Debug, Clone)]
  35. pub struct Event {
  36. id: EventHandler,
  37. data: Option<String>,
  38. }
  39. impl Event {
  40. /// The [`EventHandler`] that was triggered.
  41. pub fn id(&self) -> EventHandler {
  42. self.id
  43. }
  44. /// The event payload.
  45. pub fn payload(&self) -> Option<&str> {
  46. self.data.as_deref()
  47. }
  48. }
  49. /// What to do with the pending handler when resolving it?
  50. enum Pending {
  51. Unlisten(EventHandler),
  52. Listen(EventHandler, String, Handler),
  53. Trigger(String, Option<String>, Option<String>),
  54. }
  55. /// Stored in [`Listeners`] to be called upon when the event that stored it is triggered.
  56. struct Handler {
  57. window: Option<String>,
  58. callback: Box<dyn Fn(Event) + Send>,
  59. }
  60. /// Holds event handlers and pending event handlers, along with the salts associating them.
  61. struct InnerListeners {
  62. handlers: Mutex<HashMap<String, HashMap<EventHandler, Handler>>>,
  63. pending: Mutex<Vec<Pending>>,
  64. function_name: Uuid,
  65. listeners_object_name: Uuid,
  66. }
  67. /// A self-contained event manager.
  68. pub(crate) struct Listeners {
  69. inner: Arc<InnerListeners>,
  70. }
  71. impl Default for Listeners {
  72. fn default() -> Self {
  73. Self {
  74. inner: Arc::new(InnerListeners {
  75. handlers: Mutex::default(),
  76. pending: Mutex::default(),
  77. function_name: Uuid::new_v4(),
  78. listeners_object_name: Uuid::new_v4(),
  79. }),
  80. }
  81. }
  82. }
  83. impl Clone for Listeners {
  84. fn clone(&self) -> Self {
  85. Self {
  86. inner: self.inner.clone(),
  87. }
  88. }
  89. }
  90. impl Listeners {
  91. /// Randomly generated function name to represent the JavaScript event function.
  92. pub(crate) fn function_name(&self) -> String {
  93. self.inner.function_name.to_string()
  94. }
  95. /// Randomly generated listener object name to represent the JavaScript event listener object.
  96. pub(crate) fn listeners_object_name(&self) -> String {
  97. self.inner.listeners_object_name.to_string()
  98. }
  99. /// Insert a pending event action to the queue.
  100. fn insert_pending(&self, action: Pending) {
  101. self
  102. .inner
  103. .pending
  104. .lock()
  105. .expect("poisoned pending event queue")
  106. .push(action)
  107. }
  108. /// Finish all pending event actions.
  109. fn flush_pending(&self) {
  110. let pending = {
  111. let mut lock = self
  112. .inner
  113. .pending
  114. .lock()
  115. .expect("poisoned pending event queue");
  116. std::mem::take(&mut *lock)
  117. };
  118. for action in pending {
  119. match action {
  120. Pending::Unlisten(id) => self.unlisten(id),
  121. Pending::Listen(id, event, handler) => self.listen_(id, event, handler),
  122. Pending::Trigger(ref event, window, payload) => self.trigger(event, window, payload),
  123. }
  124. }
  125. }
  126. fn listen_(&self, id: EventHandler, event: String, handler: Handler) {
  127. match self.inner.handlers.try_lock() {
  128. Err(_) => self.insert_pending(Pending::Listen(id, event, handler)),
  129. Ok(mut lock) => {
  130. lock.entry(event).or_default().insert(id, handler);
  131. }
  132. }
  133. }
  134. /// Adds an event listener for JS events.
  135. pub(crate) fn listen<F: Fn(Event) + Send + 'static>(
  136. &self,
  137. event: String,
  138. window: Option<String>,
  139. handler: F,
  140. ) -> EventHandler {
  141. let id = EventHandler(Uuid::new_v4());
  142. let handler = Handler {
  143. window,
  144. callback: Box::new(handler),
  145. };
  146. self.listen_(id, event, handler);
  147. id
  148. }
  149. /// Listen to a JS event and immediately unlisten.
  150. pub(crate) fn once<F: FnOnce(Event) + Send + 'static>(
  151. &self,
  152. event: String,
  153. window: Option<String>,
  154. handler: F,
  155. ) -> EventHandler {
  156. let self_ = self.clone();
  157. let handler = Cell::new(Some(handler));
  158. self.listen(event, window, move |event| {
  159. self_.unlisten(event.id);
  160. let handler = handler
  161. .take()
  162. .expect("attempted to call handler more than once");
  163. handler(event)
  164. })
  165. }
  166. /// Removes an event listener.
  167. pub(crate) fn unlisten(&self, handler_id: EventHandler) {
  168. match self.inner.handlers.try_lock() {
  169. Err(_) => self.insert_pending(Pending::Unlisten(handler_id)),
  170. Ok(mut lock) => lock.values_mut().for_each(|handler| {
  171. handler.remove(&handler_id);
  172. }),
  173. }
  174. }
  175. /// Triggers the given global event with its payload.
  176. pub(crate) fn trigger(&self, event: &str, window: Option<String>, payload: Option<String>) {
  177. let mut maybe_pending = false;
  178. match self.inner.handlers.try_lock() {
  179. Err(_) => self.insert_pending(Pending::Trigger(event.to_owned(), window, payload)),
  180. Ok(lock) => {
  181. if let Some(handlers) = lock.get(event) {
  182. for (&id, handler) in handlers {
  183. if handler.window.is_none() || window == handler.window {
  184. maybe_pending = true;
  185. (handler.callback)(self::Event {
  186. id,
  187. data: payload.clone(),
  188. })
  189. }
  190. }
  191. }
  192. }
  193. }
  194. if maybe_pending {
  195. self.flush_pending();
  196. }
  197. }
  198. }
  199. #[cfg(test)]
  200. mod test {
  201. use super::*;
  202. use proptest::prelude::*;
  203. // dummy event handler function
  204. fn event_fn(s: Event) {
  205. println!("{:?}", s);
  206. }
  207. proptest! {
  208. #![proptest_config(ProptestConfig::with_cases(10000))]
  209. // check to see if listen() is properly passing keys into the LISTENERS map
  210. #[test]
  211. fn listeners_check_key(e in "[a-z]+") {
  212. let listeners: Listeners = Default::default();
  213. // clone e as the key
  214. let key = e.clone();
  215. // pass e and an dummy func into listen
  216. listeners.listen(e, None, event_fn);
  217. // lock mutex
  218. let l = listeners.inner.handlers.lock().unwrap();
  219. // check if the generated key is in the map
  220. assert!(l.contains_key(&key));
  221. }
  222. // check to see if listen inputs a handler function properly into the LISTENERS map.
  223. #[test]
  224. fn listeners_check_fn(e in "[a-z]+") {
  225. let listeners: Listeners = Default::default();
  226. // clone e as the key
  227. let key = e.clone();
  228. // pass e and an dummy func into listen
  229. listeners.listen(e, None, event_fn);
  230. // lock mutex
  231. let mut l = listeners.inner.handlers.lock().unwrap();
  232. // check if l contains key
  233. if l.contains_key(&key) {
  234. // grab key if it exists
  235. let handler = l.get_mut(&key);
  236. // check to see if we get back a handler or not
  237. match handler {
  238. // pass on Some(handler)
  239. Some(_) => {},
  240. // Fail on None
  241. None => panic!("handler is None")
  242. }
  243. }
  244. }
  245. // check to see if on_event properly grabs the stored function from listen.
  246. #[test]
  247. fn check_on_event(e in "[a-z]+", d in "[a-z]+") {
  248. let listeners: Listeners = Default::default();
  249. // clone e as the key
  250. let key = e.clone();
  251. // call listen with e and the event_fn dummy func
  252. listeners.listen(e.clone(), None, event_fn);
  253. // call on event with e and d.
  254. listeners.trigger(&e, None, Some(d));
  255. // lock the mutex
  256. let l = listeners.inner.handlers.lock().unwrap();
  257. // assert that the key is contained in the listeners map
  258. assert!(l.contains_key(&key));
  259. }
  260. }
  261. }
  262. pub fn unlisten_js(listeners_object_name: String, event_name: String, event_id: u64) -> String {
  263. format!(
  264. "
  265. (function () {{
  266. const listeners = (window['{listeners}'] || {{}})['{event_name}']
  267. if (listeners) {{
  268. const index = window['{listeners}']['{event_name}'].findIndex(e => e.id === {event_id})
  269. if (index > -1) {{
  270. window['{listeners}']['{event_name}'].splice(index, 1)
  271. }}
  272. }}
  273. }})()
  274. ",
  275. listeners = listeners_object_name,
  276. event_name = event_name,
  277. event_id = event_id,
  278. )
  279. }
  280. pub fn listen_js(
  281. listeners_object_name: String,
  282. event: String,
  283. event_id: u64,
  284. window_label: Option<String>,
  285. handler: String,
  286. ) -> String {
  287. format!(
  288. "if (window['{listeners}'] === void 0) {{
  289. Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }});
  290. }}
  291. if (window['{listeners}'][{event}] === void 0) {{
  292. Object.defineProperty(window['{listeners}'], {event}, {{ value: [] }});
  293. }}
  294. window['{listeners}'][{event}].push({{
  295. id: {event_id},
  296. windowLabel: {window_label},
  297. handler: {handler}
  298. }});
  299. ",
  300. listeners = listeners_object_name,
  301. event = event,
  302. event_id = event_id,
  303. window_label = if let Some(l) = window_label {
  304. crate::runtime::window::assert_label_is_valid(&l);
  305. format!("'{}'", l)
  306. } else {
  307. "null".to_owned()
  308. },
  309. handler = handler
  310. )
  311. }