Skip to main content

tauri/event/
mod.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5mod listener;
6pub(crate) mod plugin;
7use std::{convert::Infallible, str::FromStr};
8
9pub(crate) use listener::Listeners;
10use serde::{Deserialize, Serialize};
11
12mod event_name;
13
14pub(crate) use event_name::EventName;
15
16use crate::ipc::CallbackFn;
17
18/// Unique id of an event.
19pub type EventId = u32;
20
21/// Event Target
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
23#[serde(tag = "kind")]
24#[non_exhaustive]
25pub enum EventTarget {
26  /// Any and all event targets.
27  Any,
28
29  /// Any [`Window`](crate::Window), [`Webview`](crate::Webview) or [`WebviewWindow`](crate::WebviewWindow) that have this label.
30  AnyLabel {
31    /// Target label.
32    label: String,
33  },
34
35  /// [`App`](crate::App) and [`AppHandle`](crate::AppHandle) targets.
36  App,
37
38  /// [`Window`](crate::Window) target.
39  Window {
40    /// window label.
41    label: String,
42  },
43
44  /// [`Webview`](crate::Webview) target.
45  Webview {
46    /// webview label.
47    label: String,
48  },
49
50  /// [`WebviewWindow`](crate::WebviewWindow) target.
51  WebviewWindow {
52    /// webview window label.
53    label: String,
54  },
55}
56
57impl EventTarget {
58  /// [`Self::Any`] target.
59  pub fn any() -> Self {
60    Self::Any
61  }
62
63  /// [`Self::App`] target.
64  pub fn app() -> Self {
65    Self::App
66  }
67
68  /// [`Self::AnyLabel`] target.
69  pub fn labeled(label: impl Into<String>) -> Self {
70    Self::AnyLabel {
71      label: label.into(),
72    }
73  }
74
75  /// [`Self::Window`] target.
76  pub fn window(label: impl Into<String>) -> Self {
77    Self::Window {
78      label: label.into(),
79    }
80  }
81
82  /// [`Self::Webview`] target.
83  pub fn webview(label: impl Into<String>) -> Self {
84    Self::Webview {
85      label: label.into(),
86    }
87  }
88
89  /// [`Self::WebviewWindow`] target.
90  pub fn webview_window(label: impl Into<String>) -> Self {
91    Self::WebviewWindow {
92      label: label.into(),
93    }
94  }
95}
96
97impl<T: AsRef<str>> From<T> for EventTarget {
98  fn from(value: T) -> Self {
99    Self::AnyLabel {
100      label: value.as_ref().to_string(),
101    }
102  }
103}
104
105impl FromStr for EventTarget {
106  type Err = Infallible;
107
108  fn from_str(s: &str) -> Result<Self, Self::Err> {
109    Ok(Self::AnyLabel {
110      label: s.to_string(),
111    })
112  }
113}
114
115/// Serialized emit arguments.
116#[derive(Clone)]
117pub struct EmitArgs {
118  /// event name.
119  event: EventName,
120  /// Serialized payload.
121  payload: String,
122}
123
124impl EmitArgs {
125  pub fn new<S: Serialize>(event: EventName<&str>, payload: &S) -> crate::Result<Self> {
126    #[cfg(feature = "tracing")]
127    let _span = tracing::debug_span!("window::emit::serialize").entered();
128    Ok(EmitArgs {
129      event: event.into_owned(),
130      payload: serde_json::to_string(payload)?,
131    })
132  }
133
134  pub fn new_str(event: EventName<&str>, payload: String) -> crate::Result<Self> {
135    #[cfg(feature = "tracing")]
136    let _span = tracing::debug_span!("window::emit::json").entered();
137    Ok(EmitArgs {
138      event: event.into_owned(),
139      payload,
140    })
141  }
142}
143
144/// An event that was emitted.
145#[derive(Debug, Clone)]
146pub struct Event {
147  id: EventId,
148  data: String,
149}
150
151impl Event {
152  fn new(id: EventId, data: String) -> Self {
153    Self { id, data }
154  }
155
156  /// The [`EventId`] of the handler that was triggered.
157  pub fn id(&self) -> EventId {
158    self.id
159  }
160
161  /// The event payload.
162  pub fn payload(&self) -> &str {
163    &self.data
164  }
165}
166
167pub(crate) fn listen_js_script(
168  listeners_object_name: &str,
169  serialized_target: &str,
170  event: EventName<&str>,
171  event_id: EventId,
172  handler: CallbackFn,
173) -> String {
174  let handler_id = handler.0;
175  format!(
176    "(function () {{
177      if (window['{listeners_object_name}'] === void 0) {{
178        Object.defineProperty(window, '{listeners_object_name}', {{ value: Object.create(null) }});
179      }}
180      if (window['{listeners_object_name}']['{event}'] === void 0) {{
181        Object.defineProperty(window['{listeners_object_name}'], '{event}', {{ value: Object.create(null) }});
182      }}
183      const eventListeners = window['{listeners_object_name}']['{event}']
184      const listener = {{
185        target: {serialized_target},
186        handlerId: {handler_id}
187      }};
188      Object.defineProperty(eventListeners, '{event_id}', {{ value: listener, configurable: true }});
189    }})()
190  ",
191  )
192}
193
194pub(crate) fn emit_js_script(
195  event_emit_function_name: &str,
196  emit_args: &EmitArgs,
197  serialized_ids: &str,
198) -> crate::Result<String> {
199  Ok(format!(
200    "(function () {{ const fn = window['{}']; fn && fn({{event: '{}', payload: {}}}, {ids}) }})()",
201    event_emit_function_name,
202    emit_args.event,
203    emit_args.payload,
204    ids = serialized_ids,
205  ))
206}
207
208pub(crate) fn unlisten_js_script(
209  listeners_object_name: &str,
210  event_arg: &str,
211  event_id_arg: &str,
212) -> String {
213  format!(
214    "(function () {{
215        const listeners = (window['{listeners_object_name}'] || {{}})[{event_arg}]
216        if (listeners) {{
217          window.__TAURI_INTERNALS__.unregisterCallback(listeners[{event_id_arg}].handlerId)
218        }}
219      }})()
220    ",
221  )
222}
223
224pub(crate) fn event_initialization_script(function_name: &str, listeners: &str) -> String {
225  format!(
226    "Object.defineProperty(window, '{function_name}', {{
227      value: function (eventData, ids) {{
228        const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
229        for (const id of ids) {{
230          const listener = listeners[id]
231          if (listener) {{
232            eventData.id = id
233            window.__TAURI_INTERNALS__.runCallback(listener.handlerId, eventData)
234          }}
235        }}
236      }}
237    }});
238  "
239  )
240}
241
242#[cfg(test)]
243mod tests {
244  use super::*;
245  #[test]
246  fn test_illegal_event_name() {
247    let s = EventName::new("some\r illegal event name")
248      .unwrap_err()
249      .to_string();
250    assert_eq!("only alphanumeric, '-', '/', ':', '_' permitted for event names: \"some\\r illegal event name\"", s);
251  }
252}